(trunk gtk) #1473: Properties dialog should let you edit/view multiple torrents at once

This commit is contained in:
Charles Kerr 2009-04-24 01:37:04 +00:00
parent 015baf83db
commit 7f60d9db17
14 changed files with 1934 additions and 1221 deletions

1
NEWS
View File

@ -20,6 +20,7 @@ NEWS file for Transmission <http://www.transmissionbt.com/>
+ Speed Limit: Second set of bandwidth limits that can be toggled or scheduled + Speed Limit: Second set of bandwidth limits that can be toggled or scheduled
+ Show the file icon in the list + Show the file icon in the list
+ Allow sorting of the torrent list by size and by ETA + Allow sorting of the torrent list by size and by ETA
+ Properties dialog now lets you edit/view multiple torrents at once
- Qt - Qt
+ New beta Qt client + New beta Qt client
- Daemon - Daemon

View File

@ -98,8 +98,7 @@ removeOldTorrent( struct AddData * data )
{ {
if( data->gtor ) if( data->gtor )
{ {
file_list_set_torrent( data->list, NULL ); file_list_clear( data->list );
tr_torrent_set_remove_flag( data->gtor, TRUE ); tr_torrent_set_remove_flag( data->gtor, TRUE );
g_object_unref( G_OBJECT( data->gtor ) ); g_object_unref( G_OBJECT( data->gtor ) );
data->gtor = NULL; data->gtor = NULL;
@ -143,11 +142,13 @@ addResponseCB( GtkDialog * dialog,
static void static void
updateTorrent( struct AddData * o ) updateTorrent( struct AddData * o )
{ {
if( o->gtor ) if( !o->gtor )
tr_torrentSetDownloadDir( tr_torrent_handle( file_list_clear( o->list );
o->gtor ), o->downloadDir ); else {
tr_torrent * tor = tr_torrent_handle( o->gtor );
file_list_set_torrent( o->list, o->gtor ); tr_torrentSetDownloadDir( tor, o->downloadDir );
file_list_set_torrent( o->list, tr_torrentId( tor ) );
}
} }
/** /**
@ -292,7 +293,7 @@ addSingleTorrentDialog( GtkWindow * parent,
data->ctor = ctor; data->ctor = ctor;
data->filename = g_strdup( tr_ctorGetSourceFile( ctor ) ); data->filename = g_strdup( tr_ctorGetSourceFile( ctor ) );
data->downloadDir = g_strdup( str ); data->downloadDir = g_strdup( str );
data->list = file_list_new( NULL ); data->list = file_list_new( core, 0 );
data->trash_check = data->trash_check =
gtk_check_button_new_with_mnemonic( _( "_Move source file to Trash" ) ); gtk_check_button_new_with_mnemonic( _( "_Move source file to Trash" ) );
data->run_check = data->run_check =

File diff suppressed because it is too large Load Diff

View File

@ -18,7 +18,9 @@
#include "tr-torrent.h" #include "tr-torrent.h"
GtkWidget* torrent_inspector_new( GtkWindow * parent, GtkWidget* torrent_inspector_new( GtkWindow * parent,
TrCore * core, TrCore * core );
TrTorrent * tor );
void torrent_inspector_set_torrents( GtkWidget * widgets,
GSList * torrent_ids );
#endif /* TG_PREFS_H */ #endif /* TG_PREFS_H */

View File

@ -41,8 +41,7 @@ enum
SUB_STATE_HIGH = ( 1 << 0 ), SUB_STATE_HIGH = ( 1 << 0 ),
SUB_STATE_NORMAL = ( 1 << 1 ), SUB_STATE_NORMAL = ( 1 << 1 ),
SUB_STATE_LOW = ( 1 << 2 ), SUB_STATE_LOW = ( 1 << 2 ),
SUB_STATE_PRIORITY_MASK = SUB_STATE_PRIORITY_MASK = ( SUB_STATE_HIGH | SUB_STATE_NORMAL | SUB_STATE_LOW ),
( SUB_STATE_HIGH | SUB_STATE_NORMAL | SUB_STATE_LOW ),
SUB_STATE_DOWNLOAD = ( 1 << 4 ), SUB_STATE_DOWNLOAD = ( 1 << 4 ),
SUB_STATE_IGNORE = ( 1 << 5 ), SUB_STATE_IGNORE = ( 1 << 5 ),
SUB_STATE_DOWNLOAD_MASK = ( SUB_STATE_DOWNLOAD | SUB_STATE_IGNORE ) SUB_STATE_DOWNLOAD_MASK = ( SUB_STATE_DOWNLOAD | SUB_STATE_IGNORE )
@ -68,7 +67,9 @@ enum
typedef struct typedef struct
{ {
TrTorrent * gtor; int torrentId;
TrCore * core;
tr_torrent * tor;
GtkWidget * top; GtkWidget * top;
GtkWidget * view; GtkWidget * view;
GtkTreeModel * model; /* same object as store, but recast */ GtkTreeModel * model; /* same object as store, but recast */
@ -81,10 +82,9 @@ FileData;
static void static void
clearData( FileData * data ) clearData( FileData * data )
{ {
data->gtor = NULL; data->torrentId = -1;
if( data->timeout_tag ) if( data->timeout_tag ) {
{
g_source_remove( data->timeout_tag ); g_source_remove( data->timeout_tag );
data->timeout_tag = 0; data->timeout_tag = 0;
} }
@ -199,21 +199,22 @@ done:
***/ ***/
static gboolean static gboolean
refreshFilesForeach( GtkTreeModel * model, refreshFilesForeach( GtkTreeModel * model,
GtkTreePath * path UNUSED, GtkTreePath * path UNUSED,
GtkTreeIter * iter, GtkTreeIter * iter,
gpointer gdata ) gpointer gdata )
{ {
FileData * data = gdata; FileData * data = gdata;
gboolean is_file; gboolean is_file;
unsigned int index; unsigned int index;
gtk_tree_model_get( model, iter, FC_IS_FILE, &is_file, FC_INDEX, &index, gtk_tree_model_get( model, iter, FC_IS_FILE, &is_file,
-1 ); FC_INDEX, &index,
-1 );
if( is_file ) if( is_file )
{ {
GtkTreeStore * store = GTK_TREE_STORE( model ); GtkTreeStore * store = GTK_TREE_STORE( model );
tr_torrent * tor = tr_torrent_handle( data->gtor ); tr_torrent * tor = data->tor;
int download = tr_torrentGetFileDL( tor, index ); int download = tr_torrentGetFileDL( tor, index );
int priority = tr_torrentGetFilePriority( tor, index ); int priority = tr_torrentGetFilePriority( tor, index );
uint64_t have = data->refresh_file_stat[index].bytesCompleted; uint64_t have = data->refresh_file_stat[index].bytesCompleted;
@ -299,17 +300,29 @@ addSubForeach( GtkTreeModel * model,
static void static void
refresh( FileData * data ) refresh( FileData * data )
{ {
tr_file_index_t fileCount; tr_torrent * tor = tr_torrentFindFromId( tr_core_session( data->core ), data->torrentId );
tr_torrent * tor = tr_torrent_handle( data->gtor );
data->refresh_file_stat = tr_torrentFiles( tor, &fileCount ); if( tor == NULL )
{
file_list_clear( data->top );
}
else
{
tr_file_index_t fileCount;
gtk_tree_model_foreach( data->model, refreshFilesForeach, data ); /* initialize the temporary variables */
gtk_tree_model_foreach( data->model, resetSubForeach, data ); data->tor = tr_torrentFindFromId( tr_core_session( data->core ), data->torrentId );
gtk_tree_model_foreach( data->model, addSubForeach, data ); data->refresh_file_stat = tr_torrentFiles( tor, &fileCount );
tr_torrentFilesFree( data->refresh_file_stat, fileCount ); gtk_tree_model_foreach( data->model, refreshFilesForeach, data );
data->refresh_file_stat = NULL; gtk_tree_model_foreach( data->model, resetSubForeach, data );
gtk_tree_model_foreach( data->model, addSubForeach, data );
/* clean up the temporary variables */
data->refresh_file_stat = NULL;
tr_torrentFilesFree( data->refresh_file_stat, fileCount );
data->tor = NULL;
}
} }
static gboolean static gboolean
@ -442,13 +455,16 @@ getActiveFilesForPath( GtkTreeView * view,
***/ ***/
void void
file_list_set_torrent( GtkWidget * w, file_list_clear( GtkWidget * w )
TrTorrent * gtor ) {
file_list_set_torrent( w, -1 );
}
void
file_list_set_torrent( GtkWidget * w, int torrentId )
{ {
GtkTreeStore * store; GtkTreeStore * store;
FileData * data; FileData * data = g_object_get_data( G_OBJECT( w ), "file-data" );
data = g_object_get_data( G_OBJECT( w ), "file-data" );
/* unset the old fields */ /* unset the old fields */
clearData( data ); clearData( data );
@ -468,34 +484,34 @@ file_list_set_torrent( GtkWidget * w,
G_TYPE_UINT64, /* sub size */ G_TYPE_UINT64, /* sub size */
G_TYPE_UINT64, /* sub have */ G_TYPE_UINT64, /* sub have */
G_TYPE_INT ); /* sub state */ G_TYPE_INT ); /* sub state */
data->store = store; data->store = store;
data->model = GTK_TREE_MODEL( store ); data->model = GTK_TREE_MODEL( store );
data->gtor = gtor; data->torrentId = torrentId;
/* populate the model */ /* populate the model */
if( gtor ) if( torrentId > 0 )
{ {
tr_file_index_t i; tr_session * session = tr_core_session( data->core );
const tr_info * inf = tr_torrent_info( gtor ); tr_torrent * tor = tr_torrentFindFromId( session, torrentId );
tr_torrent * tor = tr_torrent_handle( gtor ); if( tor != NULL )
for( i = 0; inf && i < inf->fileCount; ++i )
{ {
const char * path = inf->files[i].name; tr_file_index_t i;
const char * base = const tr_info * inf = tr_torrentInfo( tor );
g_path_is_absolute( path ) ? g_path_skip_root( path ) :
path; for( i=0; i<inf->fileCount; ++i )
parsepath( w, tor, store, NULL, base, i, inf->files[i].length ); {
const char * path = inf->files[i].name;
const char * base = g_path_is_absolute( path ) ? g_path_skip_root( path ) : path;
parsepath( w, tor, store, NULL, base, i, inf->files[i].length );
}
} }
refresh( data ); refresh( data );
data->timeout_tag = gtr_timeout_add_seconds( 2, refreshModel, data ); data->timeout_tag = gtr_timeout_add_seconds( 2, refreshModel, data );
} }
gtk_tree_view_set_model( GTK_TREE_VIEW( data->view ), gtk_tree_view_set_model( GTK_TREE_VIEW( data->view ), GTK_TREE_MODEL( store ) );
GTK_TREE_MODEL( store ) );
gtk_tree_view_expand_all( GTK_TREE_VIEW( data->view ) ); gtk_tree_view_expand_all( GTK_TREE_VIEW( data->view ) );
} }
@ -656,6 +672,10 @@ onViewButtonPressed( GtkWidget * w,
{ {
FileData * data = gdata; FileData * data = gdata;
gboolean handled = FALSE; gboolean handled = FALSE;
tr_torrent * tor = tr_torrentFindFromId( tr_core_session( data->core ), data->torrentId );
if( tor == NULL )
return handled;
if( ( event->type == GDK_BUTTON_PRESS ) && ( event->button == 1 ) if( ( event->type == GDK_BUTTON_PRESS ) && ( event->button == 1 )
&& !( event->state & ( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) ) && !( event->state & ( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) )
@ -720,8 +740,7 @@ onViewButtonPressed( GtkWidget * w,
} }
/* apply that new state to the active files */ /* apply that new state to the active files */
tr_torrentSetFilePriorities( tr_torrent_handle( data-> tr_torrentSetFilePriorities( tor,
gtor ),
(tr_file_index_t*)a->data, (tr_file_index_t*)a->data,
(tr_file_index_t)a->len, (tr_file_index_t)a->len,
priority ); priority );
@ -744,7 +763,7 @@ onViewButtonPressed( GtkWidget * w,
enabled = ( sub_state & SUB_STATE_IGNORE ) ? 1 : 0; enabled = ( sub_state & SUB_STATE_IGNORE ) ? 1 : 0;
/* apply that new state to the active files */ /* apply that new state to the active files */
tr_torrentSetFileDLs( tr_torrent_handle( data->gtor ), tr_torrentSetFileDLs( tor,
(tr_file_index_t*)a->data, (tr_file_index_t*)a->data,
(tr_file_index_t)a->len, (tr_file_index_t)a->len,
enabled ); enabled );
@ -769,7 +788,7 @@ onViewButtonPressed( GtkWidget * w,
} }
GtkWidget * GtkWidget *
file_list_new( TrTorrent * gtor ) file_list_new( TrCore * core, int torrentId )
{ {
GtkWidget * ret; GtkWidget * ret;
GtkWidget * view, * scroll; GtkWidget * view, * scroll;
@ -778,6 +797,8 @@ file_list_new( TrTorrent * gtor )
GtkTreeSelection * sel; GtkTreeSelection * sel;
FileData * data = g_new0( FileData, 1 ); FileData * data = g_new0( FileData, 1 );
data->core = core;
/* create the view */ /* create the view */
view = gtk_tree_view_new( ); view = gtk_tree_view_new( );
gtk_tree_view_set_rules_hint( GTK_TREE_VIEW( view ), TRUE ); gtk_tree_view_set_rules_hint( GTK_TREE_VIEW( view ), TRUE );
@ -845,7 +866,7 @@ file_list_new( TrTorrent * gtor )
data->view = view; data->view = view;
data->top = scroll; data->top = scroll;
g_object_set_data_full( G_OBJECT( ret ), "file-data", data, freeData ); g_object_set_data_full( G_OBJECT( ret ), "file-data", data, freeData );
file_list_set_torrent( ret, gtor ); file_list_set_torrent( ret, torrentId );
return ret; return ret;
} }

View File

@ -26,12 +26,12 @@
#define GTK_TORRENT_FILE_LIST_H #define GTK_TORRENT_FILE_LIST_H
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include "tr-torrent.h" #include "tr-core.h"
/* create a new file list */ GtkWidget * file_list_new( TrCore *, int torrentId );
GtkWidget * file_list_new( TrTorrent * );
void file_list_set_torrent( GtkWidget *, void file_list_clear( GtkWidget * );
TrTorrent * );
void file_list_set_torrent( GtkWidget *, int torrentId );
#endif #endif

View File

@ -97,19 +97,18 @@ static const char * LICENSE =
struct cbdata struct cbdata
{ {
unsigned int isIconified : 1; gboolean isIconified;
unsigned int isClosing : 1; gboolean isClosing;
guint timer; guint timer;
gpointer icon; gpointer icon;
GtkWindow * wind; GtkWindow * wind;
TrCore * core; TrCore * core;
GtkWidget * msgwin; GtkWidget * msgwin;
GtkWidget * prefs; GtkWidget * prefs;
GSList * errqueue; GSList * errqueue;
GSList * dupqueue; GSList * dupqueue;
GHashTable * tor2details; GtkTreeSelection * sel;
GHashTable * details2tor; GtkWidget * details;
GtkTreeSelection * sel;
}; };
#define CBDATA_PTR "callback-data-pointer" #define CBDATA_PTR "callback-data-pointer"
@ -189,7 +188,7 @@ accumulateCanUpdateForeach( GtkTreeModel * model,
} }
static void static void
refreshTorrentActions( struct cbdata * data ) refreshActions( struct cbdata * data )
{ {
int canUpdate; int canUpdate;
struct counts_data counts; struct counts_data counts;
@ -205,7 +204,7 @@ refreshTorrentActions( struct cbdata * data )
action_sensitize( "delete-torrent", counts.totalCount != 0 ); action_sensitize( "delete-torrent", counts.totalCount != 0 );
action_sensitize( "verify-torrent", counts.totalCount != 0 ); action_sensitize( "verify-torrent", counts.totalCount != 0 );
action_sensitize( "open-torrent-folder", counts.totalCount == 1 ); action_sensitize( "open-torrent-folder", counts.totalCount == 1 );
action_sensitize( "show-torrent-properties", counts.totalCount == 1 ); action_sensitize( "show-torrent-properties", counts.totalCount != 0 );
canUpdate = 0; canUpdate = 0;
gtk_tree_selection_selected_foreach( s, accumulateCanUpdateForeach, &canUpdate ); gtk_tree_selection_selected_foreach( s, accumulateCanUpdateForeach, &canUpdate );
@ -228,10 +227,42 @@ refreshTorrentActions( struct cbdata * data )
} }
} }
static void
refreshDetailsDialog( struct cbdata * data )
{
GtkTreeSelection * s = tr_window_get_selection( data->wind );
GtkTreeModel * model;
GSList * ids = NULL;
GList * selrows = NULL;
GList * l;
if( data->details == NULL )
return;
/* build a list of the selected torrents' ids */
s = tr_window_get_selection( data->wind );
for( selrows=l=gtk_tree_selection_get_selected_rows(s,&model); l; l=l->next ) {
GtkTreeIter iter;
if( gtk_tree_model_get_iter( model, &iter, l->data ) ) {
tr_torrent * tor;
gtk_tree_model_get( model, &iter, MC_TORRENT_RAW, &tor, -1 );
ids = g_slist_append( ids, GINT_TO_POINTER( tr_torrentId( tor ) ) );
}
}
torrent_inspector_set_torrents( data->details, ids );
/* cleanup */
g_slist_free( ids );
g_list_foreach( selrows, (GFunc)gtk_tree_path_free, NULL );
g_list_free( selrows );
}
static void static void
selectionChangedCB( GtkTreeSelection * s UNUSED, gpointer data ) selectionChangedCB( GtkTreeSelection * s UNUSED, gpointer data )
{ {
refreshTorrentActions( data ); refreshActions( data );
refreshDetailsDialog( data );
} }
static void static void
@ -355,8 +386,6 @@ main( int argc,
}; };
cbdata = g_new0( struct cbdata, 1 ); cbdata = g_new0( struct cbdata, 1 );
cbdata->tor2details = g_hash_table_new( g_str_hash, g_str_equal );
cbdata->details2tor = g_hash_table_new( g_direct_hash, g_direct_equal );
/* bind the gettext domain */ /* bind the gettext domain */
setlocale( LC_ALL, "" ); setlocale( LC_ALL, "" );
@ -583,7 +612,7 @@ rowChangedCB( GtkTreeModel * model UNUSED,
{ {
struct cbdata * data = gdata; struct cbdata * data = gdata;
if( gtk_tree_selection_path_is_selected ( data->sel, path ) ) if( gtk_tree_selection_path_is_selected ( data->sel, path ) )
refreshTorrentActions( gdata ); refreshActions( gdata );
} }
static void static void
@ -602,42 +631,15 @@ winsetup( struct cbdata * cbdata,
model = tr_core_model( cbdata->core ); model = tr_core_model( cbdata->core );
g_signal_connect( model, "row-changed", G_CALLBACK( rowChangedCB ), cbdata ); g_signal_connect( model, "row-changed", G_CALLBACK( rowChangedCB ), cbdata );
g_signal_connect( wind, "delete-event", G_CALLBACK( winclose ), cbdata ); g_signal_connect( wind, "delete-event", G_CALLBACK( winclose ), cbdata );
refreshTorrentActions( cbdata ); refreshActions( cbdata );
setupdrag( GTK_WIDGET( wind ), cbdata ); setupdrag( GTK_WIDGET( wind ), cbdata );
} }
static gpointer static gpointer
quitThreadFunc( gpointer gdata ) quitThreadFunc( gpointer core )
{ {
struct cbdata * cbdata = gdata; tr_core_close( core );
tr_core_close( cbdata->core );
/* shutdown the gui */
if( cbdata->prefs )
gtk_widget_destroy( GTK_WIDGET( cbdata->prefs ) );
if( cbdata->wind )
gtk_widget_destroy( GTK_WIDGET( cbdata->wind ) );
g_object_unref( cbdata->core );
if( cbdata->icon )
g_object_unref( cbdata->icon );
if( cbdata->errqueue )
{
g_slist_foreach( cbdata->errqueue, (GFunc)g_free, NULL );
g_slist_free( cbdata->errqueue );
}
if( cbdata->dupqueue )
{
g_slist_foreach( cbdata->dupqueue, (GFunc)g_free, NULL );
g_slist_free( cbdata->dupqueue );
}
g_hash_table_destroy( cbdata->details2tor );
g_hash_table_destroy( cbdata->tor2details );
g_free( cbdata );
/* exit the gtk main loop */
gtk_main_quit( ); gtk_main_quit( );
return NULL; return NULL;
} }
@ -652,8 +654,9 @@ do_exit_cb( GtkWidget *w UNUSED,
static void static void
wannaquit( void * vdata ) wannaquit( void * vdata )
{ {
GtkWidget * r, * p, * b, * w, *c; TrCore * core;
struct cbdata * cbdata = vdata; GtkWidget *r, *p, *b, *w, *c;
struct cbdata *cbdata = vdata;
/* stop the update timer */ /* stop the update timer */
if( cbdata->timer ) if( cbdata->timer )
@ -688,16 +691,36 @@ wannaquit( void * vdata )
w = gtr_button_new_from_stock( GTK_STOCK_QUIT, _( "_Quit Now" ) ); w = gtr_button_new_from_stock( GTK_STOCK_QUIT, _( "_Quit Now" ) );
g_signal_connect( w, "clicked", G_CALLBACK( do_exit_cb ), NULL ); g_signal_connect( w, "clicked", G_CALLBACK( do_exit_cb ), NULL );
gtk_container_add( GTK_CONTAINER( b ), w ); gtk_container_add( GTK_CONTAINER( b ), w );
gtk_table_attach( GTK_TABLE( gtk_table_attach( GTK_TABLE( p ), b, 1, 2, 2, 3, GTK_FILL, GTK_FILL, 0, 10 );
p ), b, 1, 2, 2, 3, GTK_FILL, GTK_FILL, 0, 10 );
gtk_widget_show_all( r ); gtk_widget_show_all( r );
/* clear the UI */ /* clear the UI */
gtk_list_store_clear( GTK_LIST_STORE( tr_core_model( cbdata->core ) ) ); gtk_list_store_clear( GTK_LIST_STORE( tr_core_model( cbdata->core ) ) );
/* shutdown the gui */
core = cbdata->core;
if( cbdata->details )
gtk_widget_destroy( GTK_WIDGET( cbdata->details ) );
if( cbdata->prefs )
gtk_widget_destroy( GTK_WIDGET( cbdata->prefs ) );
if( cbdata->wind )
gtk_widget_destroy( GTK_WIDGET( cbdata->wind ) );
g_object_unref( cbdata->core );
if( cbdata->icon )
g_object_unref( cbdata->icon );
if( cbdata->errqueue ) {
g_slist_foreach( cbdata->errqueue, (GFunc)g_free, NULL );
g_slist_free( cbdata->errqueue );
}
if( cbdata->dupqueue ) {
g_slist_foreach( cbdata->dupqueue, (GFunc)g_free, NULL );
g_slist_free( cbdata->dupqueue );
}
g_free( cbdata );
/* shut down libT */ /* shut down libT */
g_thread_create( quitThreadFunc, vdata, TRUE, NULL ); g_thread_create( quitThreadFunc, core, TRUE, NULL );
} }
static void static void
@ -1091,7 +1114,7 @@ updatemodel( gpointer gdata )
tr_window_update( data->wind ); tr_window_update( data->wind );
/* update the actions */ /* update the actions */
refreshTorrentActions( data ); refreshActions( data );
} }
return !done; return !done;
@ -1181,18 +1204,6 @@ updateTrackerForeach( GtkTreeModel * model,
tr_torrentManualUpdate( tor ); tr_torrentManualUpdate( tor );
} }
static void
detailsClosed( gpointer user_data,
GObject * details )
{
struct cbdata * data = user_data;
gpointer hashString = g_hash_table_lookup( data->details2tor,
details );
g_hash_table_remove( data->details2tor, details );
g_hash_table_remove( data->tor2details, hashString );
}
static void static void
openFolderForeach( GtkTreeModel * model, openFolderForeach( GtkTreeModel * model,
GtkTreePath * path UNUSED, GtkTreePath * path UNUSED,
@ -1206,34 +1217,6 @@ openFolderForeach( GtkTreeModel * model,
g_object_unref( G_OBJECT( gtor ) ); g_object_unref( G_OBJECT( gtor ) );
} }
static void
showInfoForeach( GtkTreeModel * model,
GtkTreePath * path UNUSED,
GtkTreeIter * iter,
gpointer user_data )
{
const char * hashString;
struct cbdata * data = user_data;
TrTorrent * tor = NULL;
GtkWidget * w;
gtk_tree_model_get( model, iter, MC_TORRENT, &tor, -1 );
hashString = tr_torrent_info( tor )->hashString;
w = g_hash_table_lookup( data->tor2details, hashString );
if( w != NULL )
gtk_window_present( GTK_WINDOW( w ) );
else
{
w = torrent_inspector_new( GTK_WINDOW( data->wind ), data->core, tor );
gtk_widget_show( w );
g_hash_table_insert( data->tor2details, (gpointer)hashString, w );
g_hash_table_insert( data->details2tor, w, (gpointer)hashString );
g_object_weak_ref( G_OBJECT( w ), detailsClosed, data );
}
g_object_unref( G_OBJECT( tor ) );
}
static void static void
recheckTorrentForeach( GtkTreeModel * model, recheckTorrentForeach( GtkTreeModel * model,
GtkTreePath * path UNUSED, GtkTreePath * path UNUSED,
@ -1348,8 +1331,10 @@ doAction( const char * action_name, gpointer user_data )
} }
else if( !strcmp( action_name, "show-torrent-properties" ) ) else if( !strcmp( action_name, "show-torrent-properties" ) )
{ {
GtkTreeSelection * s = tr_window_get_selection( data->wind ); if( data->details == NULL )
gtk_tree_selection_selected_foreach( s, showInfoForeach, data ); data->details = torrent_inspector_new( GTK_WINDOW( data->wind ), data->core );
refreshDetailsDialog( data );
gtk_widget_show( data->details );
} }
else if( !strcmp( action_name, "update-tracker" ) ) else if( !strcmp( action_name, "update-tracker" ) )
{ {
@ -1435,4 +1420,3 @@ doAction( const char * action_name, gpointer user_data )
if( changed ) if( changed )
updatemodel( data ); updatemodel( data );
} }

View File

@ -426,7 +426,7 @@ make_meta_ui( GtkWindow * parent,
hig_workarea_add_section_divider( t, &row ); hig_workarea_add_section_divider( t, &row );
hig_workarea_add_section_title( t, &row, _( "Trackers" ) ); hig_workarea_add_section_title( t, &row, _( "Trackers" ) );
w = tracker_list_new( NULL ); w = tracker_list_new( session, -1, TRUE );
n = pref_int_get( ANNOUNCE_KEY"-count" ); n = pref_int_get( ANNOUNCE_KEY"-count" );
if( n > 0 ) if( n > 0 )

View File

@ -1428,3 +1428,22 @@ tr_core_blocklist_update( TrCore * core )
g_snprintf( buf, sizeof( buf ), "{ \"method\": \"blocklist-update\", \"tag\": %d }", tag ); g_snprintf( buf, sizeof( buf ), "{ \"method\": \"blocklist-update\", \"tag\": %d }", tag );
sendRequest( core, buf, tag, blocklistResponseFunc, NULL ); sendRequest( core, buf, tag, blocklistResponseFunc, NULL );
} }
/***
****
***/
void
tr_core_exec_json( TrCore * core, const char * json )
{
g_message( "executing %s", json );
sendRequest( core, json, 0, NULL, NULL );
}
void
tr_core_exec( TrCore * core, const tr_benc * top )
{
char * json = tr_bencToJSON( top );
tr_core_exec_json( core, json );
tr_free( json );
}

View File

@ -189,6 +189,11 @@ void tr_core_port_test( TrCore * core );
void tr_core_blocklist_update( TrCore * core ); void tr_core_blocklist_update( TrCore * core );
void tr_core_exec( TrCore * core, const tr_benc * benc );
void tr_core_exec_json( TrCore * core, const char * json );
/** /**
*** ***
**/ **/

View File

@ -32,7 +32,10 @@
struct tracker_page struct tracker_page
{ {
TrTorrent * gtor; gboolean isNew;
tr_session * session;
int torrentId;
GtkTreeView * view; GtkTreeView * view;
GtkListStore * store; GtkListStore * store;
@ -60,6 +63,11 @@ enum
TR_N_COLS TR_N_COLS
}; };
static tr_torrent * getTorrent( struct tracker_page * page )
{
return tr_torrentFindFromId( page->session, page->torrentId );
}
static void static void
setTrackerChangeState( struct tracker_page * page, setTrackerChangeState( struct tracker_page * page,
gboolean changed ) gboolean changed )
@ -105,14 +113,13 @@ onTrackerSelectionChanged( GtkTreeSelection * sel,
const gboolean has_selection = const gboolean has_selection =
gtk_tree_selection_count_selected_rows( sel ) > 0; gtk_tree_selection_count_selected_rows( sel ) > 0;
GtkTreeModel * model = GTK_TREE_MODEL( page->store ); GtkTreeModel * model = GTK_TREE_MODEL( page->store );
const int trackerCount = gtk_tree_model_iter_n_children( model, NULL ); const int trackerCount = model ? gtk_tree_model_iter_n_children( model, NULL ) : 0;
const gboolean ok_to_remove = !page->gtor || ( trackerCount > 1 ); const gboolean ok_to_remove = page->isNew || ( trackerCount > 1 );
gtk_widget_set_sensitive( page->remove_button, has_selection && ok_to_remove ); gtk_widget_set_sensitive( page->remove_button, has_selection && ok_to_remove );
} }
static void static void
onTrackerRemoveClicked( GtkButton * w UNUSED, onTrackerRemoveClicked( GtkButton * w UNUSED, gpointer gpage )
gpointer gpage )
{ {
struct tracker_page * page = gpage; struct tracker_page * page = gpage;
GtkTreeModel * model; GtkTreeModel * model;
@ -143,8 +150,7 @@ onTrackerRemoveClicked( GtkButton * w UNUSED,
} }
static void static void
onTrackerAddClicked( GtkButton * w UNUSED, onTrackerAddClicked( GtkButton * w UNUSED, gpointer gpage )
gpointer gpage )
{ {
GtkTreeIter iter; GtkTreeIter iter;
struct tracker_page * page = gpage; struct tracker_page * page = gpage;
@ -193,9 +199,7 @@ onTrackerSaveClicked( GtkButton * w UNUSED,
g_assert( i == n ); g_assert( i == n );
/* set the tracker list */ /* set the tracker list */
tr_torrentSetAnnounceList( tr_torrent_handle( page->gtor ), tr_torrentSetAnnounceList( getTorrent( page ), trackers, n );
trackers, n );
setTrackerChangeState( page, FALSE ); setTrackerChangeState( page, FALSE );
@ -211,12 +215,13 @@ onTrackerRevertClicked( GtkButton * w UNUSED,
gpointer gpage ) gpointer gpage )
{ {
struct tracker_page * page = gpage; struct tracker_page * page = gpage;
GtkTreeModel * model = GtkTreeModel * model;
tracker_model_new( tr_torrent_handle( page->gtor ) );
model = tracker_model_new( getTorrent( page ) );
gtk_tree_view_set_model( page->view, model ); gtk_tree_view_set_model( page->view, model );
page->store = GTK_LIST_STORE( model ); page->store = GTK_LIST_STORE( model );
g_object_unref( G_OBJECT( model ) ); g_object_unref( G_OBJECT( model ) );
setTrackerChangeState( page, FALSE ); setTrackerChangeState( page, FALSE );
} }
@ -291,71 +296,74 @@ onTierEdited( GtkCellRendererText * renderer UNUSED,
gtk_tree_path_free( path ); gtk_tree_path_free( path );
} }
void
tracker_list_set_torrent( GtkWidget * w, int torrentId )
{
struct tracker_page * page = g_object_get_data( G_OBJECT( w ), PAGE_KEY );
GtkTreeModel * m;
m = tracker_model_new( tr_torrentFindFromId( page->session, torrentId ) );
page->torrentId = torrentId;
page->store = GTK_LIST_STORE( m );
gtk_tree_view_set_model( GTK_TREE_VIEW( page->view ), m );
g_object_unref( m );
}
void
tracker_list_clear( GtkWidget * w )
{
tracker_list_set_torrent( w, -1 );
}
GtkWidget* GtkWidget*
tracker_list_new( TrTorrent * gtor ) tracker_list_new( tr_session * session, int torrentId, gboolean isNew )
{ {
GtkWidget * w; GtkWidget * w;
GtkWidget * buttons; GtkWidget * buttons;
GtkWidget * box; GtkWidget * box;
GtkWidget * top; GtkWidget * top;
GtkWidget * fr; GtkWidget * fr;
GtkTreeModel * m;
GtkCellRenderer * r; GtkCellRenderer * r;
GtkTreeViewColumn * c; GtkTreeViewColumn * c;
GtkTreeSelection * sel; GtkTreeSelection * sel;
struct tracker_page * page = g_new0( struct tracker_page, 1 ); struct tracker_page * page = g_new0( struct tracker_page, 1 );
page->gtor = gtor; page->session = session;
top = gtk_hbox_new( FALSE, GUI_PAD ); top = gtk_hbox_new( FALSE, GUI_PAD );
box = gtk_vbox_new( FALSE, GUI_PAD ); box = gtk_vbox_new( FALSE, GUI_PAD );
buttons = gtk_vbox_new( TRUE, GUI_PAD ); buttons = gtk_vbox_new( TRUE, GUI_PAD );
m = tracker_model_new( tr_torrent_handle( gtor ) ); //m = tracker_model_new( tr_torrent_handle( gtor ) );
page->store = GTK_LIST_STORE( m ); //page->store = GTK_LIST_STORE( m );
w = gtk_tree_view_new_with_model( m ); w = gtk_tree_view_new( );
g_signal_connect( w, "button-release-event", g_signal_connect( w, "button-release-event",
G_CALLBACK( on_tree_view_button_released ), NULL ); G_CALLBACK( on_tree_view_button_released ), NULL );
page->view = GTK_TREE_VIEW( w ); page->view = GTK_TREE_VIEW( w );
gtk_tree_view_set_enable_search( page->view, FALSE ); gtk_tree_view_set_enable_search( page->view, FALSE );
r = gtk_cell_renderer_text_new( ); r = gtk_cell_renderer_text_new( );
g_object_set( G_OBJECT( r ), g_object_set( G_OBJECT( r ), "editable", TRUE, NULL );
"editable", TRUE, g_signal_connect( r, "edited", G_CALLBACK( onTierEdited ), page );
NULL ); c = gtk_tree_view_column_new_with_attributes( _( "Tier" ), r, "text", TR_COL_TIER, NULL );
g_signal_connect( r, "edited",
G_CALLBACK( onTierEdited ), page );
c = gtk_tree_view_column_new_with_attributes( _( "Tier" ), r,
"text", TR_COL_TIER,
NULL );
gtk_tree_view_column_set_sort_column_id( c, TR_COL_TIER ); gtk_tree_view_column_set_sort_column_id( c, TR_COL_TIER );
gtk_tree_view_append_column( page->view, c ); gtk_tree_view_append_column( page->view, c );
r = gtk_cell_renderer_text_new( ); r = gtk_cell_renderer_text_new( );
g_object_set( G_OBJECT( r ), g_object_set( G_OBJECT( r ), "editable", TRUE, "ellipsize", PANGO_ELLIPSIZE_END, NULL );
"editable", TRUE, g_signal_connect( r, "edited", G_CALLBACK( onAnnounceEdited ), page );
"ellipsize", PANGO_ELLIPSIZE_END, c = gtk_tree_view_column_new_with_attributes( _( "Announce URL" ), r, "text", TR_COL_ANNOUNCE, NULL );
NULL );
g_signal_connect( r, "edited",
G_CALLBACK( onAnnounceEdited ), page );
c = gtk_tree_view_column_new_with_attributes( _( "Announce URL" ), r,
"text", TR_COL_ANNOUNCE,
NULL );
gtk_tree_view_column_set_sort_column_id( c, TR_COL_ANNOUNCE ); gtk_tree_view_column_set_sort_column_id( c, TR_COL_ANNOUNCE );
gtk_tree_view_append_column( page->view, c ); gtk_tree_view_append_column( page->view, c );
w = gtk_scrolled_window_new( NULL, NULL ); w = gtk_scrolled_window_new( NULL, NULL );
gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( w ), gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( w ), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC );
GTK_POLICY_NEVER,
GTK_POLICY_AUTOMATIC );
sel = gtk_tree_view_get_selection( page->view ); sel = gtk_tree_view_get_selection( page->view );
page->sel = sel; page->sel = sel;
g_signal_connect( sel, "changed", g_signal_connect( sel, "changed", G_CALLBACK( onTrackerSelectionChanged ), page );
G_CALLBACK( onTrackerSelectionChanged ), page );
gtk_tree_selection_set_mode( sel, GTK_SELECTION_MULTIPLE ); gtk_tree_selection_set_mode( sel, GTK_SELECTION_MULTIPLE );
gtk_container_add( GTK_CONTAINER( w ), GTK_WIDGET( page->view ) ); gtk_container_add( GTK_CONTAINER( w ), GTK_WIDGET( page->view ) );
gtk_widget_set_size_request( w, -1, 133 ); gtk_widget_set_size_request( w, -1, 133 );
fr = gtk_frame_new( NULL ); fr = gtk_frame_new( NULL );
gtk_frame_set_shadow_type( GTK_FRAME( fr ), GTK_SHADOW_IN ); gtk_frame_set_shadow_type( GTK_FRAME( fr ), GTK_SHADOW_IN );
gtk_container_add( GTK_CONTAINER( fr ), w ); gtk_container_add( GTK_CONTAINER( fr ), w );
g_object_unref( G_OBJECT( m ) );
w = gtk_button_new_from_stock( GTK_STOCK_ADD ); w = gtk_button_new_from_stock( GTK_STOCK_ADD );
g_signal_connect( w, "clicked", G_CALLBACK( onTrackerAddClicked ), page ); g_signal_connect( w, "clicked", G_CALLBACK( onTrackerAddClicked ), page );
@ -366,7 +374,8 @@ tracker_list_new( TrTorrent * gtor )
onTrackerRemoveClicked ), page ); onTrackerRemoveClicked ), page );
gtk_box_pack_start( GTK_BOX( buttons ), w, TRUE, TRUE, 0 ); gtk_box_pack_start( GTK_BOX( buttons ), w, TRUE, TRUE, 0 );
page->remove_button = w; page->remove_button = w;
if( gtor )
if( !isNew )
{ {
w = gtk_button_new_from_stock( GTK_STOCK_SAVE ); w = gtk_button_new_from_stock( GTK_STOCK_SAVE );
g_signal_connect( w, "clicked", G_CALLBACK( g_signal_connect( w, "clicked", G_CALLBACK(
@ -390,6 +399,7 @@ tracker_list_new( TrTorrent * gtor )
onTrackerSelectionChanged( sel, page ); onTrackerSelectionChanged( sel, page );
g_object_set_data_full( G_OBJECT( top ), PAGE_KEY, page, g_free ); g_object_set_data_full( G_OBJECT( top ), PAGE_KEY, page, g_free );
tracker_list_set_torrent( top, torrentId );
return top; return top;
} }

View File

@ -2,9 +2,13 @@
#define TRACKER_LIST_H #define TRACKER_LIST_H
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include "tr-torrent.h" #include <libtransmission/transmission.h>
GtkWidget* tracker_list_new( TrTorrent * gtor ); void tracker_list_set_torrent( GtkWidget*, int torrentId );
void tracker_list_clear( GtkWidget* );
GtkWidget* tracker_list_new( tr_session*, int torrentId, gboolean isNew );
/** /**
* @return an array of tr_tracker_info's. It's the caller's responsibility * @return an array of tr_tracker_info's. It's the caller's responsibility
@ -17,4 +21,5 @@ void tracker_list_add_trackers( GtkWidget * list,
const tr_tracker_info * trackers, const tr_tracker_info * trackers,
int trackerCount ); int trackerCount );
#endif #endif

View File

@ -59,9 +59,6 @@ void tr_torrentSetHasPiece( tr_torrent * tor,
void tr_torrentChangeMyPort( tr_torrent * session ); void tr_torrentChangeMyPort( tr_torrent * session );
tr_torrent* tr_torrentFindFromId( tr_session * session,
int id );
tr_torrent* tr_torrentFindFromHash( tr_session * session, tr_torrent* tr_torrentFindFromHash( tr_session * session,
const uint8_t * hash ); const uint8_t * hash );

View File

@ -953,6 +953,9 @@ uint64_t tr_torrentGetBytesLeftToAllocate( const tr_torrent * torrent );
*/ */
int tr_torrentId( const tr_torrent * torrent ); int tr_torrentId( const tr_torrent * torrent );
tr_torrent* tr_torrentFindFromId( tr_session * session, int id );
/*** /***
**** Torrent speed limits **** Torrent speed limits
**** ****