(trunk) #920: add "move data" to libT so all clients can use it
This commit is contained in:
parent
ccb14d491a
commit
29b1d3e2cc
2
NEWS
2
NEWS
|
@ -2,6 +2,8 @@ NEWS file for Transmission <http://www.transmissionbt.com/>
|
|||
|
||||
1.70 (2009/mm/dd)
|
||||
<http://trac.transmissionbt.com/query?milestone=1.70&group=component&groupdesc=1&order=severity>
|
||||
- All Platforms
|
||||
+ Add ability to move a torrent's data and/or tell Transmission where to look for it
|
||||
- Mac
|
||||
+ Hold down the option key on launch to pause all transfers
|
||||
|
||||
|
|
|
@ -75,6 +75,8 @@ static tr_option opts[] =
|
|||
{ 'i', "info", "Show the current torrent(s)' details", "i", 0, NULL },
|
||||
{ 920, "session-info", "Show the session's details", "si", 0, NULL },
|
||||
{ 'l', "list", "List all torrents", "l", 0, NULL },
|
||||
{ 960, "move", "Move current torrent's data to a new folder", NULL, 1, "<path>" },
|
||||
{ 961, "find", "Tell Transmission where to find a torrent's data", NULL, 1, "<path>" },
|
||||
{ 'm', "portmap", "Enable portmapping via NAT-PMP or UPnP", "m", 0, NULL },
|
||||
{ 'M', "no-portmap", "Disable portmapping", "M", 0, NULL },
|
||||
{ 'n', "auth", "Set authentication info", "n", 1, "<username:password>" },
|
||||
|
@ -588,6 +590,20 @@ readargs( int argc,
|
|||
tr_bencDictAddBool( args, "seedRatioLimited", FALSE );
|
||||
break;
|
||||
|
||||
case 960:
|
||||
tr_bencDictAddStr( &top, "method", "torrent-set-location" );
|
||||
tr_bencDictAddStr( args, "location", optarg );
|
||||
tr_bencDictAddBool( args, "move", TRUE );
|
||||
addIdArg( args, id );
|
||||
break;
|
||||
|
||||
case 961:
|
||||
tr_bencDictAddStr( &top, "method", "torrent-set-location" );
|
||||
tr_bencDictAddStr( args, "location", optarg );
|
||||
tr_bencDictAddBool( args, "move", FALSE );
|
||||
addIdArg( args, id );
|
||||
break;
|
||||
|
||||
case TR_OPT_ERR:
|
||||
fprintf( stderr, "invalid option\n" );
|
||||
showUsage( );
|
||||
|
|
|
@ -167,6 +167,12 @@ Remove the current torrent(s). This does not delete the downloaded data.
|
|||
.It Fl -remove-and-delete
|
||||
Remove the current torrent(s) and delete their downloaded data.
|
||||
|
||||
.It Fl -move
|
||||
Move the current torrents' data from their current locations to the specified directory.
|
||||
|
||||
.It Fl -find
|
||||
Tell Transmission where to look for the current torrents' data.
|
||||
|
||||
.It Fl sr Fl -seedratio Ar ratio
|
||||
Let the current torrent(s) seed until a specific
|
||||
.Ar ratio
|
||||
|
|
|
@ -95,6 +95,7 @@
|
|||
"files-unwanted" | array indices of file(s) to not download
|
||||
"honorsSessionLimits" | boolean true if session upload limits are honored
|
||||
"ids" | array torrent list, as described in 3.1
|
||||
"location" | string new location of the torrent's content
|
||||
"peer-limit" | number maximum number of peers
|
||||
"priority-high" | array indices of high-priority file(s)
|
||||
"priority-low" | array indices of low-priority file(s)
|
||||
|
@ -344,6 +345,21 @@
|
|||
Response arguments: none
|
||||
|
||||
|
||||
3.6. Moving a Torrent
|
||||
|
||||
Method name: "torrent-set-location"
|
||||
|
||||
Request arguments:
|
||||
|
||||
string | value type & description
|
||||
---------------------------+-------------------------------------------------
|
||||
"ids" | array torrent list, as described in 3.1
|
||||
"location" | string destination folder
|
||||
"move" | boolean if true, move from previous location
|
||||
|
||||
Response arguments: none
|
||||
|
||||
|
||||
4. Session Requests
|
||||
|
||||
4.1. Session Arguments
|
||||
|
@ -513,5 +529,7 @@
|
|||
| | NO | torrent-get | removed arg "downloadLimitMode"
|
||||
| | NO | torrent-get | removed arg "uploadLimitMode"
|
||||
------+---------+-----------+----------------+-------------------------------
|
||||
6 | 1.70 | yes | torrent-set | new arg "location"
|
||||
------+---------+-----------+----------------+-------------------------------
|
||||
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ noinst_HEADERS = \
|
|||
msgwin.h \
|
||||
notify.h \
|
||||
options-icon.h \
|
||||
relocate.h \
|
||||
stats.h \
|
||||
sexy-icon-entry.h \
|
||||
sexy-marshal.h \
|
||||
|
@ -81,6 +82,7 @@ transmission_SOURCES = \
|
|||
makemeta-ui.c \
|
||||
msgwin.c \
|
||||
notify.c \
|
||||
relocate.c \
|
||||
sexy-icon-entry.c \
|
||||
sexy-marshal.c \
|
||||
stats.c \
|
||||
|
|
|
@ -101,7 +101,7 @@ static GtkToggleActionEntry pref_toggle_entries[] =
|
|||
N_( "_Toolbar" ), NULL, NULL, G_CALLBACK( toggle_pref_cb ), FALSE }
|
||||
};
|
||||
|
||||
static GtkActionEntry entries[] =
|
||||
static GtkActionEntry entries[] =
|
||||
{
|
||||
{ "torrent-menu", NULL, N_( "_Torrent" ), NULL, NULL, NULL },
|
||||
{ "view-menu", NULL, N_( "_View" ), NULL, NULL, NULL },
|
||||
|
@ -116,6 +116,7 @@ static GtkActionEntry entries[] =
|
|||
{ "pause-torrent", GTK_STOCK_MEDIA_PAUSE, N_( "_Pause" ), "<control>P", N_( "Pause torrent" ), G_CALLBACK( action_cb ) },
|
||||
{ "pause-all-torrents", GTK_STOCK_MEDIA_PAUSE, N_( "_Pause All" ), NULL, N_( "Pause all torrents" ), G_CALLBACK( action_cb ) },
|
||||
{ "start-all-torrents", GTK_STOCK_MEDIA_PLAY, N_( "_Start All" ), NULL, N_( "Start all torrents" ), G_CALLBACK( action_cb ) },
|
||||
{ "relocate-torrent", NULL, N_("Set _Location" ), NULL, NULL, G_CALLBACK( action_cb ) },
|
||||
{ "remove-torrent", GTK_STOCK_REMOVE, NULL, "Delete", N_( "Remove torrent" ), G_CALLBACK( action_cb ) },
|
||||
{ "delete-torrent", GTK_STOCK_DELETE, N_( "_Delete Files and Remove" ), "<shift>Delete", NULL, G_CALLBACK( action_cb ) },
|
||||
{ "new-torrent", GTK_STOCK_NEW, N_( "_New..." ), NULL, N_( "Create a torrent" ), G_CALLBACK( action_cb ) },
|
||||
|
|
14
gtk/icons.c
14
gtk/icons.c
|
@ -262,13 +262,13 @@ get_mime_type_icon( const char * mime_type,
|
|||
int n;
|
||||
|
||||
switch ( icon_size ) {
|
||||
case GTK_ICON_SIZE_INVALID: n = 0; break;
|
||||
case GTK_ICON_SIZE_MENU: n = 1; break;
|
||||
case GTK_ICON_SIZE_SMALL_TOOLBAR: n = 2; break;
|
||||
case GTK_ICON_SIZE_LARGE_TOOLBAR: n = 3; break;
|
||||
case GTK_ICON_SIZE_BUTTON: n = 4; break;
|
||||
case GTK_ICON_SIZE_DND: n = 5; break;
|
||||
case GTK_ICON_SIZE_DIALOG: n = 6; break;
|
||||
case GTK_ICON_SIZE_MENU: n = 1; break;
|
||||
case GTK_ICON_SIZE_SMALL_TOOLBAR: n = 2; break;
|
||||
case GTK_ICON_SIZE_LARGE_TOOLBAR: n = 3; break;
|
||||
case GTK_ICON_SIZE_BUTTON: n = 4; break;
|
||||
case GTK_ICON_SIZE_DND: n = 5; break;
|
||||
case GTK_ICON_SIZE_DIALOG: n = 6; break;
|
||||
default /*GTK_ICON_SIZE_INVALID*/: n = 0; break;
|
||||
}
|
||||
|
||||
if( icon_cache[n] == NULL )
|
||||
|
|
30
gtk/main.c
30
gtk/main.c
|
@ -49,6 +49,7 @@
|
|||
#include "makemeta-ui.h"
|
||||
#include "msgwin.h"
|
||||
#include "notify.h"
|
||||
#include "relocate.h"
|
||||
#include "stats.h"
|
||||
#include "tr-core.h"
|
||||
#include "tr-icon.h"
|
||||
|
@ -203,6 +204,7 @@ refreshActions( struct cbdata * data )
|
|||
action_sensitize( "delete-torrent", counts.totalCount != 0 );
|
||||
action_sensitize( "verify-torrent", counts.totalCount != 0 );
|
||||
action_sensitize( "open-torrent-folder", counts.totalCount == 1 );
|
||||
action_sensitize( "relocate-torrent", counts.totalCount == 1 );
|
||||
|
||||
canUpdate = 0;
|
||||
gtk_tree_selection_selected_foreach( s, accumulateCanUpdateForeach, &canUpdate );
|
||||
|
@ -1281,6 +1283,24 @@ pauseAllTorrents( struct cbdata * data )
|
|||
tr_rpc_request_exec_json( session, cmd, strlen( cmd ), NULL, NULL );
|
||||
}
|
||||
|
||||
static tr_torrent*
|
||||
getFirstSelectedTorrent( struct cbdata * data )
|
||||
{
|
||||
tr_torrent * tor = NULL;
|
||||
GtkTreeSelection * s = tr_window_get_selection( data->wind );
|
||||
GtkTreeModel * m;
|
||||
GList * l = gtk_tree_selection_get_selected_rows( s, &m );
|
||||
if( l != NULL ) {
|
||||
GtkTreePath * p = l->data;
|
||||
GtkTreeIter i;
|
||||
if( gtk_tree_model_get_iter( m, &i, p ) )
|
||||
gtk_tree_model_get( m, &i, MC_TORRENT_RAW, &tor, -1 );
|
||||
}
|
||||
g_list_foreach( l, (GFunc)gtk_tree_path_free, NULL );
|
||||
g_list_free( l );
|
||||
return tor;
|
||||
}
|
||||
|
||||
void
|
||||
doAction( const char * action_name, gpointer user_data )
|
||||
{
|
||||
|
@ -1311,6 +1331,16 @@ doAction( const char * action_name, gpointer user_data )
|
|||
{
|
||||
startAllTorrents( data );
|
||||
}
|
||||
else if( !strcmp( action_name, "relocate-torrent" ) )
|
||||
{
|
||||
tr_torrent * tor = getFirstSelectedTorrent( data );
|
||||
if( tor )
|
||||
{
|
||||
GtkWindow * parent = GTK_WINDOW( data->wind );
|
||||
GtkWidget * w = gtr_relocate_dialog_new( parent, tor );
|
||||
gtk_widget_show( w );
|
||||
}
|
||||
}
|
||||
else if( !strcmp( action_name, "pause-torrent" ) )
|
||||
{
|
||||
GtkTreeSelection * s = tr_window_get_selection( data->wind );
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* This file Copyright (C) 2009 Charles Kerr <charles@transmissionbt.com>
|
||||
*
|
||||
* This file is licensed by the GPL version 2. Works owned by the
|
||||
* Transmission project are granted a special exemption to clause 2(b)
|
||||
* so that the bulk of its code can remain under the MIT license.
|
||||
* This exemption does not extend to derived works not owned by
|
||||
* the Transmission project.
|
||||
*
|
||||
* $Id:$
|
||||
*/
|
||||
|
||||
#include <libtransmission/transmission.h>
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "conf.h" /* pref_string_get */
|
||||
#include "hig.h"
|
||||
#include "relocate.h"
|
||||
#include "util.h"
|
||||
|
||||
static char * previousLocation = NULL;
|
||||
|
||||
struct UpdateData
|
||||
{
|
||||
GtkDialog * dialog;
|
||||
tr_bool done;
|
||||
};
|
||||
|
||||
/* every once in awhile, check to see if the move is done.
|
||||
* if so, delete the dialog */
|
||||
static gboolean
|
||||
onTimer( gpointer gdata )
|
||||
{
|
||||
struct UpdateData * data = gdata;
|
||||
const tr_bool done = data->done;
|
||||
|
||||
if( done )
|
||||
{
|
||||
gtk_widget_destroy( GTK_WIDGET( data->dialog ) );
|
||||
g_free( data );
|
||||
}
|
||||
|
||||
return !done;
|
||||
}
|
||||
|
||||
static void
|
||||
onResponse( GtkDialog * dialog, int response, gpointer unused UNUSED )
|
||||
{
|
||||
if( response == GTK_RESPONSE_APPLY )
|
||||
{
|
||||
struct UpdateData * updateData;
|
||||
|
||||
GtkWidget * w;
|
||||
GObject * d = G_OBJECT( dialog );
|
||||
tr_torrent * tor = g_object_get_data( d, "torrent" );
|
||||
GtkFileChooser * chooser = g_object_get_data( d, "chooser" );
|
||||
GtkToggleButton * move_tb = g_object_get_data( d, "move_rb" );
|
||||
char * location = gtk_file_chooser_get_filename( chooser );
|
||||
const gboolean do_move = gtk_toggle_button_get_active( move_tb );
|
||||
|
||||
/* pop up a dialog saying that the work is in progress */
|
||||
w = gtk_message_dialog_new( GTK_WINDOW( dialog ),
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
|
||||
GTK_MESSAGE_INFO,
|
||||
GTK_BUTTONS_CLOSE,
|
||||
_( "Moving \"%s\"" ),
|
||||
tr_torrentInfo(tor)->name );
|
||||
gtk_message_dialog_format_secondary_text( GTK_MESSAGE_DIALOG( w ), _( "This may take a moment..." ) );
|
||||
gtk_dialog_set_response_sensitive( GTK_DIALOG( w ), GTK_RESPONSE_CLOSE, FALSE );
|
||||
gtk_widget_show( w );
|
||||
|
||||
/* start the move and periodically check its status */
|
||||
updateData = g_new( struct UpdateData, 1 );
|
||||
updateData->dialog = dialog;
|
||||
updateData->done = FALSE;
|
||||
tr_torrentSetLocation( tor, location, do_move, &updateData->done );
|
||||
gtr_timeout_add_seconds( 1, onTimer, updateData );
|
||||
|
||||
/* remember this location so that it can be the default next time */
|
||||
g_free( previousLocation );
|
||||
previousLocation = location;
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_widget_destroy( GTK_WIDGET( dialog ) );
|
||||
}
|
||||
}
|
||||
|
||||
GtkWidget*
|
||||
gtr_relocate_dialog_new( GtkWindow * parent, tr_torrent * tor )
|
||||
{
|
||||
int row;
|
||||
GtkWidget * w;
|
||||
GtkWidget * d;
|
||||
GtkWidget * t;
|
||||
|
||||
d = gtk_dialog_new_with_buttons( _( "Set Torrent Location" ), parent,
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT |
|
||||
GTK_DIALOG_MODAL |
|
||||
GTK_DIALOG_NO_SEPARATOR,
|
||||
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
||||
GTK_STOCK_APPLY, GTK_RESPONSE_APPLY,
|
||||
NULL );
|
||||
g_object_set_data( G_OBJECT( d ), "torrent", tor );
|
||||
gtk_dialog_set_default_response( GTK_DIALOG( d ),
|
||||
GTK_RESPONSE_CANCEL );
|
||||
gtk_dialog_set_alternative_button_order( GTK_DIALOG( d ),
|
||||
GTK_RESPONSE_APPLY,
|
||||
GTK_RESPONSE_CANCEL,
|
||||
-1 );
|
||||
g_signal_connect( d, "response", G_CALLBACK( onResponse ), NULL );
|
||||
|
||||
row = 0;
|
||||
t = hig_workarea_create( );
|
||||
hig_workarea_add_section_title( t, &row, _( "Location" ) );
|
||||
|
||||
if( previousLocation == NULL )
|
||||
previousLocation = g_strdup( pref_string_get( TR_PREFS_KEY_DOWNLOAD_DIR ) );
|
||||
w = gtk_file_chooser_button_new( _( "Set Torrent Location" ), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER );
|
||||
gtk_file_chooser_set_filename( GTK_FILE_CHOOSER( w ), previousLocation );
|
||||
g_object_set_data( G_OBJECT( d ), "chooser", w );
|
||||
hig_workarea_add_row( t, &row, _( "Torrent _location:" ), w, NULL );
|
||||
w = gtk_radio_button_new_with_mnemonic( NULL, _( "_Move from the current folder" ) );
|
||||
g_object_set_data( G_OBJECT( d ), "move_rb", w );
|
||||
hig_workarea_add_wide_control( t, &row, w );
|
||||
w = gtk_radio_button_new_with_mnemonic_from_widget( GTK_RADIO_BUTTON( w ), _( "Local data is _already there" ) );
|
||||
hig_workarea_add_wide_control( t, &row, w );
|
||||
hig_workarea_finish( t, &row );
|
||||
gtk_widget_show_all( t );
|
||||
gtk_box_pack_start( GTK_BOX( GTK_DIALOG( d )->vbox ), t, TRUE, TRUE, 0 );
|
||||
|
||||
return d;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* This file Copyright (C) 2009 Charles Kerr <charles@transmissionbt.com>
|
||||
*
|
||||
* This file is licensed by the GPL version 2. Works owned by the
|
||||
* Transmission project are granted a special exemption to clause 2(b)
|
||||
* so that the bulk of its code can remain under the MIT license.
|
||||
* This exemption does not extend to derived works not owned by
|
||||
* the Transmission project.
|
||||
*
|
||||
* $Id:$
|
||||
*/
|
||||
|
||||
#ifndef GTR_RELOCATE_H
|
||||
#define GTR_RELOCATE_H
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <libtransmission/transmission.h>
|
||||
|
||||
GtkWidget * gtr_relocate_dialog_new( GtkWindow * parent, tr_torrent * tor );
|
||||
|
||||
#endif
|
|
@ -50,7 +50,7 @@ getProgressString( const tr_torrent * tor,
|
|||
char buf1[32], buf2[32], buf3[32], buf4[32];
|
||||
char * str;
|
||||
double seedRatio;
|
||||
gboolean hasSeedRatio;
|
||||
gboolean hasSeedRatio = FALSE;
|
||||
|
||||
if( !isDone )
|
||||
{
|
||||
|
|
6
gtk/ui.h
6
gtk/ui.h
|
@ -5,8 +5,9 @@ static const char * fallback_ui_file =
|
|||
" <menuitem action='add-torrent-menu'/>\n"
|
||||
" <menuitem action='new-torrent'/>\n"
|
||||
" <separator/>\n"
|
||||
" <menuitem action='show-torrent-properties'/>\n"
|
||||
" <menuitem action='open-torrent-folder'/>\n"
|
||||
" <menuitem action='show-torrent-properties'/>\n"
|
||||
" <menuitem action='relocate-torrent'/>\n"
|
||||
" <separator/>\n"
|
||||
" <menuitem action='start-torrent'/>\n"
|
||||
" <menuitem action='update-tracker'/>\n"
|
||||
|
@ -64,8 +65,9 @@ static const char * fallback_ui_file =
|
|||
" </toolbar>\n"
|
||||
"\n"
|
||||
" <popup name='main-window-popup'>\n"
|
||||
" <menuitem action='show-torrent-properties'/>\n"
|
||||
" <menuitem action='open-torrent-folder'/>\n"
|
||||
" <menuitem action='show-torrent-properties'/>\n"
|
||||
" <menuitem action='relocate-torrent'/>\n"
|
||||
" <separator/>\n"
|
||||
" <menuitem action='start-torrent'/>\n"
|
||||
" <menuitem action='pause-torrent'/>\n"
|
||||
|
|
|
@ -99,7 +99,7 @@ void gtr_widget_set_tooltip_text( GtkWidget * w, const char * tip );
|
|||
GtkWidget * gtr_button_new_from_stock( const char * stock,
|
||||
const char * mnemonic );
|
||||
|
||||
guint gtr_timeout_add_seconds( guint interval,
|
||||
guint gtr_timeout_add_seconds( guint seconds,
|
||||
GSourceFunc function,
|
||||
gpointer data );
|
||||
|
||||
|
|
|
@ -201,6 +201,25 @@ preallocateFileFull( const char * filename, uint64_t length )
|
|||
return success;
|
||||
}
|
||||
|
||||
tr_bool
|
||||
tr_preallocate_file( const char * filename, uint64_t length )
|
||||
{
|
||||
return preallocateFileFull( filename, length );
|
||||
}
|
||||
|
||||
int
|
||||
tr_open_file_for_writing( const char * filename )
|
||||
{
|
||||
int flags = O_WRONLY | O_CREAT;
|
||||
#ifdef O_BINARY
|
||||
flags |= O_BINARY;
|
||||
#endif
|
||||
#ifdef O_LARGEFILE
|
||||
flags |= O_LARGEFILE;
|
||||
#endif
|
||||
return open( filename, flags, 0666 );
|
||||
}
|
||||
|
||||
int
|
||||
tr_open_file_for_scanning( const char * filename )
|
||||
{
|
||||
|
|
|
@ -34,8 +34,12 @@ void tr_fdInit( size_t openFileLimit,
|
|||
|
||||
int tr_open_file_for_scanning( const char * filename );
|
||||
|
||||
int tr_open_file_for_writing( const char * filename );
|
||||
|
||||
void tr_close_file( int fd );
|
||||
|
||||
tr_bool tr_preallocate_file( const char * filename, uint64_t length );
|
||||
|
||||
int64_t tr_lseek( int fd, int64_t offset, int whence );
|
||||
|
||||
|
||||
|
|
|
@ -764,6 +764,42 @@ torrentSet( tr_session * session,
|
|||
return errmsg;
|
||||
}
|
||||
|
||||
static const char*
|
||||
torrentSetLocation( tr_session * session,
|
||||
tr_benc * args_in,
|
||||
tr_benc * args_out UNUSED,
|
||||
struct tr_rpc_idle_data * idle_data )
|
||||
{
|
||||
const char * errmsg = NULL;
|
||||
const char * location = NULL;
|
||||
|
||||
assert( idle_data == NULL );
|
||||
|
||||
if( !tr_bencDictFindStr( args_in, "location", &location ) )
|
||||
{
|
||||
errmsg = "no location";
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_bool move = FALSE;
|
||||
int i, torrentCount;
|
||||
tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
|
||||
|
||||
tr_bencDictFindBool( args_in, "move", &move );
|
||||
|
||||
for( i=0; i<torrentCount; ++i )
|
||||
{
|
||||
tr_torrent * tor = torrents[i];
|
||||
tr_torrentSetLocation( tor, location, move, NULL );
|
||||
notify( session, TR_RPC_TORRENT_CHANGED, tor );
|
||||
}
|
||||
|
||||
tr_free( torrents );
|
||||
}
|
||||
|
||||
return errmsg;
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
@ -1201,7 +1237,7 @@ sessionGet( tr_session * s,
|
|||
tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT, tr_sessionGetPeerPort( s ) );
|
||||
tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, tr_sessionGetPeerPortRandomOnStart( s ) );
|
||||
tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING, tr_sessionIsPortForwardingEnabled( s ) );
|
||||
tr_bencDictAddInt ( d, "rpc-version", 5 );
|
||||
tr_bencDictAddInt ( d, "rpc-version", 6 );
|
||||
tr_bencDictAddInt ( d, "rpc-version-minimum", 1 );
|
||||
tr_bencDictAddReal( d, "seedRatioLimit", tr_sessionGetRatioLimit( s ) );
|
||||
tr_bencDictAddBool( d, "seedRatioLimited", tr_sessionIsRatioLimited( s ) );
|
||||
|
@ -1234,19 +1270,20 @@ static struct method
|
|||
}
|
||||
methods[] =
|
||||
{
|
||||
{ "port-test", FALSE, portTest },
|
||||
{ "blocklist-update", FALSE, blocklistUpdate },
|
||||
{ "session-get", TRUE, sessionGet },
|
||||
{ "session-set", TRUE, sessionSet },
|
||||
{ "session-stats", TRUE, sessionStats },
|
||||
{ "torrent-add", FALSE, torrentAdd },
|
||||
{ "torrent-get", TRUE, torrentGet },
|
||||
{ "torrent-remove", TRUE, torrentRemove },
|
||||
{ "torrent-set", TRUE, torrentSet },
|
||||
{ "torrent-start", TRUE, torrentStart },
|
||||
{ "torrent-stop", TRUE, torrentStop },
|
||||
{ "torrent-verify", TRUE, torrentVerify },
|
||||
{ "torrent-reannounce", TRUE, torrentReannounce }
|
||||
{ "port-test", FALSE, portTest },
|
||||
{ "blocklist-update", FALSE, blocklistUpdate },
|
||||
{ "session-get", TRUE, sessionGet },
|
||||
{ "session-set", TRUE, sessionSet },
|
||||
{ "session-stats", TRUE, sessionStats },
|
||||
{ "torrent-add", FALSE, torrentAdd },
|
||||
{ "torrent-get", TRUE, torrentGet },
|
||||
{ "torrent-remove", TRUE, torrentRemove },
|
||||
{ "torrent-set", TRUE, torrentSet },
|
||||
{ "torrent-set-location", TRUE, torrentSetLocation },
|
||||
{ "torrent-start", TRUE, torrentStart },
|
||||
{ "torrent-stop", TRUE, torrentStop },
|
||||
{ "torrent-verify", TRUE, torrentVerify },
|
||||
{ "torrent-reannounce", TRUE, torrentReannounce }
|
||||
};
|
||||
|
||||
static void
|
||||
|
|
|
@ -972,7 +972,7 @@ tr_sessionGetActiveSpeedLimit( const tr_session * session, tr_direction dir, int
|
|||
static void
|
||||
updateBandwidth( tr_session * session, tr_direction dir )
|
||||
{
|
||||
int limit;
|
||||
int limit = 0;
|
||||
const tr_bool isLimited = tr_sessionGetActiveSpeedLimit( session, dir, &limit );
|
||||
const tr_bool zeroCase = isLimited && !limit;
|
||||
|
||||
|
|
|
@ -171,9 +171,9 @@ tr_ctorSetFilePriorities( tr_ctor * ctor,
|
|||
tr_file_index_t * mycount;
|
||||
|
||||
switch( priority ) {
|
||||
case TR_PRI_NORMAL: myfiles = &ctor->normal; mycount = &ctor->normalSize; break;
|
||||
case TR_PRI_LOW: myfiles = &ctor->low; mycount = &ctor->lowSize; break;
|
||||
case TR_PRI_HIGH: myfiles = &ctor->high; mycount = &ctor->highSize; break;
|
||||
default /*TR_PRI_NORMAL*/: myfiles = &ctor->normal; mycount = &ctor->normalSize; break;
|
||||
}
|
||||
|
||||
tr_free( *myfiles );
|
||||
|
|
|
@ -239,7 +239,7 @@ tr_torrentGetSeedRatio( const tr_torrent * tor, double * ratio )
|
|||
*ratio = tr_sessionGetRatioLimit( tor->session );
|
||||
break;
|
||||
|
||||
case TR_RATIOLIMIT_UNLIMITED:
|
||||
default: /* TR_RATIOLIMIT_UNLIMITED */
|
||||
isLimited = FALSE;
|
||||
break;
|
||||
}
|
||||
|
@ -734,8 +734,7 @@ tr_torrentNew( const tr_ctor * ctor,
|
|||
**/
|
||||
|
||||
void
|
||||
tr_torrentSetDownloadDir( tr_torrent * tor,
|
||||
const char * path )
|
||||
tr_torrentSetDownloadDir( tr_torrent * tor, const char * path )
|
||||
{
|
||||
assert( tr_isTorrent( tor ) );
|
||||
|
||||
|
@ -1235,8 +1234,7 @@ checkAndStartCB( tr_torrent * tor )
|
|||
}
|
||||
|
||||
static void
|
||||
torrentStart( tr_torrent * tor,
|
||||
int reloadProgress )
|
||||
torrentStart( tr_torrent * tor, int reloadProgress )
|
||||
{
|
||||
assert( tr_isTorrent( tor ) );
|
||||
|
||||
|
@ -1272,6 +1270,12 @@ torrentRecheckDoneImpl( void * vtor )
|
|||
|
||||
assert( tr_isTorrent( tor ) );
|
||||
tr_torrentRecheckCompleteness( tor );
|
||||
|
||||
if( tor->startAfterVerify )
|
||||
{
|
||||
tor->startAfterVerify = FALSE;
|
||||
tr_torrentStart( tor );
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1286,11 +1290,18 @@ void
|
|||
tr_torrentVerify( tr_torrent * tor )
|
||||
{
|
||||
assert( tr_isTorrent( tor ) );
|
||||
|
||||
tr_verifyRemove( tor );
|
||||
|
||||
tr_globalLock( tor->session );
|
||||
|
||||
/* if the torrent's already being verified, stop it */
|
||||
tr_verifyRemove( tor );
|
||||
|
||||
/* if the torrent's running, stop it & set the restart-after-verify flag */
|
||||
if( tor->isRunning ) {
|
||||
tr_torrentStop( tor );
|
||||
tor->startAfterVerify = TRUE;
|
||||
}
|
||||
|
||||
/* add the torrent to the recheck queue */
|
||||
tr_torrentUncheck( tor );
|
||||
tr_verifyAdd( tor, torrentRecheckDoneCB );
|
||||
|
||||
|
@ -1316,7 +1327,6 @@ tr_torrentCloseLocalFiles( const tr_torrent * tor )
|
|||
evbuffer_free( buf );
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
stopTorrent( void * vtor )
|
||||
{
|
||||
|
@ -2198,6 +2208,105 @@ tr_torrentDeleteLocalData( tr_torrent * tor, tr_fileFunc fileFunc )
|
|||
****
|
||||
***/
|
||||
|
||||
struct LocationData
|
||||
{
|
||||
tr_bool move_from_old_location;
|
||||
tr_bool * setme_done;
|
||||
char * location;
|
||||
tr_torrent * tor;
|
||||
};
|
||||
|
||||
static void
|
||||
setLocation( void * vdata )
|
||||
{
|
||||
struct LocationData * data = vdata;
|
||||
tr_torrent * tor = data->tor;
|
||||
const tr_bool do_move = data->move_from_old_location;
|
||||
const char * location = data->location;
|
||||
|
||||
assert( tr_isTorrent( tor ) );
|
||||
|
||||
if( strcmp( location, tor->downloadDir ) )
|
||||
{
|
||||
tr_file_index_t i;
|
||||
|
||||
/* bad idea to move files while they're being verified... */
|
||||
tr_verifyRemove( tor );
|
||||
|
||||
/* if the torrent is running, stop it and set a flag to
|
||||
* restart after we're done */
|
||||
if( tor->isRunning )
|
||||
{
|
||||
tr_torrentStop( tor );
|
||||
tor->startAfterVerify = TRUE;
|
||||
}
|
||||
|
||||
/* try to move the files.
|
||||
* FIXME: there are still all kinds of nasty cases, like what
|
||||
* if the target directory runs out of space halfway through... */
|
||||
for( i=0; i<tor->info.fileCount; ++i )
|
||||
{
|
||||
struct stat sb;
|
||||
char * oldpath = tr_buildPath( tor->downloadDir, tor->info.files[i].name, NULL );
|
||||
char * newpath = tr_buildPath( location, tor->info.files[i].name, NULL );
|
||||
|
||||
|
||||
if( do_move && !stat( oldpath, &sb ) )
|
||||
{
|
||||
tr_moveFile( oldpath, newpath );
|
||||
tr_torinf( tor, "moving \"%s\" to \"%s\"", oldpath, newpath );
|
||||
}
|
||||
else if( !stat( newpath, &sb ) )
|
||||
{
|
||||
tr_torinf( tor, "found \"%s\"", newpath );
|
||||
}
|
||||
|
||||
tr_free( newpath );
|
||||
tr_free( oldpath );
|
||||
}
|
||||
|
||||
/* blow away the leftover subdirectories in the old location */
|
||||
tr_torrentDeleteLocalData( tor, unlink );
|
||||
|
||||
/* set the new location and reverify */
|
||||
tr_torrentSetDownloadDir( tor, location );
|
||||
tr_torrentVerify( tor );
|
||||
}
|
||||
|
||||
if( data->setme_done )
|
||||
*data->setme_done = TRUE;
|
||||
|
||||
/* cleanup */
|
||||
tr_free( data->location );
|
||||
tr_free( data );
|
||||
}
|
||||
|
||||
void
|
||||
tr_torrentSetLocation( tr_torrent * tor,
|
||||
const char * location,
|
||||
tr_bool move_from_old_location,
|
||||
tr_bool * setme_done )
|
||||
{
|
||||
struct LocationData * data;
|
||||
|
||||
assert( tr_isTorrent( tor ) );
|
||||
|
||||
if( setme_done )
|
||||
*setme_done = FALSE;
|
||||
|
||||
/* run this in the libtransmission thread */
|
||||
data = tr_new( struct LocationData, 1 );
|
||||
data->tor = tor;
|
||||
data->location = tr_strdup( location );
|
||||
data->move_from_old_location = move_from_old_location;
|
||||
data->setme_done = setme_done;
|
||||
tr_runInEventThread( tor->session, setLocation, data );
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
void
|
||||
tr_torrentCheckSeedRatio( tr_torrent * tor )
|
||||
{
|
||||
|
|
|
@ -197,6 +197,7 @@ struct tr_torrent
|
|||
tr_bool isRunning;
|
||||
tr_bool isDeleting;
|
||||
tr_bool needsSeedRatioCheck;
|
||||
tr_bool startAfterVerify;
|
||||
|
||||
uint16_t maxConnectedPeers;
|
||||
|
||||
|
|
|
@ -935,6 +935,12 @@ void tr_torrentStop( tr_torrent * torrent );
|
|||
|
||||
typedef int tr_fileFunc( const char * filename );
|
||||
|
||||
/** @brief Tell transmsision where to find this torrent's local data */
|
||||
void tr_torrentSetLocation( tr_torrent * torrent,
|
||||
const char * location,
|
||||
tr_bool move_from_previous_location,
|
||||
tr_bool * setme_done );
|
||||
|
||||
/**
|
||||
* @brief Deletes the torrent's local data.
|
||||
* @param torrent
|
||||
|
@ -1054,8 +1060,10 @@ void tr_torrentSetFileDLs( tr_torrent * torrent,
|
|||
|
||||
const tr_info * tr_torrentInfo( const tr_torrent * torrent );
|
||||
|
||||
void tr_torrentSetDownloadDir( tr_torrent * torrent,
|
||||
const char * path );
|
||||
/* Raw function to change the torrent's downloadDir field.
|
||||
This should only be used by libtransmission or to bootstrap
|
||||
a newly-instantiated tr_torrent object. */
|
||||
void tr_torrentSetDownloadDir( tr_torrent * torrent, const char * path );
|
||||
|
||||
const char * tr_torrentGetDownloadDir( const tr_torrent * torrent );
|
||||
|
||||
|
|
|
@ -1566,3 +1566,72 @@ tr_strratio( char * buf, size_t buflen, double ratio, const char * infinity )
|
|||
tr_snprintf( buf, buflen, "%'.0f", ratio );
|
||||
return buf;
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
int
|
||||
tr_moveFile( const char * oldpath, const char * newpath )
|
||||
{
|
||||
int in;
|
||||
int out;
|
||||
char * buf;
|
||||
struct stat st;
|
||||
size_t bytesLeft;
|
||||
size_t buflen;
|
||||
|
||||
/* make sure the old file exists */
|
||||
if( stat( oldpath, &st ) ) {
|
||||
const int err = errno;
|
||||
errno = err;
|
||||
return -1;
|
||||
}
|
||||
if( !S_ISREG( st.st_mode ) ) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
bytesLeft = st.st_size;
|
||||
|
||||
/* make sure the target directory exists */
|
||||
{
|
||||
char * newdir = tr_dirname( newpath );
|
||||
int i = tr_mkdirp( newdir, 0777 );
|
||||
tr_free( newdir );
|
||||
if( i )
|
||||
return i;
|
||||
}
|
||||
|
||||
/* they might be on the same filesystem... */
|
||||
if( !rename( oldpath, newpath ) )
|
||||
return 0;
|
||||
|
||||
/* copy the file */
|
||||
in = tr_open_file_for_scanning( oldpath );
|
||||
tr_preallocate_file( newpath, bytesLeft );
|
||||
out = tr_open_file_for_writing( newpath );
|
||||
buflen = stat( newpath, &st ) ? 4096 : st.st_blksize;
|
||||
buf = tr_new( char, buflen );
|
||||
while( bytesLeft > 0 )
|
||||
{
|
||||
ssize_t bytesWritten;
|
||||
const size_t bytesThisPass = MIN( bytesLeft, buflen );
|
||||
const int numRead = read( in, buf, bytesThisPass );
|
||||
if( numRead < 0 )
|
||||
break;
|
||||
bytesWritten = write( out, buf, numRead );
|
||||
if( bytesWritten < 0 )
|
||||
break;
|
||||
bytesLeft -= bytesWritten;
|
||||
}
|
||||
|
||||
/* cleanup */
|
||||
tr_free( buf );
|
||||
tr_close_file( out );
|
||||
tr_close_file( in );
|
||||
if( bytesLeft != 0 )
|
||||
return -1;
|
||||
|
||||
unlink( oldpath );
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -469,6 +469,9 @@ char* tr_strratio( char * buf, size_t buflen, double ratio, const char * infinit
|
|||
struct tm * tr_localtime_r( const time_t *_clock, struct tm *_result );
|
||||
|
||||
|
||||
/** on success, return 0. on failure, return -1 and set errno */
|
||||
int tr_moveFile( const char * oldpath, const char * newpath );
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "options.h"
|
||||
#include "prefs.h"
|
||||
#include "prefs-dialog.h"
|
||||
#include "relocate.h"
|
||||
#include "session.h"
|
||||
#include "session-dialog.h"
|
||||
#include "speed.h"
|
||||
|
@ -157,15 +158,20 @@ TrMainWindow :: TrMainWindow( Session& session, Prefs& prefs, TorrentModel& mode
|
|||
connect( ui.action_About, SIGNAL(triggered()), myAboutDialog, SLOT(show()));
|
||||
connect( ui.action_Contents, SIGNAL(triggered()), this, SLOT(openHelp()));
|
||||
connect( ui.action_OpenFolder, SIGNAL(triggered()), this, SLOT(openFolder()));
|
||||
connect( ui.action_SetLocation, SIGNAL(triggered()), this, SLOT(setLocation()));
|
||||
connect( ui.action_Properties, SIGNAL(triggered()), this, SLOT(openProperties()));
|
||||
connect( ui.action_SessionDialog, SIGNAL(triggered()), mySessionDialog, SLOT(show()));
|
||||
connect( ui.listView, SIGNAL(activated(const QModelIndex&)), ui.action_Properties, SLOT(trigger()));
|
||||
|
||||
QAction * sep2 = new QAction( this );
|
||||
sep2->setSeparator( true );
|
||||
|
||||
// context menu
|
||||
QList<QAction*> actions;
|
||||
actions << ui.action_Properties
|
||||
<< ui.action_OpenFolder
|
||||
<< sep
|
||||
actions << ui.action_OpenFolder
|
||||
<< ui.action_Properties
|
||||
<< ui.action_SetLocation
|
||||
<< sep2
|
||||
<< ui.action_Start
|
||||
<< ui.action_Pause
|
||||
<< ui.action_Verify
|
||||
|
@ -613,6 +619,14 @@ TrMainWindow :: openProperties( )
|
|||
myDetailsDialog->show( );
|
||||
}
|
||||
|
||||
void
|
||||
TrMainWindow :: setLocation( )
|
||||
{
|
||||
const int torrentId( *getSelectedTorrents().begin() );
|
||||
QDialog * d = new RelocateDialog( mySession, torrentId, this );
|
||||
d->show( );
|
||||
}
|
||||
|
||||
void
|
||||
TrMainWindow :: openFolder( )
|
||||
{
|
||||
|
@ -733,6 +747,7 @@ TrMainWindow :: refreshActionSensitivity( )
|
|||
|
||||
const bool oneSelection( selected == 1 );
|
||||
ui.action_OpenFolder->setEnabled( oneSelection && mySession.isLocal( ) );
|
||||
ui.action_SetLocation->setEnabled( oneSelection );
|
||||
|
||||
ui.action_SelectAll->setEnabled( selected < rowCount );
|
||||
ui.action_StartAll->setEnabled( paused > 0 );
|
||||
|
|
|
@ -113,6 +113,7 @@ class TrMainWindow: public QMainWindow
|
|||
void addTorrents( const QStringList& filenames );
|
||||
void openHelp( );
|
||||
void openFolder( );
|
||||
void setLocation( );
|
||||
void openProperties( );
|
||||
void toggleSpeedMode( );
|
||||
void dataReadProgress( );
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>792</width>
|
||||
<height>23</height>
|
||||
<height>25</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
|
@ -68,8 +68,9 @@
|
|||
<addaction name="action_Add"/>
|
||||
<addaction name="action_New"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Properties"/>
|
||||
<addaction name="action_OpenFolder"/>
|
||||
<addaction name="action_Properties"/>
|
||||
<addaction name="action_SetLocation"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Start"/>
|
||||
<addaction name="action_Announce"/>
|
||||
|
@ -531,6 +532,11 @@
|
|||
<string extracomment="Start a local session or connect to a running session">Choose Session</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_SetLocation">
|
||||
<property name="text">
|
||||
<string>Set &Location...</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="application.qrc"/>
|
||||
|
|
|
@ -24,10 +24,10 @@ FORMS += mainwin.ui
|
|||
RESOURCES += application.qrc
|
||||
SOURCES += about.cc app.cc details.cc file-tree.cc filters.cc hig.cc \
|
||||
license.cc mainwin.cc make-dialog.cc options.cc prefs.cc \
|
||||
prefs-dialog.cc qticonloader.cc session.cc session-dialog.cc \
|
||||
squeezelabel.cc stats-dialog.cc torrent.cc torrent-delegate.cc \
|
||||
torrent-delegate-min.cc torrent-filter.cc torrent-model.cc \
|
||||
triconpushbutton.cc utils.cc watchdir.cc
|
||||
prefs-dialog.cc qticonloader.cc relocate.cc session.cc \
|
||||
session-dialog.cc squeezelabel.cc stats-dialog.cc torrent.cc \
|
||||
torrent-delegate.cc torrent-delegate-min.cc torrent-filter.cc \
|
||||
torrent-model.cc triconpushbutton.cc utils.cc watchdir.cc
|
||||
HEADERS += $$replace(SOURCES, .cc, .h)
|
||||
HEADERS += speed.h types.h
|
||||
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* This file Copyright (C) 2009 Charles Kerr <charles@transmissionbt.com>
|
||||
*
|
||||
* This file is licensed by the GPL version 2. Works owned by the
|
||||
* Transmission project are granted a special exemption to clause 2(b)
|
||||
* so that the bulk of its code can remain under the MIT license.
|
||||
* This exemption does not extend to derived works not owned by
|
||||
* the Transmission project.
|
||||
*
|
||||
* $Id:$
|
||||
*/
|
||||
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <QRadioButton>
|
||||
#include <QDir>
|
||||
#include <QFileDialog>
|
||||
#include <QSet>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QWidget>
|
||||
|
||||
#include "hig.h"
|
||||
#include "relocate.h"
|
||||
#include "session.h"
|
||||
|
||||
QString RelocateDialog :: myPath;
|
||||
|
||||
bool RelocateDialog :: myMoveFlag = true;
|
||||
|
||||
void
|
||||
RelocateDialog :: onSetLocation( )
|
||||
{
|
||||
QSet<int> ids;
|
||||
ids << myTorrentId;
|
||||
mySession.torrentSetLocation( ids, myPath, myMoveFlag );
|
||||
deleteLater( );
|
||||
}
|
||||
|
||||
void
|
||||
RelocateDialog :: onFileSelected( const QString& path )
|
||||
{
|
||||
myPath = path;
|
||||
myDirButton->setText( myPath );
|
||||
}
|
||||
|
||||
void
|
||||
RelocateDialog :: onDirButtonClicked( )
|
||||
{
|
||||
QFileDialog * d = new QFileDialog( this );
|
||||
d->setFileMode( QFileDialog::Directory );
|
||||
d->selectFile( myPath );
|
||||
d->show( );
|
||||
connect( d, SIGNAL(fileSelected(const QString&)), this, SLOT(onFileSelected(const QString&)));
|
||||
}
|
||||
|
||||
void
|
||||
RelocateDialog :: onMoveToggled( bool b )
|
||||
{
|
||||
myMoveFlag = b;
|
||||
}
|
||||
|
||||
RelocateDialog :: RelocateDialog( Session& session, int torrentId, QWidget * parent ):
|
||||
QDialog( parent ),
|
||||
mySession( session ),
|
||||
myTorrentId( torrentId )
|
||||
{
|
||||
QRadioButton * find_rb;
|
||||
setWindowTitle( tr( "Set Torrent Location" ) );
|
||||
|
||||
if( myPath.isEmpty( ) )
|
||||
myPath = QDir::homePath( );
|
||||
|
||||
HIG * hig = new HIG( );
|
||||
hig->addSectionTitle( tr( "Location" ) );
|
||||
hig->addRow( tr( "Torrent &location:" ), myDirButton = new QPushButton( myPath ) );
|
||||
hig->addWideControl( myMoveRadio = new QRadioButton( tr( "&Move from the current folder" ), this ) );
|
||||
hig->addWideControl( find_rb = new QRadioButton( tr( "Local data is &already there" ), this ) );
|
||||
hig->finish( );
|
||||
|
||||
if( myMoveFlag )
|
||||
myMoveRadio->setChecked( true );
|
||||
else
|
||||
find_rb->setChecked( true );
|
||||
|
||||
connect( myMoveRadio, SIGNAL(toggled(bool)), this, SLOT(onMoveToggled(bool)));
|
||||
connect( myDirButton, SIGNAL(clicked(bool)), this, SLOT(onDirButtonClicked()));
|
||||
|
||||
QLayout * layout = new QVBoxLayout( this );
|
||||
layout->addWidget( hig );
|
||||
QDialogButtonBox * buttons = new QDialogButtonBox( QDialogButtonBox::Ok|QDialogButtonBox::Cancel );
|
||||
connect( buttons, SIGNAL(rejected()), this, SLOT(deleteLater()));
|
||||
connect( buttons, SIGNAL(accepted()), this, SLOT(onSetLocation()));
|
||||
layout->addWidget( buttons );
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* This file Copyright (C) 2009 Charles Kerr <charles@transmissionbt.com>
|
||||
*
|
||||
* This file is licensed by the GPL version 2. Works owned by the
|
||||
* Transmission project are granted a special exemption to clause 2(b)
|
||||
* so that the bulk of its code can remain under the MIT license.
|
||||
* This exemption does not extend to derived works not owned by
|
||||
* the Transmission project.
|
||||
*
|
||||
* $Id:$
|
||||
*/
|
||||
|
||||
#ifndef RELOCATE_DIALOG_H
|
||||
#define RELOCATE_DIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QString>
|
||||
|
||||
class QPushButton;
|
||||
class QRadioButton;
|
||||
class Session;
|
||||
|
||||
class RelocateDialog: public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
static QString myPath;
|
||||
static bool myMoveFlag;
|
||||
|
||||
private:
|
||||
Session & mySession;
|
||||
const int myTorrentId;
|
||||
QPushButton * myDirButton;
|
||||
QRadioButton * myMoveRadio;
|
||||
|
||||
private slots:
|
||||
void onFileSelected( const QString& path );
|
||||
void onDirButtonClicked( );
|
||||
void onSetLocation( );
|
||||
void onMoveToggled( bool );
|
||||
|
||||
public:
|
||||
RelocateDialog( Session&, int torrentId, QWidget * parent = 0 );
|
||||
~RelocateDialog( ) { }
|
||||
};
|
||||
|
||||
#endif
|
|
@ -375,6 +375,19 @@ Session :: torrentSet( const QSet<int>& ids, const QString& key, const QList<int
|
|||
tr_bencFree( &top );
|
||||
}
|
||||
|
||||
void
|
||||
Session :: torrentSetLocation( const QSet<int>& ids, const QString& location, bool doMove )
|
||||
{
|
||||
tr_benc top;
|
||||
tr_bencInitDict( &top, 2 );
|
||||
tr_bencDictAddStr( &top, "method", "torrent-set-location" );
|
||||
tr_benc * args( tr_bencDictAddDict( &top, "arguments", 3 ) );
|
||||
addOptionalIds( args, ids );
|
||||
tr_bencDictAddStr( args, "location", location.toUtf8().constData() );
|
||||
tr_bencDictAddBool( args, "move", doMove );
|
||||
exec( &top );
|
||||
tr_bencFree( &top );
|
||||
}
|
||||
|
||||
void
|
||||
Session :: refreshTorrents( const QSet<int>& ids )
|
||||
|
@ -536,15 +549,15 @@ Session :: exec( const char * request )
|
|||
}
|
||||
else if( !myUrl.isEmpty( ) )
|
||||
{
|
||||
const QByteArray data( request, strlen( request ) );
|
||||
static const QString path( "/transmission/rpc" );
|
||||
QHttpRequestHeader header( "POST", path );
|
||||
header.setValue( "User-Agent", QCoreApplication::instance()->applicationName() + "/" + LONG_VERSION_STRING );
|
||||
header.setValue( "Content-Type", "application/json; charset=UTF-8" );
|
||||
if( !mySessionId.isEmpty( ) )
|
||||
header.setValue( TR_RPC_SESSION_ID_HEADER, mySessionId );
|
||||
myHttp.setProperty( "current-request", data );
|
||||
myHttp.request( header, data, &myBuffer );
|
||||
QBuffer * reqbuf = new QBuffer;
|
||||
reqbuf->setData( QByteArray( request ) );
|
||||
myHttp.request( header, reqbuf, &myBuffer );
|
||||
#ifdef DEBUG_HTTP
|
||||
std::cerr << "sending " << qPrintable(header.toString()) << "\nBody:\n" << request << std::endl;
|
||||
#endif
|
||||
|
@ -563,6 +576,7 @@ void
|
|||
Session :: onRequestFinished( int id, bool error )
|
||||
{
|
||||
Q_UNUSED( id );
|
||||
QIODevice * sourceDevice = myHttp.currentSourceDevice( );
|
||||
|
||||
QHttpResponseHeader response = myHttp.lastResponse();
|
||||
|
||||
|
@ -579,7 +593,7 @@ Session :: onRequestFinished( int id, bool error )
|
|||
// we got a 409 telling us our session id has expired.
|
||||
// update it and resubmit the request.
|
||||
mySessionId = response.value( TR_RPC_SESSION_ID_HEADER );
|
||||
exec( myHttp.property("current-request").toByteArray().constData() );
|
||||
exec( qobject_cast<QBuffer*>(sourceDevice)->buffer().constData() );
|
||||
}
|
||||
else if( error )
|
||||
{
|
||||
|
|
|
@ -91,6 +91,7 @@ class Session: public QObject
|
|||
void torrentSet( const QSet<int>& ids, const QString& key, int val );
|
||||
void torrentSet( const QSet<int>& ids, const QString& key, double val );
|
||||
void torrentSet( const QSet<int>& ids, const QString& key, const QList<int>& val );
|
||||
void torrentSetLocation( const QSet<int>& ids, const QString& path, bool doMove );
|
||||
|
||||
|
||||
public slots:
|
||||
|
|
Loading…
Reference in New Issue