string | value type & description
---------------------------------+-------------------------------------------------
"ids" | array the torrent torrent list, as described in 3.1
- | (though, this function doesn't make sense for >1 torrent)
+ | (must only be 1 torrent)
"path" | string the path to the file or folder that will be renamed
"name" | string the file or folder's new name
- Response arguments: none
+ Response arguments: "path", "name", and "id", holding the torrent ID integer
4. Session Requests
return NULL;
}
-static void
-torrentRenamePathDone (tr_torrent * tor UNUSED,
- const char * oldpath UNUSED,
- const char * newname UNUSED,
- int error,
- void * user_data)
-{
- *(int*)user_data = error;
-}
-
-static const char *
-torrentRenamePath (tr_session * session,
- tr_variant * args_in,
- tr_variant * args_out UNUSED,
- struct tr_rpc_idle_data * idle_data UNUSED)
-{
- const char * oldpath;
- const char * newname;
- const char * ret = NULL;
-
- if (!tr_variantDictFindStr (args_in, TR_KEY_path, &oldpath, NULL))
- {
- ret = "no path specified";
- }
- else if (!tr_variantDictFindStr (args_in, TR_KEY_name, &newname, NULL))
- {
- ret = "no name specified";
- }
- else
- {
- int torrentCount;
- tr_torrent ** torrents = getTorrents (session, args_in, &torrentCount);
-
- if (torrentCount != 1)
- {
- ret = "torent-rename-path requires 1 torrent";
- }
- else
- {
- int error = -1;
- tr_torrentRenamePath (torrents[0], oldpath, newname, torrentRenamePathDone, &error);
- assert (error != -1);
-
- if (error != 0)
- ret = tr_strerror (error);
- }
-
- tr_free (torrents);
- }
-
- return ret;
-}
-
static const char*
torrentReannounce (tr_session * session,
tr_variant * args_in,
}
static void
-addFiles (const tr_torrent * tor,
- tr_variant * list)
+addFiles (const tr_torrent * tor, tr_variant * list)
{
tr_file_index_t i;
tr_file_index_t n;
****
***/
+static void
+torrentRenamePathDone (tr_torrent * tor,
+ const char * oldpath,
+ const char * newname,
+ int error,
+ void * user_data)
+{
+ const char * result;
+ struct tr_rpc_idle_data * data = user_data;
+
+ tr_variantDictAddInt (data->args_out, TR_KEY_id, tr_torrentId(tor));
+ tr_variantDictAddStr (data->args_out, TR_KEY_path, oldpath);
+ tr_variantDictAddStr (data->args_out, TR_KEY_name, newname);
+
+ if (error == 0)
+ result = NULL;
+ else
+ result = tr_strerror (error);
+
+ tr_idle_function_done (data, result);
+}
+
+static const char*
+torrentRenamePath (tr_session * session,
+ tr_variant * args_in,
+ tr_variant * args_out UNUSED,
+ struct tr_rpc_idle_data * idle_data)
+{
+ int torrentCount;
+ tr_torrent ** torrents;
+ const char * oldpath = NULL;
+ const char * newname = NULL;
+
+ tr_variantDictFindStr (args_in, TR_KEY_path, &oldpath, NULL);
+ tr_variantDictFindStr (args_in, TR_KEY_name, &newname, NULL);
+ torrents = getTorrents (session, args_in, &torrentCount);
+
+ if (torrentCount == 1)
+ tr_torrentRenamePath (torrents[0], oldpath, newname, torrentRenamePathDone, idle_data);
+ else
+ tr_idle_function_done (idle_data, "torrent-rename-path requires 1 torrent");
+
+ /* cleanup */
+ tr_free (torrents);
+ return NULL; /* ignored */
+}
+
+/***
+****
+***/
+
static void
portTested (tr_session * session UNUSED,
bool did_connect UNUSED,
static const char*
portTest (tr_session * session,
- tr_variant * args_in UNUSED,
- tr_variant * args_out UNUSED,
+ tr_variant * args_in UNUSED,
+ tr_variant * args_out UNUSED,
struct tr_rpc_idle_data * idle_data)
{
const int port = tr_sessionGetPeerPort (session);
static const char*
torrentAdd (tr_session * session,
- tr_variant * args_in,
- tr_variant * args_out UNUSED,
+ tr_variant * args_in,
+ tr_variant * args_out UNUSED,
struct tr_rpc_idle_data * idle_data)
{
const char * filename = NULL;
{ "torrent-add", false, torrentAdd },
{ "torrent-get", true, torrentGet },
{ "torrent-remove", true, torrentRemove },
- { "torrent-rename-path", true, torrentRenamePath },
+ { "torrent-rename-path", false, torrentRenamePath },
{ "torrent-set", true, torrentSet },
{ "torrent-set-location", true, torrentSetLocation },
{ "torrent-start", true, torrentStart },
tr_variantDictAddInt (&response, TR_KEY_tag, tag);
buf = tr_variantToBuf (&response, TR_VARIANT_FMT_JSON_LEAN);
- (*callback)(session, buf, callback_user_data);
+ (*callback)(session, buf, callback_user_data);
evbuffer_free (buf);
tr_variantFree (&response);
data->args_out = tr_variantDictAddDict (data->response, TR_KEY_arguments, 0);
data->callback = callback;
data->callback_user_data = callback_user_data;
- (*methods[i].func)(session, args_in, data->args_out, data);
+ (*methods[i].func)(session, args_in, data->args_out, data);
}
}
****
***/
+ tor->anyDate = tr_time ();
+
/* callback */
if (data->callback != NULL)
(*data->callback)(tor, data->oldpath, data->newname, error, data->callback_user_data);
// stop listening to the old torrents
foreach( int id, myIds ) {
const Torrent * tor = myModel.getTorrentFromId( id );
- if( tor )
+ if( tor ) {
disconnect( tor, SIGNAL(torrentChanged(int)), this, SLOT(onTorrentChanged()) );
+ disconnect( tor, SIGNAL(torrentFileListRebuilt(int)), this, SLOT(onTorrentFileListRebuilt()) );
+ }
}
myFileTreeView->clear( );
// listen to the new torrents
foreach( int id, myIds ) {
const Torrent * tor = myModel.getTorrentFromId( id );
- if( tor )
+ if( tor ) {
connect( tor, SIGNAL(torrentChanged(int)), this, SLOT(onTorrentChanged()) );
+ connect( tor, SIGNAL(torrentFileListRebuilt(int)), this, SLOT(onTorrentFileListRebuilt()) );
+ }
}
foreach( QWidget * w, myWidgets )
}
}
+void
+Details :: onTorrentFileListRebuilt( )
+{
+ myFilesDirty = true;
+ onTorrentChanged( );
+}
+
namespace
{
void setIfIdle( QComboBox * box, int i )
}
myPeers = peers2;
+ if( !single || myFilesDirty )
+ myFileTreeView->clear( );
if( single )
myFileTreeView->update( torrents[0]->files( ) , myChangedTorrents );
- else
- myFileTreeView->clear( );
+ myFilesDirty = false;
myChangedTorrents = false;
myHavePendingRefresh = false;
connect( myFileTreeView, SIGNAL( wantedChanged(const QSet<int>&, bool)),
this, SLOT( onFileWantedChanged(const QSet<int>&, bool)));
+ connect( myFileTreeView, SIGNAL( pathEdited(const QString&, const QString&)),
+ this, SLOT (onPathEdited(const QString&, const QString&)));
+
return myFileTreeView;
}
mySession.torrentSet (myIds, key, indices.toList());
getNewData ();
}
+
+void
+Details :: onPathEdited (const QString& oldpath, const QString& newname)
+{
+ mySession.torrentRenamePath (myIds, oldpath, newname);
+}
private slots:
void onTorrentChanged( );
+ void onTorrentFileListRebuilt( );
void onTimer( );
public:
FileTreeView * myFileTreeView;
+ bool myFilesDirty;
+
private slots:
void refreshPref( int key );
void onBandwidthPriorityChanged( int );
void onFilePriorityChanged( const QSet<int>& fileIndices, int );
void onFileWantedChanged( const QSet<int>& fileIndices, bool );
+ void onPathEdited (const QString& oldpath, const QString& newname);
void onHonorsSessionLimitsToggled( bool );
void onDownloadLimitedToggled( bool );
void onSpinBoxEditingFinished( );
{
int i( Qt::ItemIsSelectable | Qt::ItemIsEnabled );
+ if( index.column( ) == COL_NAME )
+ i |= Qt::ItemIsEditable;
+
if( index.column( ) == COL_WANTED )
i |= Qt::ItemIsUserCheckable | Qt::ItemIsTristate;
return (Qt::ItemFlags)i;
}
+bool
+FileTreeModel :: setData (const QModelIndex& index, const QVariant& newname, int role)
+{
+ if (role == Qt::EditRole)
+ {
+ QString oldpath;
+ QModelIndex walk = index;
+ FileTreeItem * item = static_cast<FileTreeItem*>(index.internalPointer());
+
+ while (item && !item->name().isEmpty())
+ {
+ if (oldpath.isEmpty())
+ oldpath = item->name();
+ else
+ oldpath = item->name() + "/" + oldpath;
+ item = item->parent ();
+ }
+
+ emit pathEdited (oldpath, newname.toString());
+ }
+
+ return false; // don't update the view until the session confirms the change
+}
+
QVariant
FileTreeModel :: headerData( int column, Qt::Orientation orientation, int role ) const
{
connect( &myModel, SIGNAL(wantedChanged(const QSet<int>&, bool)),
this, SIGNAL(wantedChanged(const QSet<int>&, bool)));
+
+ connect( &myModel, SIGNAL(pathEdited(const QString&, const QString&)),
+ this, SIGNAL(pathEdited(const QString&, const QString&)));
}
FileTreeView :: ~FileTreeView( )
QModelIndex parent( const QModelIndex& child, int column ) const;
int rowCount( const QModelIndex& parent = QModelIndex( ) ) const;
int columnCount( const QModelIndex &parent = QModelIndex( ) ) const;
+ virtual bool setData ( const QModelIndex & index, const QVariant & value, int role = Qt::EditRole );
signals:
void priorityChanged( const QSet<int>& fileIndices, int );
void wantedChanged( const QSet<int>& fileIndices, bool );
+ void pathEdited (const QString& oldpath, const QString& newname);
public:
void clear( );
signals:
void priorityChanged( const QSet<int>& fileIndices, int );
void wantedChanged( const QSet<int>& fileIndices, bool );
+ void pathEdited (const QString& oldpath, const QString& newname);
protected:
bool eventFilter( QObject *, QEvent * );
#include <QStringList>
#include <QStyle>
#include <QTextStream>
+#include <QTimer>
#include <curl/curl.h>
TAG_ADD_TORRENT,
TAG_PORT_TEST,
TAG_MAGNET_LINK,
+ TAG_RENAME_PATH,
FIRST_UNIQUE_TAG
};
myPrefs( prefs ),
mySession( 0 ),
myConfigDir( QString::fromUtf8( configDir ) ),
- myNAM( 0 )
+ myNAM( 0 ),
+ myResponseTimer (this)
{
myStats.ratio = TR_RATIO_NA;
myStats.uploadedBytes = 0;
myCumulativeStats = myStats;
connect( &myPrefs, SIGNAL(changed(int)), this, SLOT(updatePref(int)) );
+
+ connect (&myResponseTimer, SIGNAL(timeout()), this, SLOT(onResponseTimer()));
+ myResponseTimer.setSingleShot (true);
}
Session :: ~Session( )
tr_variantFree (&top);
}
+void
+Session :: torrentRenamePath (const QSet<int>& ids, const QString& oldpath, const QString& newname)
+{
+ tr_variant top;
+ tr_variantInitDict (&top, 2);
+ tr_variantDictAddStr (&top, TR_KEY_method, "torrent-rename-path");
+ tr_variantDictAddInt (&top, TR_KEY_tag, TAG_RENAME_PATH);
+ tr_variant * args (tr_variantDictAddDict(&top, TR_KEY_arguments, 3));
+ addOptionalIds (args, ids);
+ tr_variantDictAddStr (args, TR_KEY_path, oldpath.toUtf8().constData());
+ tr_variantDictAddStr (args, TR_KEY_name, newname.toUtf8().constData());
+ exec (&top);
+ tr_variantFree (&top);
+}
+
void
Session :: refreshTorrents( const QSet<int>& ids )
{
}
void
-Session :: localSessionCallback( tr_session * session, struct evbuffer * json, void * self )
+Session :: localSessionCallback( tr_session * session, struct evbuffer * json, void * vself )
{
- Q_UNUSED( session );
+ Q_UNUSED (session);
+
+ Session * self = static_cast<Session*>(vself);
- ((Session*)self)->parseResponse( (const char*) evbuffer_pullup( json, -1 ), evbuffer_get_length( json ) );
+ self->myIdleJSON.append (QString ((const char*) evbuffer_pullup (json, -1)));
+
+ if (!self->myResponseTimer.isActive())
+ self->myResponseTimer.start(50);
}
#define REQUEST_DATA_PROPERTY_KEY "requestData"
reply->deleteLater();
}
+void
+Session :: onResponseTimer ()
+{
+ QStringList responses = myIdleJSON;
+ myIdleJSON.clear();
+
+ foreach (QString response, responses)
+ {
+ const QByteArray utf8 (response.toUtf8());
+ parseResponse (utf8.constData(), utf8.length());
+ }
+}
+
void
Session :: parseResponse( const char * json, size_t jsonLength )
{
break;
}
+ case TAG_RENAME_PATH:
+ {
+ int64_t id = 0;
+ const char * result = 0;
+ if( tr_variantDictFindStr (&top, TR_KEY_result, &result, 0) && strcmp (result, "success") )
+ {
+ const char * path = "";
+ const char * name = "";
+ tr_variantDictFindStr (args, TR_KEY_path, &path, 0);
+ tr_variantDictFindStr (args, TR_KEY_name, &name, 0);
+ const QString title = tr("Error Renaming Path");
+ const QString text = tr("<p><b>Unable to rename \"%1\" as \"%2\": %3.</b></p> <p>Please correct the errors and try again.</p>").arg(path).arg(name).arg(result);
+ QMessageBox * d = new QMessageBox( QMessageBox::Information, title, text,
+ QMessageBox::Close,
+ QApplication::activeWindow());
+ connect( d, SIGNAL(rejected()), d, SLOT(deleteLater()) );
+ d->show( );
+ }
+ else if (tr_variantDictFindInt (args, TR_KEY_id, &id) && id)
+ {
+ // let's get the updated file list
+ char * req = tr_strdup_printf ("{ \"arguments\": { \"fields\": [ \"files\", \"id\" ], \"ids\": %d }, \"method\": \"torrent-get\", \"tag\": %d }",
+ int(id),
+ int(TAG_SOME_TORRENTS));
+ exec (req);
+ tr_free (req);
+ }
+
+ break;
+ }
+
case TAG_PORT_TEST: {
bool isOpen = 0;
if( tr_variantDictFindDict( &top, TR_KEY_arguments, &args ) )
tr_variantDictFindBool( args, TR_KEY_port_is_open, &isOpen );
emit portTested( (bool)isOpen );
+ break;
}
case TAG_MAGNET_LINK: {
#include <QFileInfoList>
#include <QNetworkAccessManager>
#include <QString>
+#include <QStringList>
+#include <QTimer>
#include <QUrl>
class QStringList;
void torrentSet( const QSet<int>& ids, const tr_quark key, const QStringList& val );
void torrentSet( const QSet<int>& ids, const tr_quark key, const QPair<int,QString>& val);
void torrentSetLocation( const QSet<int>& ids, const QString& path, bool doMove );
-
+ void torrentRenamePath( const QSet<int>& ids, const QString& oldpath, const QString& newname );
public slots:
void pauseTorrents( const QSet<int>& torrentIds = QSet<int>() );
private slots:
void onFinished( QNetworkReply * reply );
+ void onResponseTimer( );
signals:
void executed( int64_t tag, const QString& result, struct tr_variant * arguments );
tr_session * mySession;
QString myConfigDir;
QString mySessionId;
+ QStringList myIdleJSON;
QUrl myUrl;
QNetworkAccessManager * myNAM;
struct tr_session_stats myStats;
struct tr_session_stats myCumulativeStats;
QString mySessionVersion;
+ QTimer myResponseTimer;
};
#endif
updateMimeIcon ();
changed = true;
+ emit torrentFileListRebuilt (id ());
}
if (tr_variantDictFindList (d, TR_KEY_fileStats, &files))
signals:
void torrentChanged( int id );
void torrentCompleted( int id );
+ void torrentFileListRebuilt( int id );
private: