--- transmission-1.06.orig/gtk/open-dialog.c +++ transmission-1.06/gtk/open-dialog.c @@ -0,0 +1,240 @@ +/* + * This file Copyright (C) 2008 Charles Kerr + * + * 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: hig.h 4404 2008-01-01 17:20:20Z charles $ + */ + +#include +#include +#include +#include "file-list.h" +#include "hig.h" +#include "open-dialog.h" + +struct OpenData +{ + TrCore * core; + GtkWidget * list; + GtkToggleButton * run_check; + GtkToggleButton * delete_check; + char * filename; + char * destination; + TrTorrent * gtor; + tr_ctor * ctor; +}; + +static void +deleteOldTorrent( struct OpenData * data ) +{ + if( data->gtor ) + { + tr_torrent * tor; + + file_list_set_torrent( data->list, NULL ); + + tor = tr_torrent_handle( data->gtor ); + tr_torrentRemoveSaved( tor ); + + g_object_unref( G_OBJECT( data->gtor ) ); + data->gtor = NULL; + } +} + +static void +openResponseCB( GtkDialog * dialog, gint response, gpointer gdata ) +{ + struct OpenData * data = gdata; + + if( data->gtor ) + { + if( response != GTK_RESPONSE_ACCEPT ) + deleteOldTorrent( data ); + else { + 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 ); + } + } + + tr_ctorFree( data->ctor ); + g_free( data->filename ); + g_free( data->destination ); + g_free( data ); + gtk_widget_destroy( GTK_WIDGET( dialog ) ); +} + +static void +updateTorrent( struct OpenData * o ) +{ + if( o->gtor ) + tr_torrentSetFolder( tr_torrent_handle( o->gtor ), o->destination ); + + file_list_set_torrent( o->list, o->gtor ); +} + +static void +sourceChanged( GtkFileChooserButton * b, gpointer gdata ) +{ + struct OpenData * data = gdata; + + deleteOldTorrent( data ); + + g_free( data->filename ); + data->filename = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER( b ) ); + + /* maybe instantiate a torrent */ + if( data->filename ) { + int err = 0; + tr_torrent * torrent; + tr_handle * handle = tr_core_handle( data->core ); + tr_ctorSetMetainfoFromFile( data->ctor, data->filename ); + tr_ctorSetDestination( data->ctor, TR_FORCE, data->destination ); + tr_ctorSetPaused( data->ctor, TR_FORCE, TRUE ); + tr_ctorSetDeleteSource( data->ctor, FALSE ); + if(( torrent = tr_torrentNew( handle, data->ctor, &err ))) + data->gtor = tr_torrent_new_preexisting( torrent ); + } + + updateTorrent( data ); +} + +static void +verifyRequested( GtkButton * button UNUSED, gpointer gdata ) +{ + struct OpenData * data = gdata; + if( data->gtor ) + tr_torrentRecheck( tr_torrent_handle( data->gtor ) ); +} + +static void +destinationChanged( GtkFileChooserButton * b, gpointer gdata ) +{ + struct OpenData * data = gdata; + + g_free( data->destination ); + data->destination = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER( b ) ); + + updateTorrent( data ); + verifyRequested( NULL, data ); +} + +/**** +***** +****/ + +void +makeaddwind( GtkWindow * parent, + TrCore * core, + tr_ctor * ctor ) +{ + int row; + int col; + const char * str; + GtkWidget * w; + GtkWidget * d; + GtkWidget * t; + GtkWidget * l; + GtkFileFilter * filter; + struct OpenData * data; + uint8_t flag; + + /* make the dialog */ + d = gtk_dialog_new_with_buttons( _( "Open Torrent" ), parent, + GTK_DIALOG_DESTROY_WITH_PARENT|GTK_DIALOG_NO_SEPARATOR, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + NULL ); + + if( tr_ctorGetDestination( ctor, TR_FORCE, &str ) ) + g_assert_not_reached( ); + data = g_new0( struct OpenData, 1 ); + data->core = core; + data->ctor = ctor; + data->filename = g_strdup( tr_ctorGetSourceFile( ctor ) ); + data->destination = g_strdup( str ); + g_signal_connect( G_OBJECT( d ), "response", + G_CALLBACK( openResponseCB ), data ); + + t = gtk_table_new( 6, 2, FALSE ); + gtk_container_set_border_width( GTK_CONTAINER( t ), GUI_PAD_BIG ); + gtk_table_set_row_spacings( GTK_TABLE( t ), GUI_PAD ); + gtk_table_set_col_spacings( GTK_TABLE( t ), GUI_PAD_BIG ); + + row = col = 0; + l = gtk_label_new_with_mnemonic( _( "_Torrent file:" ) ); + gtk_misc_set_alignment( GTK_MISC( l ), 0.0f, 0.5f ); + gtk_table_attach( GTK_TABLE( t ), l, col, col+1, row, row+1, GTK_FILL, 0, 0, 0 ); + ++col; + w = gtk_file_chooser_button_new( _( "Select Torrent" ), + GTK_FILE_CHOOSER_ACTION_OPEN ); + gtk_table_attach( GTK_TABLE( t ), w, col, col+1, row, row+1, ~0, 0, 0, 0 ); + gtk_label_set_mnemonic_widget( GTK_LABEL( l ), w ); + filter = gtk_file_filter_new( ); + gtk_file_filter_set_name( filter, _( "Torrent files" ) ); + gtk_file_filter_add_pattern( filter, "*.torrent" ); + gtk_file_chooser_add_filter( GTK_FILE_CHOOSER( w ), filter ); + filter = gtk_file_filter_new( ); + gtk_file_filter_set_name( filter, _( "All files" ) ); + gtk_file_filter_add_pattern( filter, "*" ); + gtk_file_chooser_add_filter( GTK_FILE_CHOOSER( w ), filter ); + g_signal_connect( w, "selection-changed", + G_CALLBACK( sourceChanged ), data ); + if( data->filename ) + if( !gtk_file_chooser_set_filename( GTK_FILE_CHOOSER( w ), data->filename ) ) + g_warning( "couldn't select '%s'", data->filename ); + + ++row; + col = 0; + l = gtk_label_new_with_mnemonic( _( "Download _to:" ) ); + gtk_misc_set_alignment( GTK_MISC( l ), 0.0f, 0.5f ); + gtk_table_attach( GTK_TABLE( t ), l, col, col+1, row, row+1, GTK_FILL, 0, 0, 0 ); + ++col; + w = gtk_file_chooser_button_new( _( "Destination" ), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ); + if( !gtk_file_chooser_set_filename( GTK_FILE_CHOOSER( w ), data->destination ) ) + g_warning( "couldn't select '%s'", data->destination ); + gtk_table_attach( GTK_TABLE( t ), w, col, col+1, row, row+1, ~0, 0, 0, 0 ); + gtk_label_set_mnemonic_widget( GTK_LABEL( l ), w ); + g_signal_connect( w, "selection-changed", G_CALLBACK( destinationChanged ), data ); + + ++row; + col = 0; + w = file_list_new( NULL ); + gtk_widget_set_size_request ( w, 466u, 300u ); + data->list = w; + gtk_table_attach_defaults( GTK_TABLE( t ), w, col, col+2, row, row+1 ); + + ++row; + col = 0; + w = gtk_button_new_with_mnemonic( _( "Verify Local Data" ) ); + gtk_table_attach( GTK_TABLE( t ), w, col, col+1, row, row+1, GTK_FILL, 0, 0, 0 ); + g_signal_connect( w, "clicked", G_CALLBACK( verifyRequested ), data ); + + ++row; + col = 0; + w = gtk_check_button_new_with_mnemonic( _( "_Delete original torrent file" ) ); + data->delete_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 ); + + ++row; + col = 0; + w = gtk_check_button_new_with_mnemonic( _( "_Start when added" ) ); + data->run_check = GTK_TOGGLE_BUTTON( w ); + if( tr_ctorGetPaused( ctor, TR_FORCE, &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 ); + + gtk_box_pack_start_defaults( GTK_BOX( GTK_DIALOG( d )->vbox ), t ); + gtk_widget_show_all( d ); +} --- transmission-1.06.orig/gtk/tr_core.c +++ transmission-1.06/gtk/tr_core.c @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: tr_core.c 4977 2008-02-08 03:00:14Z charles $ + * $Id$ * * Copyright (c) 2007-2008 Transmission authors and contributors * @@ -22,15 +22,13 @@ * DEALINGS IN THE SOFTWARE. *****************************************************************************/ -#include -#include -#include +#include /* strcmp, strlen */ #include #include #include -#include +#include /* tr_strcmp */ #include "conf.h" #include "tr_core.h" @@ -38,10 +36,18 @@ #include "tr_torrent.h" #include "util.h" +struct TrCorePrivate +{ + GtkTreeModel * model; + tr_handle * handle; + int nextid; + struct core_stats stats; +}; + static void -tr_core_marshal_err( GClosure * closure, GValue * ret SHUTUP, guint count, - const GValue * vals, gpointer hint SHUTUP, - gpointer marshal ) +tr_core_marshal_err( GClosure * closure, GValue * ret UNUSED, + guint count, const GValue * vals, + gpointer hint UNUSED, gpointer marshal ) { typedef void (*TRMarshalErr) ( gpointer, enum tr_core_err, const char *, gpointer ); @@ -51,97 +57,75 @@ const char * errstr; gpointer inst, gdata; - g_return_if_fail( 3 == count ); + g_return_if_fail( count == 3 ); inst = g_value_peek_pointer( vals ); errcode = g_value_get_int( vals + 1 ); errstr = g_value_get_string( vals + 2 ); gdata = closure->data; - callback = (TRMarshalErr) ( NULL == marshal ? - cclosure->callback : marshal ); + callback = (TRMarshalErr)( marshal ? marshal : cclosure->callback ); callback( inst, errcode, errstr, gdata ); } static void -tr_core_marshal_prompt( GClosure * closure, GValue * ret SHUTUP, guint count, - const GValue * vals, gpointer hint SHUTUP, - gpointer marshal ) +tr_core_marshal_prompt( GClosure * closure, GValue * ret UNUSED, + guint count, const GValue * vals, + gpointer hint UNUSED, gpointer marshal ) { - typedef void (*TRMarshalPrompt) - ( gpointer, GList *, enum tr_torrent_action, gboolean, gpointer ); + typedef void (*TRMarshalPrompt)( gpointer, GList *, gpointer, gpointer ); TRMarshalPrompt callback; GCClosure * cclosure = (GCClosure*) closure; GList * paths; - enum tr_torrent_action action; - gboolean paused; + gpointer ctor; gpointer inst, gdata; - g_return_if_fail( 4 == count ); + g_return_if_fail( count == 3 ); - inst = g_value_peek_pointer( vals ); - paths = g_value_get_pointer( vals + 1 ); - action = g_value_get_int( vals + 2 ); - paused = g_value_get_boolean( vals + 3 ); - gdata = closure->data; + inst = g_value_peek_pointer( vals ); + paths = g_value_peek_pointer( vals + 1 ); + ctor = g_value_peek_pointer( vals + 2 ); + gdata = closure->data; - callback = (TRMarshalPrompt) ( NULL == marshal ? - cclosure->callback : marshal ); - callback( inst, paths, action, paused, gdata ); + callback = (TRMarshalPrompt)( marshal ? marshal : cclosure->callback ); + callback( inst, paths, ctor, gdata ); } -static void -tr_core_marshal_data( GClosure * closure, GValue * ret SHUTUP, guint count, - const GValue * vals, gpointer hint SHUTUP, - gpointer marshal ) +static int +isDisposed( const TrCore * core ) { - typedef void (*TRMarshalPrompt) - ( gpointer, uint8_t *, size_t, gboolean, gpointer ); - TRMarshalPrompt callback; - GCClosure * cclosure = (GCClosure*) closure; - uint8_t * data; - size_t size; - gboolean paused; - gpointer inst, gdata; - - g_return_if_fail( 4 == count ); - - inst = g_value_peek_pointer( vals ); - data = (uint8_t *) g_value_get_string( vals + 1 ); - size = g_value_get_uint( vals + 2 ); - paused = g_value_get_boolean( vals + 3 ); - gdata = closure->data; - - callback = (TRMarshalPrompt) ( NULL == marshal ? - cclosure->callback : marshal ); - callback( inst, data, size, paused, gdata ); + return !core || !core->priv; } static void tr_core_dispose( GObject * obj ) { - TrCore * self = (TrCore *) obj; - GObjectClass * parent; + TrCore * core = TR_CORE( obj ); - if( self->disposed ) - return; + if( !isDisposed( core ) ) + { + GObjectClass * parent; - self->disposed = TRUE; - pref_save( NULL ); - parent = g_type_class_peek( g_type_parent( TR_CORE_TYPE ) ); - parent->dispose( obj ); -} + pref_save( NULL ); + core->priv = NULL; + parent = g_type_class_peek( g_type_parent( TR_CORE_TYPE ) ); + parent->dispose( obj ); + } +} static void -tr_core_class_init( gpointer g_class, gpointer g_class_data SHUTUP ) +tr_core_class_init( gpointer g_class, gpointer g_class_data UNUSED ) { GObjectClass * gobject_class; TrCoreClass * core_class; + g_type_class_add_private( g_class, sizeof(struct TrCorePrivate) ); + gobject_class = G_OBJECT_CLASS( g_class ); gobject_class->dispose = tr_core_dispose; + core_class = TR_CORE_CLASS( g_class ); core_class->errsig = g_signal_new( "error", G_TYPE_FROM_CLASS( g_class ), G_SIGNAL_RUN_LAST, 0, NULL, NULL, @@ -151,14 +135,7 @@ G_TYPE_FROM_CLASS( g_class ), G_SIGNAL_RUN_LAST, 0, NULL, NULL, tr_core_marshal_prompt, G_TYPE_NONE, - 3, G_TYPE_POINTER, G_TYPE_INT, - G_TYPE_BOOLEAN ); - core_class->promptdatasig = g_signal_new( "directory-prompt-data", - G_TYPE_FROM_CLASS( g_class ), - G_SIGNAL_RUN_LAST, 0, NULL, NULL, - tr_core_marshal_data, - G_TYPE_NONE, 3, G_TYPE_STRING, - G_TYPE_UINT, G_TYPE_BOOLEAN ); + 2, G_TYPE_POINTER, G_TYPE_POINTER ); core_class->quitsig = g_signal_new( "quit", G_TYPE_FROM_CLASS( g_class ), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, @@ -170,11 +147,15 @@ G_TYPE_NONE, 1, G_TYPE_STRING ); } +/*** +**** SORTING +***/ + static int compareDouble( double a, double b ) { if( a < b ) return -1; - if( b < a ) return 1; + if( a > b ) return 1; return 0; } @@ -205,15 +186,6 @@ } static int -compareByDateAdded( GtkTreeModel * model UNUSED, - GtkTreeIter * a UNUSED, - GtkTreeIter * b UNUSED, - gpointer user_data UNUSED ) -{ - return 0; /* FIXME */ -} - -static int compareByName( GtkTreeModel * model, GtkTreeIter * a, GtkTreeIter * b, @@ -281,22 +253,16 @@ tr_torrentInfo(tb)->primaryAddress ); } -/*** -**** -***/ - - static void setSort( TrCore * core, const char * mode, gboolean isReversed ) { int col = MC_TORRENT_RAW; GtkSortType type = isReversed ? GTK_SORT_ASCENDING : GTK_SORT_DESCENDING; - GtkTreeSortable * sortable = GTK_TREE_SORTABLE( core->model ); + GtkTreeModel * model = tr_core_model( core ); + GtkTreeSortable * sortable = GTK_TREE_SORTABLE( model ); if( !strcmp( mode, "sort-by-activity" ) ) gtk_tree_sortable_set_sort_func( sortable, col, compareByActivity, NULL, NULL ); - else if( !strcmp( mode, "sort-by-date-added" ) ) - gtk_tree_sortable_set_sort_func( sortable, col, compareByDateAdded, NULL, NULL ); else if( !strcmp( mode, "sort-by-progress" ) ) gtk_tree_sortable_set_sort_func( sortable, col, compareByProgress, NULL, NULL ); else if( !strcmp( mode, "sort-by-state" ) ) @@ -314,7 +280,8 @@ static void prefsChanged( TrCore * core, const char * key, gpointer data UNUSED ) { - if( !strcmp( key, PREF_KEY_SORT_MODE ) || !strcmp( key, PREF_KEY_SORT_REVERSED ) ) + if( !strcmp( key, PREF_KEY_SORT_MODE ) || + !strcmp( key, PREF_KEY_SORT_REVERSED ) ) { char * mode = pref_string_get( PREF_KEY_SORT_MODE ); gboolean isReversed = pref_flag_get( PREF_KEY_SORT_REVERSED ); @@ -324,16 +291,17 @@ else if( !strcmp( key, PREF_KEY_MAX_PEERS_GLOBAL ) ) { const uint16_t val = pref_int_get( key ); - tr_setGlobalPeerLimit( core->handle, val ); + tr_setGlobalPeerLimit( tr_core_handle( core ), val ); } } static void -tr_core_init( GTypeInstance * instance, gpointer g_class SHUTUP ) +tr_core_init( GTypeInstance * instance, gpointer g_class UNUSED ) { tr_handle * h; TrCore * self = (TrCore *) instance; GtkListStore * store; + struct TrCorePrivate * p; /* column types for the model used to store torrent information */ /* keep this in sync with the enum near the bottom of tr_core.h */ @@ -347,6 +315,11 @@ G_TYPE_INT /* ID for IPC */ }; + p = self->priv = G_TYPE_INSTANCE_GET_PRIVATE( self, + TR_CORE_TYPE, + struct TrCorePrivate ); + + h = tr_initFull( "gtk", pref_flag_get( PREF_KEY_PEX ), pref_flag_get( PREF_KEY_NAT ), @@ -360,17 +333,15 @@ pref_int_get( PREF_KEY_DL_LIMIT ), pref_int_get( PREF_KEY_MAX_PEERS_GLOBAL ), pref_int_get( PREF_KEY_MSGLEVEL ), - TRUE ); + TRUE /* message queueing */ ); /* create the model used to store torrent data */ g_assert( ALEN( types ) == MC_ROW_COUNT ); store = gtk_list_store_newv( MC_ROW_COUNT, types ); - self->model = GTK_TREE_MODEL( store ); - self->handle = h; - self->nextid = 1; - self->quitting = FALSE; - self->disposed = FALSE; + p->model = GTK_TREE_MODEL( store ); + p->handle = h; + p->nextid = 1; } GType @@ -418,19 +389,22 @@ } GtkTreeModel * -tr_core_model( TrCore * self ) +tr_core_model( TrCore * core ) { - g_return_val_if_fail (TR_IS_CORE(self), NULL); - - return self->disposed ? NULL : self->model; + return isDisposed( core ) ? NULL : core->priv->model; } tr_handle * -tr_core_handle( TrCore * self ) +tr_core_handle( TrCore * core ) { - g_return_val_if_fail (TR_IS_CORE(self), NULL); + return isDisposed( core ) ? NULL : core->priv->handle; +} - return self->disposed ? NULL : self->handle; + +const struct core_stats* +tr_core_get_stats( const TrCore * core ) +{ + return isDisposed( core ) ? NULL : &core->priv->stats; } static char* @@ -454,27 +428,32 @@ casefold = g_utf8_casefold( in, end-in ); ret = g_utf8_collate_key( casefold, -1 ); g_free( casefold ); + return ret; } -static void -tr_core_insert( TrCore * self, TrTorrent * tor ) +void +tr_core_add_torrent( TrCore * self, TrTorrent * tor ) { const tr_info * inf = tr_torrent_info( tor ); const tr_stat * torStat = tr_torrent_stat( tor ); char * collated = doCollate( inf->name ); + GtkListStore * store = GTK_LIST_STORE( tr_core_model( self ) ); GtkTreeIter unused; - gtk_list_store_insert_with_values( GTK_LIST_STORE( self->model ), &unused, 0, + + gtk_list_store_insert_with_values( store, &unused, 0, MC_NAME, inf->name, MC_NAME_COLLATED, collated, MC_HASH, inf->hashString, MC_TORRENT, tor, - MC_TORRENT_RAW, tor->handle, + MC_TORRENT_RAW, tr_torrent_handle( tor ), MC_STATUS, torStat->status, - MC_ID, self->nextid, + MC_ID, self->priv->nextid, -1); - self->nextid++; - g_object_unref( tor ); + ++self->priv->nextid; + + /* cleanup */ + g_object_unref( G_OBJECT( tor ) ); g_free( collated ); } @@ -487,19 +466,18 @@ char * path; tr_ctor * ctor; - TR_IS_CORE( self ); - path = getdownloaddir( ); - ctor = tr_ctorNew( self->handle ); + ctor = tr_ctorNew( tr_core_handle( self ) ); if( forcePaused ) tr_ctorSetPaused( ctor, TR_FORCE, TRUE ); tr_ctorSetDestination( ctor, TR_FALLBACK, path ); - tr_ctorSetMaxConnectedPeers( ctor, TR_FALLBACK, pref_int_get( PREF_KEY_MAX_PEERS_PER_TORRENT ) ); + tr_ctorSetMaxConnectedPeers( ctor, TR_FALLBACK, + pref_int_get( PREF_KEY_MAX_PEERS_PER_TORRENT ) ); - torrents = tr_loadTorrents ( self->handle, ctor, &count ); + torrents = tr_loadTorrents ( tr_core_handle( self ), ctor, &count ); for( i=0; ierrsig, 0, type, msg ); } static void -tr_core_errsig( TrCore * self, enum tr_core_err type, const char * msg ) +tr_core_apply_defaults( tr_ctor * ctor ) { - TrCoreClass * class; + if( tr_ctorGetPaused( ctor, TR_FORCE, NULL ) ) + tr_ctorSetPaused( ctor, TR_FORCE, !pref_flag_get( PREF_KEY_START ) ); - class = g_type_class_peek( TR_CORE_TYPE ); - g_signal_emit( self, class->errsig, 0, type, msg ); + if( tr_ctorGetDeleteSource( ctor, NULL ) ) + tr_ctorSetDeleteSource( ctor, + pref_flag_get( PREF_KEY_DELETE_ORIGINAL ) ); + + if( tr_ctorGetMaxConnectedPeers( ctor, TR_FORCE, NULL ) ) + tr_ctorSetMaxConnectedPeers( ctor, TR_FORCE, + pref_int_get( PREF_KEY_MAX_PEERS_PER_TORRENT ) ); + + if( tr_ctorGetDestination( ctor, TR_FORCE, NULL ) ) { + char * path = pref_string_get( PREF_KEY_DIR_DEFAULT ); + tr_ctorSetDestination( ctor, TR_FORCE, path ); + g_free( path ); + } } -gboolean -tr_core_add_dir( TrCore * self, const char * path, const char * dir, - enum tr_torrent_action act, gboolean paused ) +void +tr_core_add_ctor( TrCore * self, tr_ctor * ctor ) { TrTorrent * tor; - char * errstr; + char * errstr = NULL; - TR_IS_CORE( self ); + tr_core_apply_defaults( ctor ); - errstr = NULL; - tor = tr_torrent_new( self->handle, path, dir, act, paused, &errstr ); - if( NULL == tor ) - { + if(( tor = tr_torrent_new_ctor( tr_core_handle( self ), ctor, &errstr ))) + tr_core_add_torrent( self, tor ); + else{ tr_core_errsig( self, TR_CORE_ERR_ADD_TORRENT, errstr ); g_free( errstr ); - return FALSE; } - g_assert( NULL == errstr ); - - tr_core_insert( self, tor ); - - return TRUE; } -int -tr_core_add_list( TrCore * self, GList * paths, enum tr_torrent_action act, - gboolean paused ) +void +tr_core_add_list( TrCore * self, + GList * paths, + tr_ctor * ctor ) { - char * dir; - int count; + tr_core_apply_defaults( ctor ); - TR_IS_CORE( self ); - - if( pref_flag_get( PREF_KEY_DIR_ASK ) ) + if( pref_flag_get( PREF_KEY_OPTIONS_PROMPT ) ) { TrCoreClass * class = g_type_class_peek( TR_CORE_TYPE ); - g_signal_emit( self, class->promptsig, 0, paths, act, paused ); - return 0; + g_signal_emit( self, class->promptsig, 0, paths, ctor ); } + else + { + for( ; paths; paths=paths->next ) + if( !tr_ctorSetMetainfoFromFile( ctor, paths->data ) ) + tr_core_add_ctor( self, ctor ); + tr_ctorFree( ctor ); + } +} - dir = getdownloaddir(); - count = 0; - for( ; paths; paths=paths->next ) - if( tr_core_add_dir( self, paths->data, dir, act, paused ) ) - count++; - - g_free( dir ); - return count; +void +tr_core_torrents_added( TrCore * self ) +{ + tr_core_update( self ); + tr_core_errsig( self, TR_CORE_ERR_NO_MORE_TORRENTS, NULL ); } -gboolean -tr_core_add_data( TrCore * self, uint8_t * data, size_t size, gboolean paused ) +void +tr_core_delete_torrent( TrCore * self, GtkTreeIter * iter ) { - gboolean ret; - char * path; - TR_IS_CORE( self ); + TrTorrent * tor; + GtkTreeModel * model = tr_core_model( self ); - if( pref_flag_get( PREF_KEY_DIR_ASK ) ) - { - TrCoreClass * class = g_type_class_peek( TR_CORE_TYPE ); - g_signal_emit( self, class->promptdatasig, 0, data, size, paused ); - return FALSE; - } + gtk_tree_model_get( model, iter, MC_TORRENT, &tor, -1 ); + gtk_list_store_remove( GTK_LIST_STORE( model ), iter ); + tr_torrentRemoveSaved( tr_torrent_handle( tor ) ); - path = getdownloaddir( ); - ret = tr_core_add_data_dir( self, data, size, path, paused ); - g_free( path ); - return ret; + g_object_unref( G_OBJECT( tor ) ); } -gboolean -tr_core_add_data_dir( TrCore * self, uint8_t * data, size_t size, - const char * dir, gboolean paused ) +static gboolean +findTorrentInModel( TrCore * core, const TrTorrent * gtor, GtkTreeIter * setme ) { - TrTorrent * tor; - char * errstr = NULL; - - TR_IS_CORE( self ); + int match = 0; + GtkTreeIter iter; + GtkTreeModel * model = tr_core_model( core ); - tor = tr_torrent_new_with_data( self->handle, data, size, dir, - paused, &errstr ); - if( NULL == tor ) + if( gtk_tree_model_iter_children( model, &iter, NULL ) ) do { - tr_core_errsig( self, TR_CORE_ERR_ADD_TORRENT, errstr ); - g_free( errstr ); - return FALSE; + TrTorrent * tmp; + gtk_tree_model_get( model, &iter, MC_TORRENT, &tmp, -1 ); + match = tmp == gtor; + g_object_unref( G_OBJECT( tmp ) ); } - g_assert( NULL == errstr ); + while( !match && gtk_tree_model_iter_next( model, &iter ) ); - tr_core_insert( self, tor ); + if( match ) + *setme = iter; - return TRUE; + return match; } void -tr_core_torrents_added( TrCore * self ) +tr_core_remove_torrent( TrCore * self, TrTorrent * gtor, int deleteFiles ) { - TR_IS_CORE( self ); + GtkTreeIter iter; + GtkTreeModel * model = tr_core_model( self ); - tr_core_update( self ); - tr_core_errsig( self, TR_CORE_ERR_NO_MORE_TORRENTS, NULL ); + /* remove from the gui */ + if( findTorrentInModel( self, gtor, &iter ) ) + gtk_list_store_remove( GTK_LIST_STORE( model ), &iter ); + + /* maybe delete the downloaded files */ + if( deleteFiles ) + tr_torrent_delete_files( gtor ); + + /* delete the torrent */ + tr_torrentRemoveSaved( tr_torrent_handle( gtor ) ); + g_object_unref( G_OBJECT( gtor ) ); } -void -tr_core_delete_torrent( TrCore * self, GtkTreeIter * iter ) -{ - TrTorrent * tor; - - TR_IS_CORE( self ); - - gtk_tree_model_get( self->model, iter, MC_TORRENT, &tor, -1 ); - gtk_list_store_remove( GTK_LIST_STORE( self->model ), iter ); - tr_torrentRemoveSaved( tr_torrent_handle( tor ) ); - g_object_unref( G_OBJECT( tor ) ); -} +/*** +**** +***/ static gboolean update_foreach( GtkTreeModel * model, GtkTreePath * path UNUSED, GtkTreeIter * iter, - gpointer data UNUSED) + gpointer data ) { TrTorrent * gtor; int oldStatus; const tr_stat * torStat; + struct core_stats * stats = data; + gtk_tree_model_get( model, iter, MC_TORRENT, >or, MC_STATUS, &oldStatus, -1 ); torStat = tr_torrent_stat( gtor ); + /* sum the torrents' cumulative stats... */ + if( torStat->status == TR_STATUS_DOWNLOAD ) + ++stats->downloadCount; + else if( torStat->status == TR_STATUS_SEED ) + ++stats->seedingCount; + stats->clientDownloadSpeed += torStat->rateDownload; + stats->clientUploadSpeed += torStat->rateUpload; + + /* update the model's status if necessary */ if( oldStatus != (int) torStat->status ) gtk_list_store_set( GTK_LIST_STORE( model ), iter, MC_STATUS, torStat->status, @@ -682,14 +660,16 @@ int column; GtkSortType order; GtkTreeSortable * sortable; + GtkTreeModel * model = tr_core_model( self ); /* pause sorting */ - sortable = GTK_TREE_SORTABLE( self->model ); + sortable = GTK_TREE_SORTABLE( model ); gtk_tree_sortable_get_sort_column_id( sortable, &column, &order ); gtk_tree_sortable_set_sort_column_id( sortable, GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, order ); /* refresh the model */ - gtk_tree_model_foreach( self->model, update_foreach, NULL ); + memset( &self->priv->stats, 0, sizeof( struct core_stats ) ); + gtk_tree_model_foreach( model, update_foreach, &self->priv->stats ); /* resume sorting */ gtk_tree_sortable_set_sort_column_id( sortable, column, order ); @@ -698,11 +678,7 @@ void tr_core_quit( TrCore * self ) { - TrCoreClass * class; - - TR_IS_CORE( self ); - - class = g_type_class_peek( TR_CORE_TYPE ); + TrCoreClass * class = g_type_class_peek( TR_CORE_TYPE ); g_signal_emit( self, class->quitsig, 0 ); } --- transmission-1.06.orig/gtk/actions.c +++ transmission-1.06/gtk/actions.c @@ -7,7 +7,7 @@ * This exemption does not extend to derived works not owned by * the Transmission project. * - * $Id: actions.c 4969 2008-02-07 20:27:15Z charles $ + * $Id$ */ #include @@ -15,7 +15,6 @@ #include #include #include "conf.h" -#include "torrent-inspector.h" #include "tr_core.h" #include "tr_prefs.h" #include "lock.h" @@ -43,22 +42,6 @@ #define GTK_STOCK_SELECT_ALL NULL #endif -static GtkRadioActionEntry priority_toggle_entries[] = -{ - { "priority-high", NULL, N_("_High"), NULL, NULL, TR_PRI_HIGH }, - { "priority-normal", NULL, N_("_Normal"), NULL, NULL, TR_PRI_NORMAL }, - { "priority-low", NULL, N_("_Low"), NULL, NULL, TR_PRI_LOW } -}; - -extern void set_selected_file_priority ( tr_priority_t ); - -static void -priority_changed_cb (GtkAction *action UNUSED, GtkRadioAction *current) -{ - const int priority = gtk_radio_action_get_current_value (current); - set_selected_file_priority ( priority ); -} - static GtkRadioActionEntry sort_radio_entries[] = { { "sort-by-activity", NULL, N_("Sort by _Activity"), NULL, NULL, 0 }, @@ -83,9 +66,9 @@ static GtkToggleActionEntry show_toggle_entries[] = { { "toggle-main-window", NULL, - N_("Show _Main Window"), NULL, NULL, G_CALLBACK(action_cb), TRUE }, + N_("_Main Window"), NULL, NULL, G_CALLBACK(action_cb), TRUE }, { "toggle-message-log", NULL, - N_("Show Message _Log"), NULL, NULL, G_CALLBACK(action_cb), FALSE } + N_("Message _Log"), NULL, NULL, G_CALLBACK(action_cb), FALSE } }; static void @@ -103,11 +86,11 @@ { "sort-reversed", NULL, N_("_Reverse Sort Order"), NULL, NULL, G_CALLBACK(toggle_pref_cb), FALSE }, { "show-filter-bar", NULL, - N_("Show _Filter Bar"), NULL, NULL, G_CALLBACK(toggle_pref_cb), FALSE }, + N_("_Filter Bar"), NULL, NULL, G_CALLBACK(toggle_pref_cb), FALSE }, { "show-status-bar", NULL, - N_("Show _Status Bar"), NULL, NULL, G_CALLBACK(toggle_pref_cb), FALSE }, + N_("_Status Bar"), NULL, NULL, G_CALLBACK(toggle_pref_cb), FALSE }, { "show-toolbar", NULL, - N_("Show _Toolbar"), NULL, NULL, G_CALLBACK(toggle_pref_cb), FALSE } + N_("_Toolbar"), NULL, NULL, G_CALLBACK(toggle_pref_cb), FALSE } }; static GtkActionEntry entries[] = @@ -117,9 +100,12 @@ { "sort-menu", NULL, N_("_Sort Torrents By"), NULL, NULL, NULL }, { "edit-menu", NULL, N_("_Edit"), NULL, NULL, NULL }, { "help-menu", NULL, N_("_Help"), NULL, NULL, NULL }, - { "priority-menu", NULL, N_("_Priority"), NULL, NULL, NULL }, - { "add-torrent", GTK_STOCK_OPEN, - NULL, NULL, N_("Open Torrent"), G_CALLBACK(action_cb) }, + { "open-torrent-toolbar", GTK_STOCK_OPEN, N_("_Open"), NULL, + N_("Open an existing torrent"), + G_CALLBACK(action_cb) }, + { "open-torrent-menu", GTK_STOCK_OPEN, N_("_Open..."), "O", + N_("Open an existing torrent"), + G_CALLBACK(action_cb) }, { "start-torrent", GTK_STOCK_MEDIA_PLAY, N_("_Start"), "S", NULL, G_CALLBACK(action_cb) }, { "show-stats", NULL, N_("_Statistics"), NULL, NULL, G_CALLBACK(action_cb) }, @@ -128,11 +114,13 @@ { "pause-torrent", GTK_STOCK_MEDIA_PAUSE, N_("_Pause"), "P", NULL, G_CALLBACK(action_cb) }, { "remove-torrent", GTK_STOCK_REMOVE, - N_("_Remove"), "Delete", NULL, G_CALLBACK(action_cb) }, - { "create-torrent", GTK_STOCK_NEW, - N_("Create _New Torrent"), NULL, NULL, G_CALLBACK(action_cb) }, - { "close", GTK_STOCK_CLOSE, - N_("_Close"), "W", NULL, G_CALLBACK(action_cb) }, + N_("_Remove"), "Delete", NULL, G_CALLBACK(action_cb) }, + { "new-torrent", GTK_STOCK_NEW, N_("_New..."), NULL, + N_("Create a new torrent"), + G_CALLBACK(action_cb) }, + { "close", GTK_STOCK_CLOSE, N_("_Close"), "W", + N_("Close main window"), + G_CALLBACK(action_cb) }, { "quit", GTK_STOCK_QUIT, N_("_Quit"), "Q", NULL, G_CALLBACK(action_cb) }, { "select-all", GTK_STOCK_SELECT_ALL, @@ -237,12 +225,6 @@ action_group = myGroup = gtk_action_group_new( "Actions" ); gtk_action_group_set_translation_domain( action_group, NULL ); - gtk_action_group_add_radio_actions( action_group, - priority_toggle_entries, - G_N_ELEMENTS(priority_toggle_entries), - TR_PRI_NORMAL, - G_CALLBACK(priority_changed_cb), NULL); - match = pref_string_get( PREF_KEY_SORT_MODE ); for( i=0, n=G_N_ELEMENTS(sort_radio_entries), active=-1; active==-1 && i + * + * 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 +#include +#include +#include +#include + +#include + +#include "actions.h" +#include "details.h" +#include "file-list.h" +#include "tr_torrent.h" +#include "hig.h" +#include "util.h" + +#define UPDATE_INTERVAL_MSEC 2000 + +/**** +***** PIECES VIEW +****/ + +static int +getGridSize (int pieceCount, int * n_rows, int * n_cols) +{ + const int MAX_ACROSS = 16; + if (pieceCount >= (MAX_ACROSS * MAX_ACROSS)) { + *n_rows = *n_cols = MAX_ACROSS; + return MAX_ACROSS * MAX_ACROSS; + } + else { + int i; + for (i=0; (i*i) < pieceCount; ++i); + *n_rows = *n_cols = i; + return pieceCount; + } +} + +#define TO16(a) ((guint16)((a<<8)|(a))) +#define RGB_2_GDK(R,G,B) { 0, TO16(R), TO16(G), TO16(B) } + +enum { DRAW_AVAIL, DRAW_PROG }; + +static void +release_gobject_array (gpointer data) +{ + int i; + GObject **objects = (GObject**) data; + for (i=0; objects[i]!=NULL; ++i) + g_object_unref (G_OBJECT(objects[i])); + g_free (objects); +} + +static gboolean +refresh_pieces( GtkWidget * da, GdkEventExpose * event UNUSED, gpointer gtor ) +{ + tr_torrent * tor = tr_torrent_handle( TR_TORRENT(gtor) ); + const tr_info * info = tr_torrent_info( TR_TORRENT(gtor) ); + int mode = GPOINTER_TO_INT (g_object_get_data (G_OBJECT(da), "draw-mode")); + + GdkColormap * colormap = gtk_widget_get_colormap (da); + const int widget_w = da->allocation.width; + const int widget_h = da->allocation.height; + int n_rows, n_cols; + const int n_cells = getGridSize (info->pieceCount, &n_rows, &n_cols); + const GdkRectangle grid_bounds = { 0, 0, widget_w, widget_h }; + const double piece_w = grid_bounds.width / (double)n_cols; + const double piece_h = grid_bounds.height / (double)n_rows; + const int piece_w_int = (int) (piece_w + 1); /* pad for roundoff */ + const int piece_h_int = (int) (piece_h + 1); /* pad for roundoff */ + const gboolean rtl = gtk_widget_get_direction( da ) == GTK_TEXT_DIR_RTL; + + guint8 * prev_color = NULL; + gboolean first_time = FALSE; + + int i, x, y; + int8_t * pieces = NULL; + float * completeness = NULL; + + /** + *** Get the Graphics Contexts... + **/ + + enum { ALL, LOTS, SOME, FEW, NONE, + BLACK, GRAY, BLINK, + N_COLORS }; + GdkGC **gcs = (GdkGC**) g_object_get_data (G_OBJECT(da), "graphics-contexts"); + if (gcs == NULL) + { + const GdkColor colors [N_COLORS] = { + RGB_2_GDK ( 114, 159, 207 ), /* all */ + RGB_2_GDK ( 52, 101, 164 ), /* lots */ + RGB_2_GDK ( 32, 74, 135 ), /* some */ + RGB_2_GDK ( 85, 87, 83 ), /* few */ + RGB_2_GDK ( 238, 238, 236 ), /* none - tango aluminum highlight */ + RGB_2_GDK ( 46, 52, 54 ), /* black - tango slate shadow */ + RGB_2_GDK ( 186, 189, 182 ), /* gray - tango aluminum shadow */ + RGB_2_GDK ( 252, 233, 79 ), /* blink - tango butter highlight */ + }; + + gcs = g_new (GdkGC*, N_COLORS+1); + + for (i=0; iwindow); + gdk_gc_set_colormap (gcs[i], colormap); + gdk_gc_set_rgb_fg_color (gcs[i], &colors[i]); + gdk_gc_set_rgb_bg_color (gcs[i], &colors[i]); + }; + + gcs[N_COLORS] = NULL; /* a sentinel in the release function */ + g_object_set_data_full (G_OBJECT(da), "graphics-contexts", + gcs, release_gobject_array); + } + + /** + *** Get the cells' previous colors... + *** (this is used for blinking when the color changes) + **/ + + prev_color = (guint8*) g_object_get_data (G_OBJECT(da), "prev-color"); + if (prev_color == NULL) + { + first_time = TRUE; + prev_color = g_new0 (guint8, n_cells); + g_object_set_data_full (G_OBJECT(da), "prev-color", prev_color, g_free); + } + + /** + *** Get the piece data values... + **/ + + switch (mode) { + case DRAW_AVAIL: + pieces = g_new (int8_t, n_cells); + tr_torrentAvailability ( tor, pieces, n_cells ); + break; + case DRAW_PROG: + completeness = g_new (float, n_cells); + tr_torrentAmountFinished ( tor, completeness, n_cells ); + break; + default: g_error("no mode defined!"); + } + + /** + *** Draw... + **/ + + i = 0; + for (y=0; y= 1.00) color = ALL; + else if (val >= 0.66) color = LOTS; + else if (val >= 0.33) color = SOME; + else if (val >= 0.01) color = FEW; + else color = NONE; + } + + /* draw a "blink" for one interval when a piece changes */ + if (first_time) + prev_color[i] = color; + else if (color != prev_color[i]) { + prev_color[i] = color; + color = border = BLINK; + } + } + + gdk_draw_rectangle (da->window, gcs[color], TRUE, + draw_x, draw_y, + piece_w_int, piece_h_int); + + if (i < n_cells) + gdk_draw_rectangle (da->window, gcs[border], FALSE, + draw_x, draw_y, + piece_w_int, piece_h_int); + + ++i; + } + } + + gdk_draw_rectangle (da->window, gcs[GRAY], FALSE, + grid_bounds.x, grid_bounds.y, + grid_bounds.width-1, grid_bounds.height-1); + + g_free (pieces); + g_free (completeness); + return FALSE; +} + +/**** +***** PEERS TAB +****/ + +enum +{ + PEER_COL_ADDRESS, + PEER_COL_CLIENT, + PEER_COL_PROGRESS, + PEER_COL_IS_ENCRYPTED, + PEER_COL_DOWNLOAD_RATE, + PEER_COL_UPLOAD_RATE, + PEER_COL_STATUS, + N_PEER_COLS +}; + +static const char* peer_column_names[N_PEER_COLS] = +{ + N_("Address"), + N_("Client"), + N_("%"), + " ", + N_("Down"), + N_("Up"), + N_("Status") +}; + +static int compare_peers (const void * a, const void * b) +{ + const tr_peer_stat * pa = a; + const tr_peer_stat * pb = b; + return strcmp (pa->addr, pb->addr); +} +static int compare_addr_to_peer (const void * a, const void * b) +{ + const char * addr = (const char *) a; + const tr_peer_stat * peer = b; + return strcmp (addr, peer->addr); +} + +static void +peer_row_set (GtkTreeStore * store, + GtkTreeIter * iter, + const tr_peer_stat * peer) +{ + const char * client = peer->client; + + if (!client || !strcmp(client,"Unknown Client")) + client = " "; + + gtk_tree_store_set (store, iter, + PEER_COL_ADDRESS, peer->addr, + PEER_COL_CLIENT, client, + PEER_COL_IS_ENCRYPTED, peer->isEncrypted, + PEER_COL_PROGRESS, (int)(100.0*peer->progress), + PEER_COL_DOWNLOAD_RATE, peer->downloadFromRate, + PEER_COL_UPLOAD_RATE, peer->uploadToRate, + PEER_COL_STATUS, peer->flagStr, + -1); +} + +static void +append_peers_to_model (GtkTreeStore * store, + const tr_peer_stat * peers, + int n_peers) +{ + int i; + for (i=0; i count ) { + gtk_label_set_text( GTK_LABEL(l), "?" ); + } else { + char str[16]; + g_snprintf( str, sizeof str, "%i", count ); + gtk_label_set_text( GTK_LABEL(l), str ); + } +} + +static void +refresh_peers (GtkWidget * top) +{ + int i; + int n_peers; + GtkTreeIter iter; + PeerData * p = (PeerData*) g_object_get_data (G_OBJECT(top), "peer-data"); + tr_torrent * tor = tr_torrent_handle ( p->gtor ); + GtkTreeModel * model = p->model; + GtkTreeStore * store = p->store; + tr_peer_stat * peers; + const tr_stat * stat = tr_torrent_stat( p->gtor ); + + /** + *** merge the peer diffs into the tree model. + *** + *** this is more complicated than creating a new model, + *** but is also (a) more efficient and (b) doesn't undo + *** the view's visible area and sorting on every refresh. + **/ + + n_peers = 0; + peers = tr_torrentPeers (tor, &n_peers); + qsort (peers, n_peers, sizeof(tr_peer_stat), compare_peers); + + i = 0; + if (gtk_tree_model_get_iter_first (model, &iter)) do + { + char * addr = NULL; + tr_peer_stat * peer = NULL; + gtk_tree_model_get (model, &iter, PEER_COL_ADDRESS, &addr, -1); + peer = bsearch (addr, peers, n_peers, sizeof(tr_peer_stat), + compare_addr_to_peer); + g_free (addr); + + if (peer) /* update a pre-existing row */ + { + const int pos = peer - peers; + const int n_rhs = n_peers - (pos+1); + g_assert (n_rhs >= 0); + + peer_row_set (store, &iter, peer); + + /* remove it from the tr_peer_stat list */ + g_memmove (peer, peer+1, sizeof(tr_peer_stat)*n_rhs); + --n_peers; + } + else if (!gtk_tree_store_remove (store, &iter)) + break; /* we removed the model's last item */ + } + while (gtk_tree_model_iter_next (model, &iter)); + + append_peers_to_model (store, peers, n_peers); /* all these are new */ + + if (GDK_IS_DRAWABLE (p->completeness->window)) + refresh_pieces (p->completeness, NULL, p->gtor); + + fmtpeercount (p->seeders_lb, stat->seeders); + fmtpeercount (p->leechers_lb, stat->leechers); + fmtpeercount (p->completed_lb, stat->completedFromTracker ); + + free( peers ); +} + +static GtkWidget* peer_page_new ( TrTorrent * gtor ) +{ + guint i; + GtkTreeModel *m; + GtkWidget *h, *v, *w, *ret, *da, *sw, *l, *vbox, *hbox; + tr_torrent * tor = tr_torrent_handle (gtor); + PeerData * p = g_new (PeerData, 1); + char name[64]; + + /* TODO: make this configurable? */ + int view_columns[] = { PEER_COL_IS_ENCRYPTED, + PEER_COL_UPLOAD_RATE, + PEER_COL_DOWNLOAD_RATE, + PEER_COL_PROGRESS, + PEER_COL_STATUS, + PEER_COL_ADDRESS, + PEER_COL_CLIENT }; + + m = peer_model_new (tor); + v = gtk_tree_view_new_with_model (m); + gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(v), TRUE); + g_object_unref (G_OBJECT(m)); + + for (i=0; i%s", _("Piece Availability")); + l = gtk_label_new (NULL); + gtk_misc_set_alignment (GTK_MISC(l), 0.0f, 0.5f); + gtk_label_set_markup (GTK_LABEL(l), name); + gtk_box_pack_start (GTK_BOX(vbox), l, FALSE, FALSE, 0); + + w = da = p->completeness = gtk_drawing_area_new (); + gtk_widget_set_size_request (w, 0u, 100u); + g_object_set_data (G_OBJECT(w), "draw-mode", GINT_TO_POINTER(DRAW_AVAIL)); + g_signal_connect (w, "expose-event", G_CALLBACK(refresh_pieces), gtor); + + h = gtk_hbox_new (FALSE, GUI_PAD); + w = gtk_alignment_new (0.0f, 0.0f, 0.0f, 0.0f); + gtk_widget_set_size_request (w, GUI_PAD_BIG, 0); + gtk_box_pack_start (GTK_BOX(h), w, FALSE, FALSE, 0); + gtk_box_pack_start_defaults (GTK_BOX(h), da); + gtk_box_pack_start (GTK_BOX(vbox), h, FALSE, FALSE, 0); + + /* a small vertical spacer */ + w = gtk_alignment_new (0.0f, 0.0f, 0.0f, 0.0f); + gtk_widget_set_size_request (w, 0u, GUI_PAD); + gtk_box_pack_start (GTK_BOX(vbox), w, FALSE, FALSE, 0); + + g_snprintf (name, sizeof(name), "%s", _("Connected Peers")); + l = gtk_label_new (NULL); + gtk_misc_set_alignment (GTK_MISC(l), 0.0f, 0.5f); + gtk_label_set_markup (GTK_LABEL(l), name); + gtk_box_pack_start (GTK_BOX(vbox), l, FALSE, FALSE, 0); + + h = gtk_hbox_new (FALSE, GUI_PAD); + w = gtk_alignment_new (0.0f, 0.0f, 0.0f, 0.0f); + gtk_widget_set_size_request (w, GUI_PAD_BIG, 0); + gtk_box_pack_start (GTK_BOX(h), w, FALSE, FALSE, 0); + gtk_box_pack_start_defaults (GTK_BOX(h), sw); + gtk_box_pack_start_defaults (GTK_BOX(vbox), h); + + hbox = gtk_hbox_new (FALSE, GUI_PAD); + w = gtk_alignment_new (0.0f, 0.0f, 0.0f, 0.0f); + gtk_widget_set_size_request (w, GUI_PAD_BIG, 0); + gtk_box_pack_start (GTK_BOX(hbox), w, FALSE, FALSE, 0); + g_snprintf (name, sizeof(name), "%s:", _("Seeders")); + l = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL(l), name); + gtk_box_pack_start (GTK_BOX(hbox), l, FALSE, FALSE, 0); + l = p->seeders_lb = gtk_label_new (NULL); + gtk_box_pack_start (GTK_BOX(hbox), l, FALSE, FALSE, 0); + gtk_box_pack_start_defaults (GTK_BOX(hbox), + gtk_alignment_new (0.0f, 0.0f, 0.0f, 0.0f)); + g_snprintf (name, sizeof(name), "%s:", _("Leechers")); + l = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL(l), name); + gtk_box_pack_start (GTK_BOX(hbox), l, FALSE, FALSE, 0); + l = p->leechers_lb = gtk_label_new (NULL); + gtk_box_pack_start (GTK_BOX(hbox), l, FALSE, FALSE, 0); + gtk_box_pack_start_defaults (GTK_BOX(hbox), + gtk_alignment_new (0.0f, 0.0f, 0.0f, 0.0f)); + g_snprintf (name, sizeof(name), "%s:", _("Completed")); + l = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL(l), name); + gtk_box_pack_start (GTK_BOX(hbox), l, FALSE, FALSE, 0); + l = p->completed_lb = gtk_label_new (NULL); + gtk_box_pack_start (GTK_BOX(hbox), l, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + ret = vbox; + p->gtor = gtor; + p->model = m; + p->store = GTK_TREE_STORE(m); + g_object_set_data_full (G_OBJECT(ret), "peer-data", p, g_free); + return ret; +} + +/**** +***** INFO TAB +****/ + +static GtkWidget* info_page_new (tr_torrent * tor) +{ + int row = 0; + GtkWidget *t = hig_workarea_create (); + GtkWidget *l, *w, *fr; + char *pch; + char *dname, *bname; + char sizeStr[128]; + char buf[256]; + char name[128]; + const char * namefmt = "%s:"; + GtkTextBuffer * b; + tr_tracker_info * track; + const tr_info * info = tr_torrentInfo(tor); + + hig_workarea_add_section_title (t, &row, _("Torrent Information")); + + g_snprintf (name, sizeof(name), namefmt, _("Tracker")); + track = info->trackerList->list; + pch = track->port==80 + ? g_strdup_printf( "http://%s%s", track->address, track->announce ) + : g_strdup_printf( "http://%s:%d%s", track->address, track->port, track->announce ); + l = gtk_label_new( pch ); + gtk_label_set_ellipsize( GTK_LABEL( l ), PANGO_ELLIPSIZE_END ); + + hig_workarea_add_row (t, &row, name, l, NULL); + g_free (pch); + + g_snprintf (name, sizeof(name), namefmt, _("Pieces")); + tr_strlsize( sizeStr, info->pieceSize, sizeof(sizeStr) ); + g_snprintf( buf, sizeof(buf), "%d (%s)", info->pieceCount, sizeStr ); + l = gtk_label_new (buf); + hig_workarea_add_row (t, &row, name, l, NULL); + + g_snprintf (name, sizeof(name), namefmt, _("Hash")); + l = gtk_label_new (info->hashString); + gtk_label_set_ellipsize( GTK_LABEL( l ), PANGO_ELLIPSIZE_END ); + hig_workarea_add_row (t, &row, name, l, NULL); + + g_snprintf (name, sizeof(name), namefmt, _("Secure")); + pch = (info->isPrivate ) + ? _("Private Torrent, PEX disabled") + : _("Public Torrent"); + l = gtk_label_new (pch); + hig_workarea_add_row (t, &row, name, l, NULL); + + g_snprintf (name, sizeof(name), namefmt, _("Comment")); + b = gtk_text_buffer_new (NULL); + if( info->comment ) + gtk_text_buffer_set_text (b, info->comment, -1); + w = gtk_text_view_new_with_buffer (b); + gtk_widget_set_size_request (w, 0u, 100u); + gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW(w), GTK_WRAP_WORD); + gtk_text_view_set_editable (GTK_TEXT_VIEW(w), FALSE); + fr = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME(fr), GTK_SHADOW_IN); + gtk_container_add (GTK_CONTAINER(fr), w); + w = hig_workarea_add_row (t, &row, name, fr, NULL); + gtk_misc_set_alignment (GTK_MISC(w), 0.0f, 0.0f); + + hig_workarea_add_section_divider (t, &row); + hig_workarea_add_section_title (t, &row, _("Created by")); + + g_snprintf (name, sizeof(name), namefmt, _("Creator")); + l = gtk_label_new (*info->creator ? info->creator : _("N/A")); + hig_workarea_add_row (t, &row, name, l, NULL); + + g_snprintf (name, sizeof(name), namefmt, _("Date")); + pch = rfc822date ((guint64)info->dateCreated * 1000u); + l = gtk_label_new (pch); + hig_workarea_add_row (t, &row, name, l, NULL); + g_free (pch); + + hig_workarea_add_section_divider (t, &row); + hig_workarea_add_section_title (t, &row, _("Location")); + + g_snprintf (name, sizeof(name), namefmt, _("Downloaded data")); + l = gtk_label_new (tr_torrentGetFolder (tor)); + gtk_label_set_ellipsize( GTK_LABEL( l ), PANGO_ELLIPSIZE_END ); + hig_workarea_add_row (t, &row, name, l, NULL); + + g_snprintf (name, sizeof(name), namefmt, _("Torrent file path")); + dname = g_path_get_dirname (info->torrent); + l = gtk_label_new ( dname ); + gtk_label_set_ellipsize( GTK_LABEL( l ), PANGO_ELLIPSIZE_END ); + hig_workarea_add_row (t, &row, name, l, NULL); + g_free (dname); + + g_snprintf (name, sizeof(name), namefmt, _("Torrent file name")); + bname = g_path_get_basename (info->torrent); + l = gtk_label_new (bname); + gtk_label_set_ellipsize( GTK_LABEL( l ), PANGO_ELLIPSIZE_END ); + hig_workarea_add_row (t, &row, name, l, NULL); + g_free (bname); + + hig_workarea_finish (t, &row); + return t; +} + +/**** +***** ACTIVITY TAB +****/ + +typedef struct +{ + GtkWidget * state_lb; + GtkWidget * progress_lb; + GtkWidget * have_lb; + GtkWidget * dl_lb; + GtkWidget * ul_lb; + GtkWidget * failed_lb; + GtkWidget * ratio_lb; + GtkWidget * err_lb; + GtkWidget * swarm_lb; + GtkWidget * date_added_lb; + GtkWidget * last_activity_lb; + GtkWidget * availability_da; + TrTorrent * gtor; +} +Activity; + +static void +refresh_activity (GtkWidget * top) +{ + Activity * a = (Activity*) g_object_get_data (G_OBJECT(top), "activity-data"); + const tr_stat * stat = tr_torrent_stat( a->gtor ); + char *pch; + char sizeStr[64]; + char sizeStr2[64]; + char buf[128]; + + pch = tr_torrent_status_str( a->gtor ); + gtk_label_set_text (GTK_LABEL(a->state_lb), pch); + g_free (pch); + + pch = g_strdup_printf( _( "%.1f%% (%.1f%% selected)" ), stat->percentComplete*100.0, stat->percentDone*100.0 ); + gtk_label_set_text (GTK_LABEL(a->progress_lb), pch); + g_free (pch); + + tr_strlsize( sizeStr, stat->haveValid + stat->haveUnchecked, sizeof(sizeStr) ); + tr_strlsize( sizeStr2, stat->haveValid, sizeof(sizeStr2) ); + g_snprintf( buf, sizeof(buf), _("%s (%s verified)"), sizeStr, sizeStr2 ); + gtk_label_set_text( GTK_LABEL( a->have_lb ), buf ); + + tr_strlsize( sizeStr, stat->downloadedEver, sizeof(sizeStr) ); + gtk_label_set_text( GTK_LABEL(a->dl_lb), sizeStr ); + + tr_strlsize( sizeStr, stat->uploadedEver, sizeof(sizeStr) ); + gtk_label_set_text( GTK_LABEL(a->ul_lb), sizeStr ); + + tr_strlsize( sizeStr, stat->corruptEver, sizeof( sizeStr ) ); + gtk_label_set_text( GTK_LABEL( a->failed_lb ), sizeStr ); + + tr_strlratio( buf, stat->ratio, sizeof( buf ) ); + gtk_label_set_text( GTK_LABEL( a->ratio_lb ), buf ); + + tr_strlspeed( buf, stat->swarmspeed, sizeof(buf) ); + gtk_label_set_text (GTK_LABEL(a->swarm_lb), buf ); + + gtk_label_set_text (GTK_LABEL(a->err_lb), + *stat->errorString ? stat->errorString : _("None")); + + pch = stat->startDate ? rfc822date (stat->startDate) + : g_strdup_printf ("?"); + gtk_label_set_text (GTK_LABEL(a->date_added_lb), pch); + g_free (pch); + + pch = stat->activityDate ? rfc822date (stat->activityDate) + : g_strdup_printf ("?"); + gtk_label_set_text (GTK_LABEL(a->last_activity_lb), pch); + g_free (pch); + + if (GDK_IS_DRAWABLE (a->availability_da->window)) + refresh_pieces (a->availability_da, NULL, a->gtor); +} + + +static GtkWidget* +activity_page_new (TrTorrent * gtor) +{ + Activity * a = g_new (Activity, 1); + int row = 0; + GtkWidget *t = hig_workarea_create (); + GtkWidget *l, *w; + + a->gtor = gtor; + + hig_workarea_add_section_title (t, &row, _("Transfer")); + + l = a->state_lb = gtk_label_new (NULL); + hig_workarea_add_row (t, &row, _("State:"), l, NULL); + + l = a->progress_lb = gtk_label_new (NULL); + hig_workarea_add_row (t, &row, _("Progress:"), l, NULL); + + l = a->have_lb = gtk_label_new (NULL); + hig_workarea_add_row (t, &row, _("Have:"), l, NULL); + + l = a->dl_lb = gtk_label_new (NULL); + hig_workarea_add_row (t, &row, _("Downloaded:"), l, NULL); + + l = a->ul_lb = gtk_label_new (NULL); + hig_workarea_add_row (t, &row, _("Uploaded:"), l, NULL); + + l = a->failed_lb = gtk_label_new (NULL); + hig_workarea_add_row (t, &row, _("Failed DL:"), l, NULL); + + l = a->ratio_lb = gtk_label_new (NULL); + hig_workarea_add_row (t, &row, _("Ratio:"), l, NULL); + + l = a->swarm_lb = gtk_label_new (NULL); + hig_workarea_add_row (t, &row, _("Swarm rate:"), l, NULL); + + l = a->err_lb = gtk_label_new (NULL); + hig_workarea_add_row (t, &row, _("Error:"), l, NULL); + + hig_workarea_add_section_divider (t, &row); + hig_workarea_add_section_title (t, &row, _("Completeness")); + + w = a->availability_da = gtk_drawing_area_new (); + gtk_widget_set_size_request (w, 0u, 100u); + g_object_set_data (G_OBJECT(w), "draw-mode", GINT_TO_POINTER(DRAW_PROG)); + g_signal_connect (w, "expose-event", G_CALLBACK(refresh_pieces), gtor); + hig_workarea_add_wide_control( t, &row, w ); + + hig_workarea_add_section_divider (t, &row); + hig_workarea_add_section_title (t, &row, _("Dates")); + + l = a->date_added_lb = gtk_label_new (NULL); + hig_workarea_add_row (t, &row, _("Date added:"), l, NULL); + + l = a->last_activity_lb = gtk_label_new (NULL); + hig_workarea_add_row (t, &row, _("Last activity"), l, NULL); + + hig_workarea_add_section_divider (t, &row); + hig_workarea_finish (t, &row); + g_object_set_data_full (G_OBJECT(t), "activity-data", a, g_free); + return t; +} + + +/**** +***** OPTIONS +****/ + +static void +speed_toggled_cb( GtkToggleButton * tb, gpointer gtor, int up_or_down ) +{ + tr_torrent * tor = tr_torrent_handle (gtor); + gboolean b = gtk_toggle_button_get_active(tb); + tr_torrentSetSpeedMode( tor, up_or_down, b ? TR_SPEEDLIMIT_SINGLE + : TR_SPEEDLIMIT_GLOBAL ); +} +static void +ul_speed_toggled_cb (GtkToggleButton *tb, gpointer gtor) +{ + speed_toggled_cb( tb, gtor, TR_UP ); +} +static void +dl_speed_toggled_cb (GtkToggleButton *tb, gpointer gtor) +{ + speed_toggled_cb( tb, gtor, TR_DOWN ); +} + + +#if 0 +static void +seeding_cap_toggled_cb (GtkToggleButton *tb, gpointer gtor) +{ + tr_torrent_set_seeding_cap_enabled (TR_TORRENT(gtor), + gtk_toggle_button_get_active(tb)); +} +#endif + +static void +sensitize_from_check_cb (GtkToggleButton *toggle, gpointer w) +{ + gtk_widget_set_sensitive (GTK_WIDGET(w), + gtk_toggle_button_get_active(toggle)); +} + +static void +setSpeedLimit( GtkSpinButton* spin, gpointer gtor, int up_or_down ) +{ + tr_torrent * tor = tr_torrent_handle (gtor); + int KiB_sec = gtk_spin_button_get_value_as_int (spin); + tr_torrentSetSpeedLimit( tor, up_or_down, KiB_sec ); +} +static void +ul_speed_spun_cb (GtkSpinButton *spin, gpointer gtor) +{ + setSpeedLimit( spin, gtor, TR_UP ); +} +static void +dl_speed_spun_cb (GtkSpinButton *spin, gpointer gtor) +{ + setSpeedLimit( spin, gtor, TR_DOWN ); +} + +#if 0 +static void +seeding_ratio_spun_cb (GtkSpinButton *spin, gpointer gtor) +{ + tr_torrent_set_seeding_cap_ratio (TR_TORRENT(gtor), + gtk_spin_button_get_value(spin)); +} +#endif + +static void +max_peers_spun_cb( GtkSpinButton * spin, gpointer gtor ) +{ + const uint16_t n = gtk_spin_button_get_value( spin ); + tr_torrentSetMaxConnectedPeers( tr_torrent_handle( gtor ), n ); +} + +static GtkWidget* +options_page_new ( TrTorrent * gtor ) +{ + uint16_t maxConnectedPeers; + int i, row; + gboolean b; + GtkAdjustment *a; + GtkWidget *t, *w, *tb, *hb, *mis; + tr_torrent * tor = tr_torrent_handle (gtor); + + row = 0; + t = hig_workarea_create (); + hig_workarea_add_section_title (t, &row, _("Speed Limits") ); + + tb = gtk_check_button_new_with_mnemonic (_("Limit _download speed to:")); + b = tr_torrentGetSpeedMode(tor,TR_DOWN) == TR_SPEEDLIMIT_SINGLE; + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(tb), b ); + g_signal_connect (tb, "toggled", G_CALLBACK(dl_speed_toggled_cb), gtor); + + i = tr_torrentGetSpeedLimit( tor, TR_DOWN ); + hb = gtk_hbox_new ( FALSE, 6 ); + a = (GtkAdjustment*) gtk_adjustment_new (i, 0.0, G_MAXDOUBLE, 1, 1, 1); + w = gtk_spin_button_new (a, 1, 0); + g_signal_connect (w, "value-changed", G_CALLBACK(dl_speed_spun_cb), gtor); + g_signal_connect (tb, "toggled", G_CALLBACK(sensitize_from_check_cb), w); + sensitize_from_check_cb (GTK_TOGGLE_BUTTON(tb), w); + gtk_box_pack_start ( GTK_BOX (hb), GTK_WIDGET (w), FALSE, TRUE, 0); + mis = gtk_label_new (_("KiB/s")); + gtk_box_pack_start ( GTK_BOX (hb), GTK_WIDGET (mis), FALSE, TRUE, 0); + hig_workarea_add_row_w (t, &row, tb, hb, NULL); + + tb = gtk_check_button_new_with_mnemonic (_("Limit _upload speed to:")); + b = tr_torrentGetSpeedMode(tor,TR_UP) == TR_SPEEDLIMIT_SINGLE; + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(tb), b ); + g_signal_connect (tb, "toggled", G_CALLBACK(ul_speed_toggled_cb), gtor); + + i = tr_torrentGetSpeedLimit( tor, TR_UP ); + hb = gtk_hbox_new ( FALSE, 6 ); + a = (GtkAdjustment*) gtk_adjustment_new (i, 0.0, G_MAXDOUBLE, 1, 1, 1); + w = gtk_spin_button_new (a, 1, 0); + g_signal_connect (w, "value-changed", G_CALLBACK(ul_speed_spun_cb), gtor); + g_signal_connect (tb, "toggled", G_CALLBACK(sensitize_from_check_cb), w); + sensitize_from_check_cb (GTK_TOGGLE_BUTTON(tb), w); + gtk_box_pack_start ( GTK_BOX (hb), GTK_WIDGET (w), FALSE, TRUE, 0); + mis = gtk_label_new (_("KiB/s")); + gtk_box_pack_start ( GTK_BOX (hb), GTK_WIDGET (mis), FALSE, TRUE, 0); + hig_workarea_add_row_w (t, &row, tb, hb, NULL); + + hig_workarea_add_section_divider (t, &row); + hig_workarea_add_section_title (t, &row, _("Peer Connections")); + + maxConnectedPeers = tr_torrentGetMaxConnectedPeers( tor ); + w = gtk_spin_button_new_with_range( 1, 3000, 5 ); + gtk_spin_button_set_value( GTK_SPIN_BUTTON( w ), maxConnectedPeers ); + hb = gtk_hbox_new ( FALSE, 6 ); + gtk_box_pack_start ( GTK_BOX (hb), GTK_WIDGET (w), FALSE, TRUE, 0); + mis = gtk_label_new (_("peers")); + gtk_box_pack_start ( GTK_BOX (hb), GTK_WIDGET (mis), FALSE, TRUE, 0); + hig_workarea_add_row( t, &row, _( "Connect at _maximum to:" ), hb, w ); + g_signal_connect( w, "value-changed", G_CALLBACK( max_peers_spun_cb ), gtor ); + +#if 0 + tb = gtk_check_button_new_with_mnemonic (_("_Stop seeding at ratio:")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(tb), gtor->seeding_cap_enabled); + g_signal_connect (tb, "toggled", G_CALLBACK(seeding_cap_toggled_cb), gtor); + a = (GtkAdjustment*) gtk_adjustment_new (gtor->seeding_cap, 0.0, G_MAXDOUBLE, 1, 1, 1); + w = gtk_spin_button_new (a, 1, 1); + g_signal_connect (w, "value-changed", G_CALLBACK(seeding_ratio_spun_cb), gtor); + g_signal_connect (tb, "toggled", G_CALLBACK(sensitize_from_check_cb), w); + sensitize_from_check_cb (GTK_TOGGLE_BUTTON(tb), w); + hig_workarea_add_row_w (t, &row, tb, w, NULL); +#endif + + hig_workarea_finish (t, &row); + return t; +} + +static void +refresh_options (GtkWidget * top UNUSED) +{ +} + +/**** +***** DIALOG +****/ + +static void +torrent_destroyed (gpointer dialog, GObject * dead_torrent UNUSED) +{ + gtk_widget_destroy (GTK_WIDGET(dialog)); +} + +static void +remove_tag (gpointer tag) +{ + g_source_remove (GPOINTER_TO_UINT(tag)); /* stop the periodic refresh */ +} + +static void +response_cb (GtkDialog *dialog, int response UNUSED, gpointer gtor) +{ + g_object_weak_unref (G_OBJECT(gtor), torrent_destroyed, dialog); + gtk_widget_destroy (GTK_WIDGET(dialog)); +} + +static gboolean +periodic_refresh (gpointer data) +{ + refresh_peers (g_object_get_data (G_OBJECT(data), "peers-top")); + refresh_activity (g_object_get_data (G_OBJECT(data), "activity-top")); + refresh_options (g_object_get_data (G_OBJECT(data), "options-top")); + return TRUE; +} + +GtkWidget* +torrent_inspector_new ( GtkWindow * parent, TrTorrent * gtor ) +{ + guint tag; + GtkWidget *d, *n, *w; + tr_torrent * tor = tr_torrent_handle (gtor); + char sizeStr[64]; + char title[512]; + const tr_info * info = tr_torrent_info (gtor); + + /* create the dialog */ + tr_strlsize( sizeStr, info->totalSize, sizeof(sizeStr) ); + g_snprintf( title, sizeof(title), _( "Details for %s (%s)" ), info->name, sizeStr ); + d = gtk_dialog_new_with_buttons (title, parent, 0, + GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, + NULL); + gtk_window_set_role (GTK_WINDOW(d), "tr-info" ); + g_signal_connect (d, "response", G_CALLBACK (response_cb), gtor); + gtk_dialog_set_has_separator( GTK_DIALOG( d ), FALSE ); + gtk_container_set_border_width (GTK_CONTAINER (d), 5); + gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (d)->vbox), 2); + g_object_weak_ref (G_OBJECT(gtor), torrent_destroyed, d); + + + /* add the notebook */ + n = gtk_notebook_new (); + gtk_container_set_border_width ( GTK_CONTAINER ( n ), 5 ); + + w = activity_page_new (gtor); + g_object_set_data (G_OBJECT(d), "activity-top", w); + gtk_notebook_append_page (GTK_NOTEBOOK(n), w, + gtk_label_new (_("Activity"))); + + w = peer_page_new (gtor); + g_object_set_data (G_OBJECT(d), "peers-top", w); + gtk_notebook_append_page (GTK_NOTEBOOK(n), w, + gtk_label_new (_("Peers"))); + + gtk_notebook_append_page (GTK_NOTEBOOK(n), + info_page_new (tor), + gtk_label_new (_("Info"))); + + w = file_list_new( gtor ); + gtk_container_set_border_width( GTK_CONTAINER( w ), GUI_PAD_BIG ); + g_object_set_data (G_OBJECT(d), "files-top", w); + gtk_notebook_append_page (GTK_NOTEBOOK(n), w, + gtk_label_new (_("Files"))); + + w = options_page_new (gtor); + g_object_set_data (G_OBJECT(d), "options-top", w); + gtk_notebook_append_page (GTK_NOTEBOOK(n), w, + gtk_label_new (_("Options"))); + + gtk_box_pack_start_defaults (GTK_BOX(GTK_DIALOG(d)->vbox), n); + + tag = g_timeout_add (UPDATE_INTERVAL_MSEC, periodic_refresh, d); + g_object_set_data_full (G_OBJECT(d), "tag", + GUINT_TO_POINTER(tag), remove_tag); + + /* return the results */ + periodic_refresh (d); + gtk_widget_show_all (GTK_DIALOG(d)->vbox); + return d; +} --- transmission-1.06.orig/gtk/open-dialog.h +++ transmission-1.06/gtk/open-dialog.h @@ -0,0 +1,23 @@ +/* + * This file Copyright (C) 2008 Charles Kerr + * + * 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: hig.h 4404 2008-01-01 17:20:20Z charles $ + */ + +#ifndef TR_GTK_OPEN_DIALOG_H +#define TR_GTK_OPEN_DIALOG_H + +#include +#include "tr_core.h" + +void makeaddwind( GtkWindow * parent, + TrCore * core, + tr_ctor * ctor ); + +#endif /* TR_GTK_OPEN_DIALOG */ --- transmission-1.06.orig/gtk/Makefile.am +++ transmission-1.06/gtk/Makefile.am @@ -2,6 +2,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir) \ + -I$(top_srcdir)/third-party/ \ $(LIBEVENT_CPPFLAGS) \ -DTRANSMISSIONLOCALEDIR=\""$(transmissionlocaledir)"\" @@ -13,18 +14,20 @@ noinst_HEADERS = \ actions.h \ conf.h \ + details.h \ dialogs.h \ hig.h \ io.h \ ipc.h \ + file-list.h \ lock.h \ logo.h \ makemeta-ui.h \ msgwin.h \ + open-dialog.h \ stats.h \ sexy-icon-entry.h \ torrent-cell-renderer.h \ - torrent-inspector.h \ tr_core.h \ tr_icon.h \ tr_prefs.h \ @@ -38,17 +41,19 @@ transmission_SOURCES = \ actions.c \ conf.c \ + details.c \ dialogs.c \ + file-list.c \ hig.c \ io.c \ ipc.c \ main.c \ makemeta-ui.c \ msgwin.c \ + open-dialog.c \ sexy-icon-entry.c \ stats.c \ torrent-cell-renderer.c \ - torrent-inspector.c \ tr_core.c \ tr_icon.c \ tr_prefs.c \ --- transmission-1.06.orig/gtk/dialogs.h +++ transmission-1.06/gtk/dialogs.h @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: dialogs.h 4404 2008-01-01 17:20:20Z charles $ + * $Id$ * * Copyright (c) 2005-2008 Transmission authors and contributors * @@ -29,17 +29,12 @@ #include "tr_torrent.h" #include "util.h" -/* show the "add a torrent" dialog */ -void -makeaddwind( GtkWindow * parent, TrCore * core ); - /* prompt for a download directory for torrents, then add them */ -void -promptfordir( GtkWindow * parent, TrCore * core, GList * files, uint8_t * data, - size_t size, enum tr_torrent_action act, gboolean paused ); +void promptfordir( GtkWindow* parent, TrCore*, GList* filenames, tr_ctor* ); /* prompt if the user wants to quit, calls func with cbdata if they do */ -void -askquit( TrCore*, GtkWindow* parent, callbackfunc_t func, void * cbdata ); +void askquit( TrCore*, GtkWindow* parent, callbackfunc_t func, void* cbdata ); + +void confirmDelete( GtkWindow * parent, TrCore * core, GList * torrents ); #endif /* TG_PREFS_H */ --- transmission-1.06.orig/gtk/tr_torrent.c +++ transmission-1.06/gtk/tr_torrent.c @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: tr_torrent.c 4404 2008-01-01 17:20:20Z charles $ + * $Id$ * * Copyright (c) 2006-2008 Transmission authors and contributors * @@ -22,11 +22,13 @@ * DEALINGS IN THE SOFTWARE. *****************************************************************************/ +#include #include #include #include #include +#include #include @@ -35,58 +37,69 @@ #include "conf.h" #include "util.h" +struct TrTorrentPrivate +{ + tr_torrent * handle; + gboolean seeding_cap_enabled; + gdouble seeding_cap; /* ratio to stop seeding at */ +}; + + static void tr_torrent_init(GTypeInstance *instance, gpointer g_class UNUSED ) { - TrTorrent *self = (TrTorrent *)instance; + TrTorrent * self = TR_TORRENT( instance ); + struct TrTorrentPrivate * p; + + p = self->priv = G_TYPE_INSTANCE_GET_PRIVATE( self, + TR_TORRENT_TYPE, + struct TrTorrentPrivate ); + p->handle = NULL; + p->seeding_cap = 2.0; #ifdef REFDBG - fprintf( stderr, "torrent %p init\n", self ); + g_message( "torrent %p init", self ); #endif +} - self->handle = NULL; - self->delfile = NULL; - self->severed = FALSE; - self->disposed = FALSE; - self->seeding_cap = 2.0; +static int +isDisposed( const TrTorrent * self ) +{ + return !self || !self->priv; } static void -tr_torrent_dispose(GObject *obj) +tr_torrent_dispose( GObject * o ) { - GObjectClass *parent = g_type_class_peek(g_type_parent(TR_TORRENT_TYPE)); - TrTorrent *self = (TrTorrent*)obj; - - if(self->disposed) - return; - self->disposed = TRUE; - -#ifdef REFDBG - fprintf( stderr, "torrent %p dispose\n", self ); -#endif - - if( !self->severed ) - tr_torrent_sever( self ); + GObjectClass * parent; + TrTorrent * self = TR_TORRENT( o ); - g_free (self->delfile); + if( !isDisposed( self ) ) + { + if( self->priv->handle ) + tr_torrentClose( self->priv->handle ); + self->priv = NULL; + } - /* Chain up to the parent class */ - parent->dispose(obj); + parent = g_type_class_peek(g_type_parent(TR_TORRENT_TYPE)); + parent->dispose( o ); } static void -tr_torrent_class_init(gpointer g_class, gpointer g_class_data UNUSED ) +tr_torrent_class_init( gpointer g_class, gpointer g_class_data UNUSED ) { - GObjectClass *gobject_class = G_OBJECT_CLASS(g_class); - gobject_class->dispose = tr_torrent_dispose; + GObjectClass *gobject_class = G_OBJECT_CLASS(g_class); + gobject_class->dispose = tr_torrent_dispose; + g_type_class_add_private( g_class, sizeof( struct TrTorrentPrivate ) ); } GType -tr_torrent_get_type(void) +tr_torrent_get_type( void ) { static GType type = 0; - if(0 == type) { + if( !type ) + { static const GTypeInfo info = { sizeof (TrTorrentClass), NULL, /* base_init */ @@ -104,67 +117,49 @@ return type; } -void -tr_torrent_sever( TrTorrent * self ) -{ - g_return_if_fail (TR_IS_TORRENT( self )); - - if( !self->severed ) - { - self->severed = TRUE; - - if( self->handle ) - tr_torrentClose( self->handle ); - } -} - tr_torrent * tr_torrent_handle(TrTorrent *tor) { g_assert( TR_IS_TORRENT(tor) ); - return tor->severed ? NULL : tor->handle; + return isDisposed( tor ) ? NULL : tor->priv->handle; } const tr_stat * tr_torrent_stat(TrTorrent *tor) { - return tor && !tor->severed ? tr_torrentStatCached( tor->handle ) : NULL; + tr_torrent * handle = tr_torrent_handle( tor ); + return handle ? tr_torrentStatCached( handle ) : NULL; } const tr_info * -tr_torrent_info(TrTorrent *tor) { - TR_IS_TORRENT(tor); - - if(tor->severed) - return NULL; - - return tr_torrentInfo(tor->handle); +tr_torrent_info( TrTorrent * tor ) +{ + tr_torrent * handle = tr_torrent_handle( tor ); + return handle ? tr_torrentInfo( handle ) : NULL; } void tr_torrent_start( TrTorrent * self ) { - TR_IS_TORRENT( self ); - - if( !self->severed ) - tr_torrentStart( self->handle ); + tr_torrent * handle = tr_torrent_handle( self ); + if( handle ) + tr_torrentStart( handle ); } void tr_torrent_stop( TrTorrent * self ) { - TR_IS_TORRENT( self ); - - if( !self->severed ) - tr_torrentStop( self->handle ); + tr_torrent * handle = tr_torrent_handle( self ); + if( handle ) + tr_torrentStop( handle ); } static TrTorrent * maketorrent( tr_torrent * handle ) { TrTorrent * tor = g_object_new( TR_TORRENT_TYPE, NULL ); - tor->handle = handle; + tor->priv->handle = handle; return tor; } @@ -175,109 +170,60 @@ } TrTorrent * -tr_torrent_new( tr_handle * handle, - const char * metainfo_filename, - const char * destination, - enum tr_torrent_action act, - gboolean paused, - char ** err ) -{ - TrTorrent * ret; - tr_torrent * tor; - tr_ctor * ctor; - int errcode = -1; - - g_assert( destination ); - - *err = NULL; - - ctor = tr_ctorNew( handle ); - tr_ctorSetMetainfoFromFile( ctor, metainfo_filename ); - tr_ctorSetDestination( ctor, TR_FORCE, destination ); - tr_ctorSetPaused( ctor, TR_FORCE, paused ); - tr_ctorSetMaxConnectedPeers( ctor, TR_FORCE, pref_int_get( PREF_KEY_MAX_PEERS_PER_TORRENT ) ); - tor = tr_torrentNew( handle, ctor, &errcode ); - tr_ctorFree( ctor ); - - if( tor == NULL ) { - switch( errcode ) { - case TR_EINVALID: - *err = g_strdup_printf(_("%s: not a valid torrent file"), metainfo_filename ); - break; - case TR_EDUPLICATE: - *err = g_strdup_printf(_("%s: torrent is already open"), metainfo_filename ); - break; - default: - *err = g_strdup( metainfo_filename ); - break; - } - return NULL; - } +tr_torrent_new_ctor( tr_handle * handle, + tr_ctor * ctor, + char ** err ) +{ + tr_torrent * tor; + int errcode; - ret = maketorrent( tor ); - if( TR_TOR_MOVE == act ) - ret->delfile = g_strdup( metainfo_filename ); - return ret; -} + errcode = -1; + *err = NULL; -TrTorrent * -tr_torrent_new_with_data( tr_handle * handle, - uint8_t * metainfo, - size_t size, - const char * destination, - gboolean paused, - char ** err ) -{ - tr_torrent * tor; - tr_ctor * ctor; - int errcode = -1; - - g_assert( destination ); - - *err = NULL; - - ctor = tr_ctorNew( handle ); - tr_ctorSetMetainfo( ctor, metainfo, size ); - tr_ctorSetDestination( ctor, TR_FORCE, destination ); - tr_ctorSetPaused( ctor, TR_FORCE, paused ); - tr_ctorSetMaxConnectedPeers( ctor, TR_FORCE, pref_int_get( PREF_KEY_MAX_PEERS_PER_TORRENT ) ); - tor = tr_torrentNew( handle, ctor, &errcode ); + tor = tr_torrentNew( handle, ctor, &errcode ); - if( tor == NULL ) { - switch( errcode ) { - case TR_EINVALID: - *err = g_strdup( _("not a valid torrent file") ); - break; - case TR_EDUPLICATE: - *err = g_strdup( _("torrent is already open") ); - break; - default: - *err = g_strdup( "" ); - break; + if( !tor ) + { + const char * filename = tr_ctorGetSourceFile( ctor ); + if( !filename ) + filename = "(null)"; + + switch( errcode ) + { + case TR_EINVALID: + *err = g_strdup_printf( _("%s: not a valid torrent file"), filename ); + break; + case TR_EDUPLICATE: + *err = g_strdup_printf( _("%s: torrent is already open"), filename ); + break; + default: + *err = g_strdup( filename ); + break; + } + + return NULL; } - return NULL; - } - return maketorrent( tor ); + return maketorrent( tor ); } void tr_torrent_check_seeding_cap ( TrTorrent *gtor) { const tr_stat * st = tr_torrent_stat( gtor ); - if ((gtor->seeding_cap_enabled) && (st->ratio >= gtor->seeding_cap)) + if ((gtor->priv->seeding_cap_enabled) && (st->ratio >= gtor->priv->seeding_cap)) tr_torrent_stop (gtor); } void tr_torrent_set_seeding_cap_ratio ( TrTorrent *gtor, gdouble ratio ) { - gtor->seeding_cap = ratio; + gtor->priv->seeding_cap = ratio; tr_torrent_check_seeding_cap (gtor); } void tr_torrent_set_seeding_cap_enabled ( TrTorrent *gtor, gboolean b ) { - if ((gtor->seeding_cap_enabled = b)) + if ((gtor->priv->seeding_cap_enabled = b)) tr_torrent_check_seeding_cap (gtor); } @@ -341,3 +287,24 @@ return top; } + +void +tr_torrent_delete_files( TrTorrent * gtor ) +{ + int i; + const tr_info * info = tr_torrent_info( gtor ); + const char * stop = tr_torrentGetFolder( tr_torrent_handle( gtor ) ); + + for( i=0; info && ifileCount; ++i ) + { + char * file = g_build_filename( stop, info->files[i].name, NULL ); + while( strcmp( stop, file ) && strlen(stop) < strlen(file) ) + { + char * swap = g_path_get_dirname( file ); + g_unlink( file ); + g_free( file ); + file = swap; + } + g_free( file ); + } +} --- transmission-1.06.orig/gtk/details.h +++ transmission-1.06/gtk/details.h @@ -0,0 +1,21 @@ +/* + * This file Copyright (C) 2007-2008 Charles Kerr + * + * 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 GTK_TORRENT_INSPECTOR_H +#define GTK_TORRENT_INSPECTOR_H + +#include +#include "tr_torrent.h" + +GtkWidget* torrent_inspector_new ( GtkWindow * parent, TrTorrent * tor ); + +#endif /* TG_PREFS_H */ --- transmission-1.06.orig/gtk/tr_torrent.h +++ transmission-1.06/gtk/tr_torrent.h @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: tr_torrent.h 4404 2008-01-01 17:20:20Z charles $ + * $Id$ * * Copyright (c) 2006-2008 Transmission authors and contributors * @@ -42,25 +42,18 @@ #define TR_TORRENT_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), TR_TORRENT_TYPE, TrTorrentClass)) -typedef struct _TrTorrent TrTorrent; -typedef struct _TrTorrentClass TrTorrentClass; - -/* treat the contents of this structure as private */ -struct _TrTorrent { - GObject parent; - tr_torrent *handle; - char *delfile; - - /* FIXME: hm, are these heavyweight enough to deserve their own properties? */ - gboolean severed; - gboolean disposed; - gboolean seeding_cap_enabled; - gdouble seeding_cap; /* ratio to stop seeding at */ -}; - -struct _TrTorrentClass { - GObjectClass parent; -}; +typedef struct _TrTorrent +{ + GObject parent; + struct TrTorrentPrivate * priv; +} +TrTorrent; + +typedef struct TrTorrentClass +{ + GObjectClass parent; +} +TrTorrentClass; GType tr_torrent_get_type(void); @@ -84,6 +77,9 @@ tr_torrent_status_str ( TrTorrent * tor ); void +tr_torrent_delete_files( TrTorrent * tor ); + +void tr_torrent_check_seeding_cap ( TrTorrent* ); void tr_torrent_set_seeding_cap_ratio ( TrTorrent*, gdouble ratio ); @@ -94,14 +90,6 @@ tr_torrent_new_preexisting( tr_torrent * tor ); TrTorrent * -tr_torrent_new( tr_handle * handle, const char * path, const char * dir, - enum tr_torrent_action act, gboolean paused, char ** err); - -TrTorrent * -tr_torrent_new_with_data( tr_handle * handle, uint8_t * data, size_t size, - const char * dir, gboolean paused, char ** err ); - -void -tr_torrent_sever( TrTorrent * tor ); +tr_torrent_new_ctor( tr_handle * handle, tr_ctor * ctor, char ** err ); #endif --- transmission-1.06.orig/gtk/dialogs.c +++ transmission-1.06/gtk/dialogs.c @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: dialogs.c 4847 2008-01-27 20:12:07Z charles $ + * $Id$ * * Copyright (c) 2005-2008 Transmission authors and contributors * @@ -22,11 +22,6 @@ * DEALINGS IN THE SOFTWARE. *****************************************************************************/ -#include -#include -#include -#include - #include #include @@ -36,186 +31,62 @@ #include "dialogs.h" #include "hig.h" #include "tr_core.h" -#include "tr_icon.h" #include "tr_prefs.h" -#include "util.h" - -#define UPDATE_INTERVAL 1000 -#define PREFNAME "transmission-dialog-pref-name" -#define FILESWIND_EXTRA_INDENT 4 - -#define STRIPROOT( path ) \ - ( g_path_is_absolute( (path) ) ? g_path_skip_root( (path) ) : (path) ) - -struct addcb -{ - GtkWidget * widget; - TrCore * core; - gboolean autostart; - gboolean usingaltdir; - GtkFileChooser *altdir; - GtkButtonBox *altbox; -}; struct dirdata { - GtkWidget * widget; - TrCore * core; - GList * files; - uint8_t * data; - size_t size; - enum tr_torrent_action action; - gboolean paused; + GtkWidget * widget; + TrCore * core; + GList * files; + tr_ctor * ctor; }; static void -addwindnocore( gpointer gdata, GObject * core ); -static void -autoclick(GtkWidget *widget, gpointer gdata); -static void -dirclick(GtkWidget *widget, gpointer gdata); -static void -addresp(GtkWidget *widget, gint resp, gpointer gdata); -static void -promptdirnocore( gpointer gdata, GObject * core ); -static void -promptresp( GtkWidget * widget, gint resp, gpointer data ); - -void -makeaddwind( GtkWindow * parent, TrCore * core ) -{ - GtkWidget *wind = gtk_file_chooser_dialog_new(_("Open Torrent"), parent, - GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); - struct addcb *data = g_new(struct addcb, 1); - GtkWidget *vbox = gtk_vbox_new(FALSE, 3); - GtkWidget *bbox = gtk_hbutton_box_new(); - GtkWidget *autocheck = gtk_check_button_new_with_mnemonic( - _("Automatically _start torrent")); - GtkWidget *dircheck = gtk_check_button_new_with_mnemonic( - _("Use alternate _download directory")); - GtkFileFilter *filter = gtk_file_filter_new(); - GtkFileFilter *unfilter = gtk_file_filter_new(); - GtkWidget *getdir = gtk_file_chooser_button_new( - _("Choose a download directory"), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER); - char * pref; - - data->widget = wind; - data->core = core; - data->autostart = TRUE; - data->usingaltdir = FALSE; - data->altdir = GTK_FILE_CHOOSER(getdir); - data->altbox = GTK_BUTTON_BOX(bbox); - - g_object_weak_ref( G_OBJECT( core ), addwindnocore, data ); - - gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_EDGE); - gtk_box_pack_start_defaults(GTK_BOX(bbox), dircheck); - gtk_box_pack_start_defaults(GTK_BOX(bbox), getdir); - - gtk_box_pack_start_defaults(GTK_BOX(vbox), autocheck); - gtk_box_pack_start_defaults(GTK_BOX(vbox), bbox); - - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(autocheck), TRUE); - pref = pref_string_get( PREF_KEY_DIR_DEFAULT ); - if( pref != NULL ) { - gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER( wind ), pref ); - g_free( pref ); - } - - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dircheck), FALSE); - gtk_widget_set_sensitive(getdir, FALSE); - - gtk_file_filter_set_name(filter, _("Torrent files")); - gtk_file_filter_add_pattern(filter, "*.torrent"); - gtk_file_filter_set_name(unfilter, _("All files")); - gtk_file_filter_add_pattern(unfilter, "*"); - - gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(wind), filter); - gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(wind), unfilter); - gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(wind), TRUE); - gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(wind), vbox); - - g_signal_connect(G_OBJECT(autocheck), "clicked", G_CALLBACK(autoclick),data); - g_signal_connect(G_OBJECT(dircheck), "clicked", G_CALLBACK(dirclick), data); - g_signal_connect(G_OBJECT(wind), "response", G_CALLBACK(addresp), data); - - gtk_widget_show_all(wind); -} - -void -addwindnocore( gpointer gdata, GObject * core SHUTUP ) +promptdirnocore( gpointer gdata, GObject * core UNUSED ) { - struct addcb * data = gdata; + struct dirdata * stuff = gdata; /* prevent the response callback from trying to remove the weak reference which no longer exists */ - data->core = NULL; - - gtk_dialog_response( GTK_DIALOG( data->widget ), GTK_RESPONSE_NONE ); -} - -static void -autoclick(GtkWidget *widget, gpointer gdata) { - struct addcb *data = gdata; + stuff->core = NULL; - data->autostart = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); + gtk_dialog_response( GTK_DIALOG( stuff->widget ), GTK_RESPONSE_NONE ); } static void -dirclick(GtkWidget *widget, gpointer gdata) { - struct addcb *data = gdata; - - data->usingaltdir = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); - gtk_widget_set_sensitive(GTK_WIDGET(data->altdir), data->usingaltdir); -} +promptresp( GtkWidget * widget, gint resp, gpointer data ) +{ + struct dirdata * stuff; -static void -addresp(GtkWidget *widget, gint resp, gpointer gdata) { - struct addcb *data = gdata; - GSList *files, *ii; - GList *stupidgtk; - char *dir; - enum tr_torrent_action action; - - if(GTK_RESPONSE_ACCEPT == resp) { - dir = NULL; - if(data->usingaltdir) - dir = gtk_file_chooser_get_filename(data->altdir); - files = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(widget)); - action = tr_prefs_get_action( PREF_KEY_ADDSTD ); + stuff = data; - if( NULL == dir ) - { - stupidgtk = NULL; - for( ii = files; NULL != ii; ii = ii->next ) - { - stupidgtk = g_list_append( stupidgtk, ii->data ); - } - tr_core_add_list( data->core, stupidgtk, action, !data->autostart ); - freestrlist(stupidgtk); - } - else + if( GTK_RESPONSE_ACCEPT == resp ) { - for( ii = files; NULL != ii; ii = ii->next ) - { - tr_core_add_dir( data->core, ii->data, dir, - action, !data->autostart ); - g_free( ii->data ); - } + char * dir; + GList * l; + + /* update the destination */ + dir = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER( widget ) ); + tr_ctorSetDestination( stuff->ctor, TR_FORCE, dir ); g_free( dir ); + + /* if there's metainfo in the ctor already, use it */ + if( !tr_ctorGetMetainfo( stuff->ctor, NULL ) ) + tr_core_add_ctor( stuff->core, stuff->ctor ); + + /* if there's a list of files, use them too */ + for( l=stuff->files; l!=NULL; l=l->next ) + if( !tr_ctorSetMetainfoFromFile( stuff->ctor, l->data ) ) + tr_core_add_ctor( stuff->core, stuff->ctor ); } - tr_core_torrents_added( data->core ); - g_slist_free(files); - } - - if( NULL != data->core ) - { - g_object_weak_unref( G_OBJECT( data->core ), addwindnocore, data ); - } - g_free( data ); - gtk_widget_destroy(widget); + if( stuff->core ) + g_object_weak_unref( G_OBJECT( stuff->core ), promptdirnocore, stuff ); + + gtk_widget_destroy( widget ); + freestrlist( stuff->files ); + tr_ctorFree( stuff->ctor ); + g_free( stuff ); } void @@ -234,28 +105,32 @@ } } +static void +deleteToggled( GtkToggleButton * tb, gpointer ctor ) +{ + tr_ctorSetDeleteSource( ctor, gtk_toggle_button_get_active( tb ) ); +} + +static void +startToggled( GtkToggleButton * tb, gpointer ctor ) +{ + tr_ctorSetPaused( ctor, TR_FORCE, !gtk_toggle_button_get_active( tb ) ); +} + void -promptfordir( GtkWindow * parent, TrCore * core, GList * files, uint8_t * data, - size_t size, enum tr_torrent_action act, gboolean paused ) +promptfordir( GtkWindow * parent, TrCore * core, GList * files, tr_ctor * ctor ) { - char * path; + uint8_t flag = 0; + const char * str; struct dirdata * stuff; GtkWidget * wind; + GtkWidget * v; + GtkWidget * w; stuff = g_new0( struct dirdata, 1 ); - stuff->core = core; - if( NULL != files ) - { - stuff->files = dupstrlist( files ); - } - if( NULL != data ) - { - stuff->data = g_new( uint8_t, size ); - memcpy( stuff->data, data, size ); - stuff->size = size; - } - stuff->action = act; - stuff->paused = paused; + stuff->core = core; + stuff->ctor = ctor; + stuff->files = dupstrlist( files ); g_object_weak_ref( G_OBJECT( core ), promptdirnocore, stuff ); @@ -266,9 +141,30 @@ NULL ); gtk_file_chooser_set_local_only( GTK_FILE_CHOOSER( wind ), TRUE ); gtk_file_chooser_set_select_multiple( GTK_FILE_CHOOSER( wind ), FALSE ); - path = getdownloaddir( ); - gtk_file_chooser_set_filename( GTK_FILE_CHOOSER( wind ), path ); - g_free( path ); + if( tr_ctorGetDestination( ctor, TR_FORCE, &str ) ) + g_assert_not_reached( ); + if( !gtk_file_chooser_set_filename( GTK_FILE_CHOOSER( wind ), str ) ) + g_warning( "couldn't set destination '%s'", str ); + + v = gtk_vbox_new( FALSE, GUI_PAD ); + + flag = 0; + w = gtk_check_button_new_with_mnemonic( _( "_Delete original torrent file" ) ); + 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 ); + + flag = 1; + w = gtk_check_button_new_with_mnemonic( _( "_Start when added" ) ); + g_signal_connect( w, "toggled", G_CALLBACK( startToggled ), ctor ); + if( tr_ctorGetPaused( ctor, TR_FORCE, &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 ); + + gtk_file_chooser_set_extra_widget( GTK_FILE_CHOOSER( wind ), v ); stuff->widget = wind; @@ -278,57 +174,6 @@ gtk_widget_show_all(wind); } -void -promptdirnocore( gpointer gdata, GObject * core SHUTUP ) -{ - struct dirdata * stuff = gdata; - - /* prevent the response callback from trying to remove the weak - reference which no longer exists */ - stuff->core = NULL; - - gtk_dialog_response( GTK_DIALOG( stuff->widget ), GTK_RESPONSE_NONE ); -} - -static void -promptresp( GtkWidget * widget, gint resp, gpointer data ) -{ - struct dirdata * stuff; - char * dir; - GList * ii; - - stuff = data; - - if( GTK_RESPONSE_ACCEPT == resp ) - { - dir = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER( widget ) ); - /* it seems that we will always get a directory */ - g_assert( NULL != dir ); - for( ii = g_list_first( stuff->files ); NULL != ii; ii = ii->next ) - { - tr_core_add_dir( stuff->core, ii->data, dir, - stuff->action, stuff->paused ); - } - if( NULL != stuff->data ) - { - tr_core_add_data_dir( stuff->core, stuff->data, stuff->size, dir, - stuff->paused ); - } - tr_core_torrents_added( stuff->core ); - g_free( dir ); - } - - if( NULL != stuff->core ) - { - g_object_weak_unref( G_OBJECT( stuff->core ), promptdirnocore, stuff ); - } - - freestrlist( stuff->files ); - g_free( stuff->data ); - g_free( stuff ); - gtk_widget_destroy( widget ); -} - /*** **** ***/ @@ -345,9 +190,11 @@ quitresp( GtkWidget * widget, int response, gpointer data ) { struct quitdata * stuff = data; + GtkToggleButton * tb = GTK_TOGGLE_BUTTON( stuff->dontask ); - pref_flag_set( PREF_KEY_ASKQUIT, - !gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(stuff->dontask) ) ); + tr_core_set_pref_bool( stuff->core, + PREF_KEY_ASKQUIT, + !gtk_toggle_button_get_active( tb ) ); if( response == GTK_RESPONSE_ACCEPT ) stuff->func( stuff->cbdata ); @@ -427,3 +274,105 @@ gtk_widget_show_all( wind ); } + +/*** +**** +***/ + +struct DeleteData +{ + GtkWidget * delete_files_tb; + GList * torrents; + TrCore * core; +}; + +static void +deleteDownloadedToggled( GtkToggleButton * tb, gpointer warn ) +{ + GtkWidget * w = GTK_WIDGET( warn ); + + if( gtk_toggle_button_get_active( tb ) ) + gtk_widget_show( w ); + else + gtk_widget_hide( w ); +} + +static void +deleteResponse( GtkDialog * dialog, gint response, gpointer gdata ) +{ + struct DeleteData * data = gdata; + const int del = response == GTK_RESPONSE_YES; + const int deleteFiles = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( data->delete_files_tb ) ); + GList * l; + + for( l=data->torrents; l!=NULL; l=l->next ) + { + TrTorrent * gtor = TR_TORRENT( l->data ); + + if( del ) + tr_core_remove_torrent( data->core, gtor, deleteFiles ); + else + g_object_unref( G_OBJECT( gtor ) ); + } + + gtk_widget_destroy( GTK_WIDGET( dialog ) ); + g_list_free( data->torrents ); + g_free( data ); +} + +void +confirmDelete( GtkWindow * parent, + TrCore * core, + GList * torrents ) +{ + char buf[1024]; + GtkWidget * t; + GtkWidget * d; + GtkWidget * w; + GtkWidget * warn; + struct DeleteData * dd = g_new0( struct DeleteData, 1 ); + + dd->core = core; + dd->torrents = torrents; + + d = gtk_dialog_new_with_buttons( _( "Remove Torrent" ), + parent, + GTK_DIALOG_DESTROY_WITH_PARENT|GTK_DIALOG_NO_SEPARATOR, + GTK_STOCK_NO, GTK_RESPONSE_NO, + GTK_STOCK_YES, GTK_RESPONSE_YES, + NULL ); + g_signal_connect( d, "response", G_CALLBACK( deleteResponse ), dd ); + + t = gtk_table_new( 3, 2, FALSE ); + gtk_container_set_border_width( GTK_CONTAINER( t ), GUI_PAD_BIG ); + gtk_table_set_col_spacing( GTK_TABLE( t ), 0, GUI_PAD_BIG ); + gtk_table_set_row_spacings( GTK_TABLE( t ), GUI_PAD_BIG ); + + w = gtk_image_new_from_stock( GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_DIALOG ); + gtk_table_attach( GTK_TABLE( t ), w, 0, 1, 0, 3, GTK_FILL, GTK_FILL, 0, 0 ); + gtk_widget_show( w ); + + g_snprintf( buf, sizeof(buf), "%s", _( "Remove the selected torrent(s)?" ) ); + w = gtk_label_new( buf ); + gtk_misc_set_alignment( GTK_MISC( w ), 0.0, 0.5 ); + gtk_label_set_use_markup( GTK_LABEL( w ), TRUE ); + gtk_table_attach( GTK_TABLE( t ), w, 1, 2, 0, 1, GTK_FILL, 0, 0, 0 ); + gtk_widget_show( w ); + + g_snprintf( buf, sizeof( buf ), "%s", + _( "All downloaded files for this torrent will be deleted!" ) ); + warn = gtk_label_new( buf ); + gtk_label_set_use_markup( GTK_LABEL( warn ), TRUE ); + gtk_table_attach( GTK_TABLE( t ), warn, 1, 2, 2, 3, GTK_FILL, 0, 0, 0 ); + + w = gtk_check_button_new_with_mnemonic( _( "Delete _downloaded files" ) ); + dd->delete_files_tb = w; + gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( w ), FALSE ); + gtk_table_attach( GTK_TABLE( t ), w, 1, 2, 1, 2, GTK_FILL, 0, 0, 0 ); + g_signal_connect( w, "toggled", G_CALLBACK(deleteDownloadedToggled), warn ); + gtk_widget_show( w ); + + gtk_widget_show( t ); + gtk_container_add( GTK_CONTAINER( GTK_DIALOG( d )->vbox ), t ); + gtk_widget_show( d ); +} --- transmission-1.06.orig/gtk/tr_core.h +++ transmission-1.06/gtk/tr_core.h @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: tr_core.h 4404 2008-01-01 17:20:20Z charles $ + * $Id$ * * Copyright (c) 2007-2008 Transmission authors and contributors * @@ -30,62 +30,54 @@ #include #include -#include #include +#include "tr_torrent.h" #include "util.h" -#define TR_CORE_TYPE ( tr_core_get_type() ) +#define TR_CORE_TYPE (tr_core_get_type()) +#define TR_CORE(o) G_TYPE_CHECK_INSTANCE_CAST((o),TR_CORE_TYPE,TrCore) +#define TR_IS_CORE(o) G_TYPE_CHECK_INSTANCE_TYPE((o),TR_CORE_TYPE) +#define TR_CORE_CLASS(k) G_TYPE_CHECK_CLASS_CAST((k),TR_CORE_TYPE,TrCoreClass) +#define TR_IS_CORE_CLASS(k) G_TYPE_CHECK_CLASS_TYPE((k),TR_CORE_TYPE) +#define TR_CORE_GET_CLASS(o) G_TYPE_INSTANCE_GET_CLASS((o),TR_CORE_TYPE,TrCoreClass) -#define TR_CORE( obj ) \ - ( G_TYPE_CHECK_INSTANCE_CAST( (obj), TR_CORE_TYPE, TrCore ) ) - -#define TR_CORE_CLASS( class ) \ - ( G_TYPE_CHECK_CLASS_CAST( (class), TR_CORE_TYPE, TrCoreClass ) ) - -#define TR_IS_CORE( obj ) \ - ( G_TYPE_CHECK_INSTANCE_TYPE( (obj), TR_CORE_TYPE ) ) - -#define TR_IS_CORE_CLASS( class ) \ - ( G_TYPE_CHECK_CLASS_TYPE( (class), TR_CORE_TYPE ) ) - -#define TR_CORE_GET_CLASS( obj ) \ - ( G_TYPE_INSTANCE_GET_CLASS( (obj), TR_CORE_TYPE, TrCoreClass ) ) - -typedef struct _TrCore TrCore; -typedef struct _TrCoreClass TrCoreClass; - -/* treat the contents of this structure as private */ -struct _TrCore +struct core_stats { - GObject parent; - GtkTreeModel * model; - tr_handle * handle; - int nextid; - gboolean quitting; - gboolean disposed; + int downloadCount; + int seedingCount; + float clientDownloadSpeed; + float clientUploadSpeed; }; -struct _TrCoreClass +typedef struct TrCore +{ + GObject parent; + struct TrCorePrivate * priv; +} +TrCore; + +typedef struct TrCoreClass { - GObjectClass parent; + GObjectClass parent; + /* "error" signal: void handler( TrCore *, enum tr_core_err, const char *, gpointer ) */ - int errsig; + int errsig; + /* "directory-prompt" signal: - void handler( TrCore *, GList *, enum tr_torrent_action, gboolean, - gpointer ) */ - int promptsig; - /* "directory-prompt-data" signal: - void handler( TrCore *, uint8_t *, size_t, gboolean, gpointer ) */ - int promptdatasig; + void handler( TrCore *, GList *, gpointer ctor, gpointer userData ) */ + int promptsig; + /* "quit" signal: void handler( TrCore *, gpointer ) */ - int quitsig; + int quitsig; + /* "prefs-changed" signal: void handler( TrCore *, int, gpointer ) */ - int prefsig; -}; + int prefsig; +} +TrCoreClass; enum tr_core_err { @@ -95,82 +87,82 @@ TR_CORE_ERR_SAVE_STATE /* error saving state */ }; -GType -tr_core_get_type( void ); +GType tr_core_get_type( void ); -TrCore * -tr_core_new( void ); +TrCore * tr_core_new( void ); /* Return the model used without incrementing the reference count */ -GtkTreeModel * -tr_core_model( TrCore * self ); +GtkTreeModel * tr_core_model( TrCore * self ); + +tr_handle * tr_core_handle( TrCore * self ); -/* Returns the libtransmission handle */ -tr_handle * -tr_core_handle( TrCore * self ); - -/* Load saved state, return number of torrents added. May trigger one - or more "error" signals with TR_CORE_ERR_ADD_TORRENT */ -int -tr_core_load( TrCore * self, gboolean forcepaused ); - -/* Any the tr_core_add functions below may trigger an "error" signal - with TR_CORE_ERR_ADD_TORRENT */ - -/* Add the torrent at the given path */ -gboolean -tr_core_add( TrCore * self, const char * path, enum tr_torrent_action act, - gboolean paused ); - -/* Add the torrent at the given path with the given download directory */ -gboolean -tr_core_add_dir( TrCore * self, const char * path, const char * dir, - enum tr_torrent_action act, gboolean paused ); - -/* Add a list of torrents with the given paths */ -int -tr_core_add_list( TrCore * self, GList * paths, enum tr_torrent_action act, - gboolean paused ); - -/* Add the torrent data in the given buffer */ -gboolean -tr_core_add_data( TrCore * self, uint8_t * data, size_t size, gboolean paused ); - -/* Add the torrent data in the given buffer with the given download directory */ -gboolean -tr_core_add_data_dir( TrCore * self, uint8_t * data, size_t size, - const char * dir, gboolean paused ); - -/* Save state, update model, and signal the end of a torrent cluster */ -void -tr_core_torrents_added( TrCore * self ); - -/* remove a torrent, waiting for it to pause if necessary */ -void -tr_core_delete_torrent( TrCore * self, GtkTreeIter * iter /* XXX */ ); +const struct core_stats* tr_core_get_stats( const TrCore * self ); + +/****** +******* +******/ + +/** + * Load saved state and return number of torrents added. + * May trigger one or more "error" signals with TR_CORE_ERR_ADD_TORRENT + */ +int tr_core_load( TrCore * self, gboolean forcepaused ); + +/** + * Add a torrent. + * May trigger an "error" signal with TR_CORE_ERR_ADD_TORRENT + * Caller must free the ctor. + */ +void tr_core_add_ctor( TrCore * self, tr_ctor * ctor ); + +/** + * Add a list of torrents. + * May trigger one or more "error" signals with TR_CORE_ERR_ADD_TORRENT + */ +void tr_core_add_list( TrCore * self, + GList * torrentFiles, + tr_ctor * ctor ); + +/** + * Add a torrent. + */ +void tr_core_add_torrent( TrCore*, TrTorrent* ); + +/** + * Notifies listeners that torrents have been added. + * This should be called after one or more tr_core_add*() calls. + */ +void tr_core_torrents_added( TrCore * self ); + +/****** +******* +******/ + +void tr_core_delete_torrent( TrCore * self, GtkTreeIter * iter ); + +void tr_core_remove_torrent( TrCore * self, TrTorrent * gtor, int deleteFiles ); /* update the model with current torrent status */ -void -tr_core_update( TrCore * self ); +void tr_core_update( TrCore * self ); /* emit the "quit" signal */ -void -tr_core_quit( TrCore * self ); +void tr_core_quit( TrCore * self ); /* Set a preference value, save the prefs file, and emit the "prefs-changed" signal */ -void -tr_core_set_pref( TrCore * self, const char * key, const char * val ); +void tr_core_set_pref( TrCore * self, const char * key, const char * val ); /* Set a boolean preference value, save the prefs file, and emit the "prefs-changed" signal */ -void -tr_core_set_pref_bool( TrCore * self, const char * key, gboolean val ); +void tr_core_set_pref_bool( TrCore * self, const char * key, gboolean val ); /* Set an integer preference value, save the prefs file, and emit the "prefs-changed" signal */ -void -tr_core_set_pref_int( TrCore * self, const char * key, int val ); +void tr_core_set_pref_int( TrCore * self, const char * key, int val ); + +/** +*** +**/ /* column names for the model used to store torrent information */ /* keep this in sync with the type array in tr_core_init() in tr_core.c */ --- transmission-1.06.orig/gtk/main.c +++ transmission-1.06/gtk/main.c @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: main.c 5027 2008-02-13 16:31:34Z charles $ + * $Id$ * * Copyright (c) 2005-2008 Transmission authors and contributors * @@ -43,12 +43,13 @@ #include "actions.h" #include "conf.h" +#include "details.h" #include "dialogs.h" #include "ipc.h" #include "makemeta-ui.h" #include "msgwin.h" +#include "open-dialog.h" #include "stats.h" -#include "torrent-inspector.h" #include "tr_core.h" #include "tr_icon.h" #include "tr_prefs.h" @@ -146,17 +147,13 @@ coreerr( TrCore * core, enum tr_core_err code, const char * msg, gpointer gdata ); static void -coreprompt( TrCore *, GList *, enum tr_torrent_action, gboolean, gpointer ); -static void -corepromptdata( TrCore *, uint8_t *, size_t, gboolean, gpointer ); +coreprompt( TrCore *, GList *, gpointer, gpointer ); static void initializeFromPrefs( struct cbdata * cbdata ); static void prefschanged( TrCore * core, const char * key, gpointer data ); static gboolean updatemodel(gpointer gdata); -static GList * -getselection( struct cbdata * cbdata ); static void setupsighandlers(void); @@ -329,10 +326,8 @@ static void appsetup( TrWindow * wind, GList * args, struct cbdata * cbdata, - gboolean paused, gboolean minimized ) + gboolean forcepause, gboolean minimized ) { - enum tr_torrent_action action; - /* fill out cbdata */ cbdata->wind = NULL; cbdata->icon = NULL; @@ -349,8 +344,6 @@ g_signal_connect( cbdata->core, "error", G_CALLBACK( coreerr ), cbdata ); g_signal_connect( cbdata->core, "directory-prompt", G_CALLBACK( coreprompt ), cbdata ); - g_signal_connect( cbdata->core, "directory-prompt-data", - G_CALLBACK( corepromptdata ), cbdata ); g_signal_connect_swapped( cbdata->core, "quit", G_CALLBACK( wannaquit ), cbdata ); g_signal_connect( cbdata->core, "prefs-changed", @@ -363,12 +356,14 @@ initializeFromPrefs( cbdata ); /* add torrents from command-line and saved state */ - tr_core_load( cbdata->core, paused ); + tr_core_load( cbdata->core, forcepause ); if( NULL != args ) { - action = tr_prefs_get_action( PREF_KEY_ADDIPC ); - tr_core_add_list( cbdata->core, args, action, paused ); + tr_ctor * ctor = tr_ctorNew( tr_core_handle( cbdata->core ) ); + if( forcepause ) + tr_ctorSetPaused( ctor, TR_FORCE, TRUE ); + tr_core_add_list( cbdata->core, args, ctor ); } tr_core_torrents_added( cbdata->core ); @@ -514,7 +509,7 @@ makeicon( struct cbdata * cbdata ) { if( cbdata->icon == NULL ) - cbdata->icon = tr_icon_new( ); + cbdata->icon = tr_icon_new( cbdata->core ); } static gpointer @@ -625,7 +620,8 @@ GList * paths = NULL; GList * freeme = NULL; -#ifdef DND_DEBUG +#if 0 + int i; char *sele = gdk_atom_name(sel->selection); char *targ = gdk_atom_name(sel->target); char *type = gdk_atom_name(sel->type); @@ -655,7 +651,7 @@ continue; /* decode the filename */ - filename = urldecode( files[i], -1 ); + filename = decode_uri( files[i] ); freeme = g_list_prepend( freeme, filename ); if( !g_utf8_validate( filename, -1, NULL ) ) continue; @@ -683,9 +679,9 @@ /* try to add any torrents we found */ if( paths != NULL ) { - enum tr_torrent_action action = tr_prefs_get_action( PREF_KEY_ADDSTD ); + tr_ctor * ctor = tr_ctorNew( tr_core_handle( data->core ) ); paths = g_list_reverse( paths ); - tr_core_add_list( data->core, paths, action, FALSE ); + tr_core_add_list( data->core, paths, ctor ); tr_core_torrents_added( data->core ); g_list_free( paths ); } @@ -748,22 +744,21 @@ g_assert_not_reached(); } -void -coreprompt( TrCore * core, GList * paths, enum tr_torrent_action act, - gboolean paused, gpointer gdata ) -{ - struct cbdata * cbdata = gdata; - - promptfordir( cbdata->wind, core, paths, NULL, 0, act, paused ); -} - -void -corepromptdata( TrCore * core, uint8_t * data, size_t size, - gboolean paused, gpointer gdata ) +static void +coreprompt( TrCore * core, + GList * paths, + gpointer ctor, + gpointer gdata ) { struct cbdata * cbdata = gdata; - - promptfordir( cbdata->wind, core, NULL, data, size, TR_TOR_LEAVE, paused ); + const int len = g_list_length( paths ); + if( len > 1 ) + promptfordir( cbdata->wind, core, paths, ctor ); + else { + if( len == 1 ) + tr_ctorSetMetainfoFromFile( ctor, paths->data ); + makeaddwind( cbdata->wind, core, ctor ); + } } static void @@ -856,35 +851,6 @@ return TRUE; } -/* returns a GList of GtkTreeRowReferences to each selected row */ -static GList * -getselection( struct cbdata * cbdata ) -{ - GList * rows = NULL; - - if( NULL != cbdata->wind ) - { - GList * l; - GtkTreeSelection *s = tr_window_get_selection(cbdata->wind); - GtkTreeModel * filter_model; - GtkTreeModel * store_model; - rows = gtk_tree_selection_get_selected_rows( s, &filter_model ); - store_model = gtk_tree_model_filter_get_model( - GTK_TREE_MODEL_FILTER( filter_model ) ); - for( l=rows; l!=NULL; l=l->next ) - { - GtkTreePath * path = gtk_tree_model_filter_convert_path_to_child_path( - GTK_TREE_MODEL_FILTER( filter_model ), l->data ); - GtkTreeRowReference * ref = gtk_tree_row_reference_new( store_model, path ); - gtk_tree_path_free( path ); - gtk_tree_path_free( l->data ); - l->data = ref; - } - } - - return rows; -} - static void about ( GtkWindow * parent ) { @@ -939,6 +905,18 @@ } static void +accumulateSelectedTorrents( GtkTreeModel * model, + GtkTreePath * path UNUSED, + GtkTreeIter * iter, + gpointer gdata ) +{ + GList ** data = ( GList** ) gdata; + TrTorrent * tor = NULL; + gtk_tree_model_get( model, iter, MC_TORRENT, &tor, -1 ); + *data = g_list_append( *data, tor ); +} + +static void updateTrackerForeach (GtkTreeModel * model, GtkTreePath * path UNUSED, GtkTreeIter * iter, @@ -1011,9 +989,9 @@ struct cbdata * data = user_data; gboolean changed = FALSE; - if (!strcmp (action_name, "add-torrent")) + if ( !strcmp (action_name, "open-torrent-menu") || !strcmp( action_name, "open-torrent-toolbar" )) { - makeaddwind( data->wind, data->core ); + tr_core_add_list( data->core, NULL, tr_ctorNew( tr_core_handle( data->core ) ) ); } else if (!strcmp (action_name, "show-stats")) { @@ -1049,7 +1027,7 @@ GtkTreeSelection * s = tr_window_get_selection(data->wind); gtk_tree_selection_selected_foreach( s, updateTrackerForeach, data->wind ); } - else if (!strcmp (action_name, "create-torrent")) + else if (!strcmp (action_name, "new-torrent")) { GtkWidget * w = make_meta_ui( GTK_WINDOW( data->wind ), tr_core_handle( data->core ) ); @@ -1057,22 +1035,11 @@ } else if (!strcmp (action_name, "remove-torrent")) { - /* this modifies the model's contents, so can't use foreach */ - GList *l, *sel = getselection( data ); - GtkTreeModel *model = tr_core_model( data->core ); - gtk_tree_selection_unselect_all( tr_window_get_selection( data->wind) ); - for( l=sel; l!=NULL; l=l->next ) - { - GtkTreeIter iter; - GtkTreeRowReference * reference = l->data; - GtkTreePath * path = gtk_tree_row_reference_get_path( reference ); - gtk_tree_model_get_iter( model, &iter, path ); - tr_core_delete_torrent( data->core, &iter ); - gtk_tree_path_free( path ); - gtk_tree_row_reference_free( reference ); - changed = TRUE; - } - g_list_free( sel ); + GList * l = NULL; + GtkTreeSelection * s = tr_window_get_selection(data->wind); + gtk_tree_selection_selected_foreach( s, accumulateSelectedTorrents, &l ); + if( l != NULL ) + confirmDelete( data->wind, data->core, l ); } else if (!strcmp (action_name, "close")) { --- transmission-1.06.orig/autogen.sh +++ transmission-1.06/autogen.sh @@ -0,0 +1,36 @@ +#!/bin/sh +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +ORIGDIR=`pwd` +cd $srcdir +PROJECT=Transmission + +GETTEXTIZE="glib-gettextize" +$GETTEXTIZE --version < /dev/null > /dev/null 2>&1 +if test $? -ne 0; then + GETTEXTIZE="" +fi + +LIBTOOLIZE=libtoolize +if ! libtoolize --help >/dev/null 2>&1 && glibtoolize --help >/dev/null 2>&1 +then + LIBTOOLIZE=glibtoolize +fi +export LIBTOOLIZE + +autoreconf -fi -I m4 + +if test "$GETTEXTIZE"; then + echo "Creating aclocal.m4 ..." + test -r aclocal.m4 || touch aclocal.m4 + echo "Running $GETTEXTIZE... Ignore non-fatal messages." + echo "no" | $GETTEXTIZE --force --copy + echo "Making aclocal.m4 writable ..." + test -r aclocal.m4 && chmod u+w aclocal.m4 + echo "Running intltoolize..." + intltoolize --copy --force --automake +fi + +cd $ORIGDIR || exit $? + --- transmission-1.06.orig/po/POTFILES.in +++ transmission-1.06/po/POTFILES.in @@ -2,16 +2,18 @@ gtk/transmission.desktop.in gtk/actions.c gtk/conf.c +gtk/details.c gtk/dialogs.c +gtk/file-list.c gtk/hig.c gtk/io.c gtk/ipc.c gtk/main.c gtk/makemeta-ui.c gtk/msgwin.c +gtk/open-dialog.c gtk/stats.c gtk/torrent-cell-renderer.c -gtk/torrent-inspector.c gtk/tr_core.c gtk/tr_icon.c gtk/tr_prefs.c