(gtk) #772, #753: added `watch dir' for automatically adding torrents. delete files to trashcan, rather than unlinking them. These features require 2.15.5 or higher.
This commit is contained in:
parent
e85a328b1b
commit
e4562bcd7b
13
configure.ac
13
configure.ac
|
@ -10,10 +10,12 @@ AC_CONFIG_SRCDIR(libtransmission/transmission.h)
|
|||
AM_INIT_AUTOMAKE([1.9 tar-ustar])
|
||||
AC_PROG_LIBTOOL
|
||||
|
||||
GIO_MINIMUM=2.15.5
|
||||
GLIB_MINIMUM=2.6.0
|
||||
GTK_MINIMUM=2.6.0
|
||||
WX_MINIMUM=2.6.0
|
||||
LIBNOTIFY_MINIMUM=0.4.4
|
||||
AC_SUBST(GIO_MINIMUM)
|
||||
AC_SUBST(GLIB_MINIMUM)
|
||||
AC_SUBST(GTK_MINIMUM)
|
||||
AC_SUBST(WX_MINIMUM)
|
||||
|
@ -94,6 +96,16 @@ AM_CONDITIONAL([BUILD_GTK],[test "x$build_gtk" = "xyes"])
|
|||
AC_SUBST(GTK_LIBS)
|
||||
AC_SUBST(GTK_CFLAGS)
|
||||
|
||||
PKG_CHECK_MODULES([GIO],
|
||||
[gio-2.0 >= $GIO_MINIMUM],
|
||||
[use_gio=yes],
|
||||
[use_gio=no])
|
||||
AC_SUBST(GIO_LIBS)
|
||||
AC_SUBST(GIO_CFLAGS)
|
||||
if test "x$use_gio" = "xyes"; then
|
||||
AC_DEFINE([HAVE_GIO], 1)
|
||||
fi
|
||||
|
||||
PKG_CHECK_MODULES([LIBNOTIFY],
|
||||
[libnotify >= $LIBNOTIFY_MINIMUM],
|
||||
[use_libnotify=yes],
|
||||
|
@ -277,6 +289,7 @@ Configuration:
|
|||
Build BeOS client: ${build_beos}
|
||||
Build GTK+ client: ${build_gtk}
|
||||
... libnotify support: ${use_libnotify}
|
||||
... gio support: ${use_gio}
|
||||
Build OS X client: ${build_darwin}
|
||||
Build wxWidgets client: ${build_wx}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ AM_CFLAGS = \
|
|||
$(GTK_CFLAGS) \
|
||||
$(OPENSSL_CFLAGS) \
|
||||
$(PTHREAD_CFLAGS) \
|
||||
$(GIO_CFLAGS) \
|
||||
$(LIBNOTIFY_CFLAGS)
|
||||
|
||||
noinst_HEADERS = \
|
||||
|
@ -71,6 +72,7 @@ transmission_LDADD = \
|
|||
$(top_builddir)/third-party/miniupnp/libminiupnp.a \
|
||||
$(top_builddir)/third-party/libnatpmp/libnatpmp.a \
|
||||
$(GTK_LIBS) \
|
||||
$(GIO_LIBS) \
|
||||
$(LIBNOTIFY_LIBS) \
|
||||
$(OPENSSL_LIBS) \
|
||||
$(PTHREAD_LIBS) -lm
|
||||
|
|
|
@ -153,8 +153,10 @@ promptfordir( GtkWindow * parent, TrCore * core, GList * files, tr_ctor * ctor )
|
|||
v = gtk_vbox_new( FALSE, GUI_PAD );
|
||||
|
||||
flag = 0;
|
||||
w = gtk_check_button_new_with_mnemonic( _( "_Delete original torrent file" ) );
|
||||
w = gtk_check_button_new_with_mnemonic( _( "_Trash original torrent files" ) );
|
||||
g_signal_connect( w, "toggled", G_CALLBACK( deleteToggled ), ctor );
|
||||
if( tr_ctorGetDeleteSource( ctor, &flag ) )
|
||||
g_assert_not_reached( );
|
||||
gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( w ), flag );
|
||||
gtk_box_pack_start( GTK_BOX( v ), w, FALSE, FALSE, 0 );
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
#include <gtk/gtk.h>
|
||||
#include <glib/gi18n.h>
|
||||
#include <glib/gstdio.h> /* g_unlink */
|
||||
|
||||
#include <libevent/evutil.h>
|
||||
|
||||
|
@ -359,8 +360,8 @@ ipc_sendquit_blocking( void )
|
|||
|
||||
static void
|
||||
rmsock(void) {
|
||||
if(NULL != gl_sockpath) {
|
||||
unlink(gl_sockpath);
|
||||
if( gl_sockpath) {
|
||||
g_unlink(gl_sockpath);
|
||||
g_free(gl_sockpath);
|
||||
}
|
||||
}
|
||||
|
@ -463,10 +464,10 @@ serv_bind(struct constate *con) {
|
|||
strncpy(sa.sun_path, gl_sockpath, sizeof(sa.sun_path) - 1);
|
||||
|
||||
/* unlink any existing socket file before trying to create ours */
|
||||
unlink(gl_sockpath);
|
||||
g_unlink(gl_sockpath);
|
||||
if(0 > bind(con->fd, (struct sockaddr *)&sa, SUN_LEN(&sa))) {
|
||||
/* bind may fail if there was already a socket, so try twice */
|
||||
unlink(gl_sockpath);
|
||||
g_unlink(gl_sockpath);
|
||||
if(0 > bind(con->fd, (struct sockaddr *)&sa, SUN_LEN(&sa)))
|
||||
goto fail;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
*/
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include "file-list.h"
|
||||
#include "hig.h"
|
||||
|
@ -22,7 +21,7 @@ struct OpenData
|
|||
TrCore * core;
|
||||
GtkWidget * list;
|
||||
GtkToggleButton * run_check;
|
||||
GtkToggleButton * delete_check;
|
||||
GtkToggleButton * trash_check;
|
||||
char * filename;
|
||||
char * destination;
|
||||
TrTorrent * gtor;
|
||||
|
@ -55,8 +54,8 @@ openResponseCB( GtkDialog * dialog, gint response, gpointer gdata )
|
|||
if( gtk_toggle_button_get_active( data->run_check ) )
|
||||
tr_torrentStart( tr_torrent_handle( data->gtor ) );
|
||||
tr_core_add_torrent( data->core, data->gtor );
|
||||
if( gtk_toggle_button_get_active( data->delete_check ) )
|
||||
g_unlink( data->filename );
|
||||
if( gtk_toggle_button_get_active( data->trash_check ) )
|
||||
tr_file_trash_or_unlink( data->filename );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -221,8 +220,10 @@ makeaddwind( GtkWindow * parent,
|
|||
|
||||
++row;
|
||||
col = 0;
|
||||
w = gtk_check_button_new_with_mnemonic( _( "_Delete original torrent file" ) );
|
||||
data->delete_check = GTK_TOGGLE_BUTTON( w );
|
||||
w = gtk_check_button_new_with_mnemonic( _( "_Trash original torrent file" ) );
|
||||
data->trash_check = GTK_TOGGLE_BUTTON( w );
|
||||
if( tr_ctorGetDeleteSource( ctor, &flag ) )
|
||||
g_assert_not_reached( );
|
||||
gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( w ), flag );
|
||||
gtk_table_attach( GTK_TABLE( t ), w, col, col+2, row, row+1, GTK_FILL, 0, 0, 0 );
|
||||
|
||||
|
|
|
@ -26,6 +26,9 @@
|
|||
|
||||
#include <gtk/gtk.h>
|
||||
#include <glib/gi18n.h>
|
||||
#ifdef HAVE_GIO
|
||||
#include <gio/gio.h>
|
||||
#endif
|
||||
|
||||
#include <libtransmission/transmission.h>
|
||||
#include <libtransmission/utils.h> /* tr_free */
|
||||
|
@ -38,6 +41,9 @@
|
|||
|
||||
struct TrCorePrivate
|
||||
{
|
||||
#ifdef HAVE_GIO
|
||||
GFileMonitor * monitor;
|
||||
#endif
|
||||
GtkTreeModel * model;
|
||||
tr_handle * handle;
|
||||
int nextid;
|
||||
|
@ -277,6 +283,74 @@ setSort( TrCore * core, const char * mode, gboolean isReversed )
|
|||
gtk_tree_sortable_set_sort_column_id( sortable, col, type );
|
||||
}
|
||||
|
||||
#ifdef HAVE_GIO
|
||||
static void
|
||||
watchFolderChanged( GFileMonitor * monitor UNUSED,
|
||||
GFile * file,
|
||||
GFile * other_type UNUSED,
|
||||
GFileMonitorEvent event_type,
|
||||
gpointer gcore )
|
||||
{
|
||||
if( event_type == G_FILE_MONITOR_EVENT_CREATED )
|
||||
{
|
||||
TrCore * core = TR_CORE( gcore );
|
||||
char * filename = g_file_get_path( file );
|
||||
const gboolean isTorrent = g_str_has_suffix( filename, ".torrent" );
|
||||
if( isTorrent )
|
||||
{
|
||||
tr_ctor * ctor = tr_ctorNew( core->priv->handle );
|
||||
tr_core_add_list( core, g_list_append( NULL, g_strdup( filename ) ), ctor );
|
||||
}
|
||||
g_free( filename );
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
scanWatchDir( TrCore * core )
|
||||
{
|
||||
const gboolean isEnabled = pref_flag_get( PREF_KEY_DIR_WATCH_ENABLED );
|
||||
if( isEnabled )
|
||||
{
|
||||
GList * torrents = NULL;
|
||||
char * dirname = pref_string_get( PREF_KEY_DIR_WATCH );
|
||||
GDir * dir = g_dir_open( dirname, 0, NULL );
|
||||
const char * basename;
|
||||
while(( basename = g_dir_read_name( dir )))
|
||||
if( g_str_has_suffix( basename, ".torrent" ) )
|
||||
torrents = g_list_append( torrents, g_build_filename( dirname, basename, NULL ) );
|
||||
if( torrents )
|
||||
tr_core_add_list( core, torrents, tr_ctorNew( core->priv->handle ) );
|
||||
g_free( dirname );
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
updateWatchDir( TrCore * core )
|
||||
{
|
||||
char * filename = pref_string_get( PREF_KEY_DIR_WATCH );
|
||||
const gboolean isEnabled = pref_flag_get( PREF_KEY_DIR_WATCH_ENABLED );
|
||||
|
||||
if( core->priv->monitor && !isEnabled )
|
||||
{
|
||||
GFileMonitor * m = core->priv->monitor;
|
||||
core->priv->monitor = NULL;
|
||||
g_signal_handlers_disconnect_by_func( m, watchFolderChanged, core );
|
||||
g_file_monitor_cancel( m );
|
||||
g_object_unref( G_OBJECT( m ) );
|
||||
}
|
||||
else if( isEnabled && !core->priv->monitor )
|
||||
{
|
||||
GFile * file = g_file_new_for_path( filename );
|
||||
GFileMonitor * m = g_file_monitor_directory( file, 0, NULL, NULL );
|
||||
scanWatchDir( core );
|
||||
g_signal_connect( m, "changed", G_CALLBACK (watchFolderChanged), core );
|
||||
core->priv->monitor = m;
|
||||
}
|
||||
|
||||
g_free( filename );
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
prefsChanged( TrCore * core, const char * key, gpointer data UNUSED )
|
||||
{
|
||||
|
@ -293,6 +367,13 @@ prefsChanged( TrCore * core, const char * key, gpointer data UNUSED )
|
|||
const uint16_t val = pref_int_get( key );
|
||||
tr_setGlobalPeerLimit( tr_core_handle( core ), val );
|
||||
}
|
||||
#ifdef HAVE_GIO
|
||||
else if( !strcmp( key, PREF_KEY_DIR_WATCH ) ||
|
||||
!strcmp( key, PREF_KEY_DIR_WATCH_ENABLED ) )
|
||||
{
|
||||
updateWatchDir( core );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -382,6 +463,7 @@ tr_core_new( void )
|
|||
/* init from prefs & listen to pref changes */
|
||||
prefsChanged( core, PREF_KEY_SORT_MODE, NULL );
|
||||
prefsChanged( core, PREF_KEY_SORT_REVERSED, NULL );
|
||||
prefsChanged( core, PREF_KEY_DIR_WATCH_ENABLED, NULL );
|
||||
prefsChanged( core, PREF_KEY_MAX_PEERS_GLOBAL, NULL );
|
||||
g_signal_connect( core, "prefs-changed", G_CALLBACK(prefsChanged), NULL );
|
||||
|
||||
|
@ -499,6 +581,9 @@ tr_core_apply_defaults( tr_ctor * ctor )
|
|||
if( tr_ctorGetPaused( ctor, TR_FORCE, NULL ) )
|
||||
tr_ctorSetPaused( ctor, TR_FORCE, !pref_flag_get( PREF_KEY_START ) );
|
||||
|
||||
if( tr_ctorGetDeleteSource( ctor, NULL ) )
|
||||
tr_ctorSetDeleteSource( ctor, pref_flag_get( PREF_KEY_TRASH_ORIGINAL ) );
|
||||
|
||||
if( tr_ctorGetMaxConnectedPeers( ctor, TR_FORCE, NULL ) )
|
||||
tr_ctorSetMaxConnectedPeers( ctor, TR_FORCE,
|
||||
pref_int_get( PREF_KEY_MAX_PEERS_PER_TORRENT ) );
|
||||
|
|
|
@ -31,6 +31,14 @@ tr_prefs_init_global( void )
|
|||
|
||||
cf_check_older_configs( );
|
||||
|
||||
#if HAVE_GIO
|
||||
str = NULL;
|
||||
if( !str ) str = g_get_user_special_dir( G_USER_DIRECTORY_DESKTOP );
|
||||
if( !str ) str = g_get_home_dir( );
|
||||
pref_string_set_default ( PREF_KEY_DIR_WATCH, str );
|
||||
pref_flag_set_default ( PREF_KEY_DIR_WATCH_ENABLED, FALSE );
|
||||
#endif
|
||||
|
||||
pref_int_set_default ( PREF_KEY_MAX_PEERS_GLOBAL, 200 );
|
||||
pref_int_set_default ( PREF_KEY_MAX_PEERS_PER_TORRENT, 50 );
|
||||
|
||||
|
@ -47,11 +55,9 @@ tr_prefs_init_global( void )
|
|||
|
||||
str = NULL;
|
||||
#if GLIB_CHECK_VERSION(2,14,0)
|
||||
if( !str )
|
||||
str = g_get_user_special_dir( G_USER_DIRECTORY_DOWNLOAD );
|
||||
if( !str ) str = g_get_user_special_dir( G_USER_DIRECTORY_DOWNLOAD );
|
||||
#endif
|
||||
if( !str )
|
||||
str = g_get_home_dir( );
|
||||
if( !str ) str = g_get_home_dir( );
|
||||
pref_string_set_default ( PREF_KEY_DIR_DEFAULT, str );
|
||||
|
||||
pref_int_set_default ( PREF_KEY_PORT, TR_DEFAULT_PORT );
|
||||
|
@ -70,6 +76,7 @@ tr_prefs_init_global( void )
|
|||
pref_flag_set_default ( PREF_KEY_MINIMAL_VIEW, FALSE );
|
||||
|
||||
pref_flag_set_default ( PREF_KEY_START, TRUE );
|
||||
pref_flag_set_default ( PREF_KEY_TRASH_ORIGINAL, FALSE );
|
||||
|
||||
pref_save( NULL );
|
||||
}
|
||||
|
@ -211,26 +218,40 @@ torrentPage( GObject * core )
|
|||
const char * s;
|
||||
GtkWidget * t;
|
||||
GtkWidget * w;
|
||||
GtkWidget * l;
|
||||
|
||||
t = hig_workarea_create( );
|
||||
hig_workarea_add_section_title( t, &row, _( "Adding" ) );
|
||||
|
||||
w = new_path_chooser_button( PREF_KEY_DIR_DEFAULT, core );
|
||||
hig_workarea_add_row( t, &row, _( "Default destination _folder:" ), w, NULL );
|
||||
#ifdef HAVE_GIO
|
||||
s = _( "Automatically add torrents from:" );
|
||||
l = new_check_button( s, PREF_KEY_DIR_WATCH_ENABLED, core );
|
||||
w = new_path_chooser_button( PREF_KEY_DIR_WATCH, core );
|
||||
gtk_widget_set_sensitive( GTK_WIDGET(w), pref_flag_get( PREF_KEY_DIR_WATCH_ENABLED ) );
|
||||
g_signal_connect( l, "toggled", G_CALLBACK(target_cb), w );
|
||||
hig_workarea_add_row_w( t, &row, l, w, NULL );
|
||||
#endif
|
||||
|
||||
s = _( "Show _options dialog" );
|
||||
w = new_check_button( s, PREF_KEY_OPTIONS_PROMPT, core );
|
||||
hig_workarea_add_wide_control( t, &row, w );
|
||||
|
||||
s = _( "_Start when added" );
|
||||
s = _( "_Start torrents when added" );
|
||||
w = new_check_button( s, PREF_KEY_START, core );
|
||||
hig_workarea_add_wide_control( t, &row, w );
|
||||
|
||||
s = _( "_Trash original torrent files" );
|
||||
w = new_check_button( s, PREF_KEY_TRASH_ORIGINAL, core );
|
||||
hig_workarea_add_wide_control( t, &row, w );
|
||||
|
||||
w = new_path_chooser_button( PREF_KEY_DIR_DEFAULT, core );
|
||||
hig_workarea_add_row( t, &row, _( "Default destination _folder:" ), w, NULL );
|
||||
|
||||
#ifdef HAVE_LIBNOTIFY
|
||||
hig_workarea_add_section_divider( t, &row );
|
||||
hig_workarea_add_section_title( t, &row, _( "Notification" ) );
|
||||
|
||||
s = _( "_Popup message when a torrent finishes" );
|
||||
s = _( "_Display a message when torrents finish" );
|
||||
w = new_check_button( s, PREF_KEY_NOTIFY, core );
|
||||
hig_workarea_add_wide_control( t, &row, w );
|
||||
#endif
|
||||
|
|
|
@ -26,7 +26,10 @@ GtkWidget * tr_prefs_dialog_new( GObject * core, GtkWindow * parent );
|
|||
#define PREF_KEY_UL_LIMIT "upload-limit"
|
||||
#define PREF_KEY_OPTIONS_PROMPT "show-options-window"
|
||||
#define PREF_KEY_DIR_DEFAULT "default-download-directory"
|
||||
#define PREF_KEY_DIR_WATCH "watch-folder"
|
||||
#define PREF_KEY_DIR_WATCH_ENABLED "watch-folder-enabled"
|
||||
#define PREF_KEY_START "start-added-torrents"
|
||||
#define PREF_KEY_TRASH_ORIGINAL "trash-original-torrent-files"
|
||||
#define PREF_KEY_PORT "listening-port"
|
||||
#define PREF_KEY_NAT "nat-traversal-enabled"
|
||||
#define PREF_KEY_PEX "pex-enabled"
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
|
||||
#include <gtk/gtk.h>
|
||||
#include <glib/gi18n.h>
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include <libtransmission/transmission.h>
|
||||
|
||||
|
@ -204,8 +203,17 @@ tr_torrent_new_ctor( tr_handle * handle,
|
|||
errcode = -1;
|
||||
*err = NULL;
|
||||
|
||||
tr_ctorSetDeleteSource( ctor, FALSE );
|
||||
tor = tr_torrentNew( handle, ctor, &errcode );
|
||||
|
||||
if( tor )
|
||||
{
|
||||
uint8_t doTrash = FALSE;
|
||||
tr_ctorGetDeleteSource( ctor, &doTrash );
|
||||
if( doTrash )
|
||||
tr_file_trash_or_unlink( tr_ctorGetSourceFile( ctor ) );
|
||||
}
|
||||
|
||||
if( !tor )
|
||||
{
|
||||
const char * filename = tr_ctorGetSourceFile( ctor );
|
||||
|
@ -334,7 +342,7 @@ tr_torrent_delete_files( TrTorrent * gtor )
|
|||
while( strcmp( stop, file ) && strlen(stop) < strlen(file) )
|
||||
{
|
||||
char * swap = g_path_get_dirname( file );
|
||||
g_unlink( file );
|
||||
tr_file_trash_or_unlink( file );
|
||||
g_free( file );
|
||||
file = swap;
|
||||
}
|
||||
|
|
21
gtk/util.c
21
gtk/util.c
|
@ -28,6 +28,10 @@
|
|||
|
||||
#include <gtk/gtk.h>
|
||||
#include <glib/gi18n.h>
|
||||
#include <glib/gstdio.h> /* g_unlink() */
|
||||
#ifdef HAVE_GIO
|
||||
#include <gio/gio.h> /* g_file_trash() */
|
||||
#endif
|
||||
|
||||
#include <libevent/evhttp.h>
|
||||
|
||||
|
@ -416,3 +420,20 @@ tr_object_ref_sink( gpointer object )
|
|||
#endif
|
||||
return object;
|
||||
}
|
||||
|
||||
void
|
||||
tr_file_trash_or_unlink( const char * filename )
|
||||
{
|
||||
if( filename && *filename )
|
||||
{
|
||||
gboolean trashed = FALSE;
|
||||
#ifdef HAVE_GIO
|
||||
GError * err = NULL;
|
||||
GFile * file = g_file_new_for_path( filename );
|
||||
trashed = g_file_trash( file, NULL, &err );
|
||||
g_object_unref( G_OBJECT( file ) );
|
||||
#endif
|
||||
if( !trashed )
|
||||
g_unlink( filename );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -110,6 +110,8 @@ on_tree_view_button_pressed (GtkWidget * view,
|
|||
|
||||
gpointer tr_object_ref_sink (gpointer object);
|
||||
|
||||
void tr_file_trash_or_unlink( const char * filename );
|
||||
|
||||
#endif /* GTK_MAJOR_VERSION */
|
||||
|
||||
#endif /* TG_UTIL_H */
|
||||
|
|
Loading…
Reference in New Issue