(gtk) clean up yesterday's properties dialog rewrite a bit.

This commit is contained in:
Charles Kerr 2009-04-24 12:05:53 +00:00
parent 85b33e22be
commit 9dc4672ace
3 changed files with 298 additions and 282 deletions

View File

@ -11,21 +11,19 @@
*/
#include <assert.h>
#include <errno.h>
#include <math.h> /* ceil() */
#include <stddef.h>
#include <stdio.h>
#include <stdio.h> /* sscanf */
#include <stdlib.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include <libtransmission/transmission.h>
#include <libtransmission/utils.h> /* tr_httpIsValidURL */
#include <libtransmission/utils.h> /* tr_free */
#include "actions.h"
#include "details.h"
#include "file-list.h"
#include "tr-torrent.h"
#include "tracker-list.h"
#include "hig.h"
#include "util.h"
@ -229,7 +227,8 @@ refreshOptions( struct DetailsImpl * di, tr_torrent ** torrents, int n )
if( baseline != tr_torrentUsesSessionLimits( torrents[i] ) )
break;
if( i == n )
set_togglebutton_if_different( di->honorLimitsCheck, di->honorLimitsCheckTag, baseline );
set_togglebutton_if_different( di->honorLimitsCheck,
di->honorLimitsCheckTag, baseline );
}
/* downLimitedCheck */
@ -240,7 +239,8 @@ refreshOptions( struct DetailsImpl * di, tr_torrent ** torrents, int n )
if( baseline != tr_torrentUsesSpeedLimit( torrents[i], TR_DOWN ) )
break;
if( i == n )
set_togglebutton_if_different( di->downLimitedCheck, di->downLimitedCheckTag, baseline );
set_togglebutton_if_different( di->downLimitedCheck,
di->downLimitedCheckTag, baseline );
}
/* downLimitSpin */
@ -251,7 +251,8 @@ refreshOptions( struct DetailsImpl * di, tr_torrent ** torrents, int n )
if( baseline != tr_torrentGetSpeedLimit( torrents[i], TR_DOWN ) )
break;
if( i == n )
set_int_spin_if_different( di->downLimitSpin, di->downLimitSpinTag, baseline );
set_int_spin_if_different( di->downLimitSpin,
di->downLimitSpinTag, baseline );
}
/* upLimitedCheck */
@ -262,7 +263,8 @@ refreshOptions( struct DetailsImpl * di, tr_torrent ** torrents, int n )
if( baseline != tr_torrentUsesSpeedLimit( torrents[i], TR_UP ) )
break;
if( i == n )
set_togglebutton_if_different( di->upLimitedCheck, di->upLimitedCheckTag, baseline );
set_togglebutton_if_different( di->upLimitedCheck,
di->upLimitedCheckTag, baseline );
}
/* upLimitSpin */
@ -273,7 +275,8 @@ refreshOptions( struct DetailsImpl * di, tr_torrent ** torrents, int n )
if( baseline != tr_torrentGetSpeedLimit( torrents[i], TR_UP ) )
break;
if( i == n )
set_int_spin_if_different( di->upLimitSpin, di->upLimitSpinTag, baseline );
set_int_spin_if_different( di->upLimitSpin,
di->upLimitSpinTag, baseline );
}
/* bandwidthCombo */
@ -284,7 +287,8 @@ refreshOptions( struct DetailsImpl * di, tr_torrent ** torrents, int n )
if( baseline != tr_torrentGetPriority( torrents[i] ) )
break;
if( i == n )
set_int_combo_if_different( di->bandwidthCombo, di->bandwidthComboTag, 0, baseline );
set_int_combo_if_different( di->bandwidthCombo,
di->bandwidthComboTag, 0, baseline );
else
unset_combo( di->bandwidthCombo, di->bandwidthComboTag );
}
@ -318,7 +322,8 @@ refreshOptions( struct DetailsImpl * di, tr_torrent ** torrents, int n )
if( (int)(100*baseline) != (int)(100*tr_torrentGetRatioLimit(torrents[i])) )
break;
if( i == n )
set_double_spin_if_different( di->seedCustomSpin, di->seedCustomSpinTag, baseline );
set_double_spin_if_different( di->seedCustomSpin,
di->seedCustomSpinTag, baseline );
}
}
@ -377,57 +382,58 @@ torrent_set_real( struct DetailsImpl * di, const char * key, double value )
}
static void
up_speed_toggled_cb( GtkToggleButton * tb, gpointer data )
up_speed_toggled_cb( GtkToggleButton * tb, gpointer d )
{
torrent_set_bool( data, "uploadLimited", gtk_toggle_button_get_active( tb ) );
torrent_set_bool( d, "uploadLimited", gtk_toggle_button_get_active( tb ) );
}
static void
down_speed_toggled_cb( GtkToggleButton *tb, gpointer data )
down_speed_toggled_cb( GtkToggleButton *tb, gpointer d )
{
torrent_set_bool( data, "downloadLimited", gtk_toggle_button_get_active( tb ) );
torrent_set_bool( d, "downloadLimited", gtk_toggle_button_get_active( tb ) );
}
static void
global_speed_toggled_cb( GtkToggleButton * tb, gpointer data )
global_speed_toggled_cb( GtkToggleButton * tb, gpointer d )
{
torrent_set_bool( data, "honorsSessionLimits", gtk_toggle_button_get_active( tb ) );
torrent_set_bool( d, "honorsSessionLimits", gtk_toggle_button_get_active( tb ) );
}
#define RATIO_MODE_KEY "ratio-mode"
#define RATIO_KEY "ratio-mode"
static void
ratio_mode_changed_cb( GtkToggleButton * tb, struct DetailsImpl * data )
ratio_mode_changed_cb( GtkToggleButton * tb, struct DetailsImpl * d )
{
if( gtk_toggle_button_get_active( tb ) )
{
const int mode = GPOINTER_TO_INT( g_object_get_data( G_OBJECT( tb ), RATIO_MODE_KEY ) );
torrent_set_int( data, "seedRatioMode", mode );
GObject * o = G_OBJECT( tb );
const int mode = GPOINTER_TO_INT( g_object_get_data( o, RATIO_KEY ) );
torrent_set_int( d, "seedRatioMode", mode );
}
}
static void
up_speed_spun_cb( GtkSpinButton * spin, struct DetailsImpl * di )
up_speed_spun_cb( GtkSpinButton * s, struct DetailsImpl * di )
{
torrent_set_int( di, "uploadLimit", gtk_spin_button_get_value_as_int( spin ) );
torrent_set_int( di, "uploadLimit", gtk_spin_button_get_value_as_int( s ) );
}
static void
down_speed_spun_cb( GtkSpinButton * spin, struct DetailsImpl * di )
down_speed_spun_cb( GtkSpinButton * s, struct DetailsImpl * di )
{
torrent_set_int( di, "downloadLimit", gtk_spin_button_get_value_as_int( spin ) );
torrent_set_int( di, "downloadLimit", gtk_spin_button_get_value_as_int( s ) );
}
static void
ratio_spun_cb( GtkSpinButton * spin, struct DetailsImpl * di )
ratio_spun_cb( GtkSpinButton * s, struct DetailsImpl * di )
{
torrent_set_real( di, "seedRatioLimit", gtk_spin_button_get_value( spin ) );
torrent_set_real( di, "seedRatioLimit", gtk_spin_button_get_value( s ) );
}
static void
max_peers_spun_cb( GtkSpinButton * spin, struct DetailsImpl * di )
max_peers_spun_cb( GtkSpinButton * s, struct DetailsImpl * di )
{
torrent_set_int( di, "peer-limit", gtk_spin_button_get_value( spin ) );
torrent_set_int( di, "peer-limit", gtk_spin_button_get_value( s ) );
}
static char*
@ -448,7 +454,8 @@ get_global_ratio_radiobutton_string( void )
static void
prefsChanged( TrCore * core UNUSED, const char * key, gpointer rb )
{
if( !strcmp( key, TR_PREFS_KEY_RATIO_ENABLED ) || !strcmp( key, TR_PREFS_KEY_RATIO ) )
if( !strcmp( key, TR_PREFS_KEY_RATIO_ENABLED ) ||
!strcmp( key, TR_PREFS_KEY_RATIO ) )
{
char * s = get_global_ratio_radiobutton_string( );
gtk_button_set_label( GTK_BUTTON( rb ), s );
@ -493,7 +500,9 @@ new_priority_combo( struct DetailsImpl * di )
for( i=0; i<(int)G_N_ELEMENTS(items); ++i ) {
GtkTreeIter iter;
gtk_list_store_append( store, &iter );
gtk_list_store_set( store, &iter, 0, items[i].value, 1, _( items[i].text ), -1 );
gtk_list_store_set( store, &iter, 0, items[i].value,
1, _( items[i].text ),
-1 );
}
/* build the widget */
@ -523,87 +532,87 @@ options_page_new( struct DetailsImpl * d )
t = hig_workarea_create( );
hig_workarea_add_section_title( t, &row, _( "Speed" ) );
tb = hig_workarea_add_wide_checkbutton( t, &row, _( "Honor global _limits" ), FALSE );
d->honorLimitsCheck = tb;
tag = g_signal_connect( tb, "toggled", G_CALLBACK( global_speed_toggled_cb ), d );
d->honorLimitsCheckTag = tag;
tb = hig_workarea_add_wide_checkbutton( t, &row, _( "Honor global _limits" ), FALSE );
d->honorLimitsCheck = tb;
tag = g_signal_connect( tb, "toggled", G_CALLBACK( global_speed_toggled_cb ), d );
d->honorLimitsCheckTag = tag;
tb = gtk_check_button_new_with_mnemonic( _( "Limit _download speed (KB/s):" ) );
gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( tb ), FALSE );
d->downLimitedCheck = tb;
tag = g_signal_connect( tb, "toggled", G_CALLBACK( down_speed_toggled_cb ), d );
d->downLimitedCheckTag = tag;
tb = gtk_check_button_new_with_mnemonic( _( "Limit _download speed (KB/s):" ) );
gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( tb ), FALSE );
d->downLimitedCheck = tb;
tag = g_signal_connect( tb, "toggled", G_CALLBACK( down_speed_toggled_cb ), d );
d->downLimitedCheckTag = tag;
w = gtk_spin_button_new_with_range( 1, INT_MAX, 5 );
gtk_spin_button_set_value( GTK_SPIN_BUTTON( w ), i );
tag = g_signal_connect( w, "value-changed", G_CALLBACK( down_speed_spun_cb ), d );
d->downLimitSpinTag = tag;
hig_workarea_add_row_w( t, &row, tb, w, NULL );
d->downLimitSpin = w;
w = gtk_spin_button_new_with_range( 1, INT_MAX, 5 );
gtk_spin_button_set_value( GTK_SPIN_BUTTON( w ), i );
tag = g_signal_connect( w, "value-changed", G_CALLBACK( down_speed_spun_cb ), d );
d->downLimitSpinTag = tag;
hig_workarea_add_row_w( t, &row, tb, w, NULL );
d->downLimitSpin = w;
tb = gtk_check_button_new_with_mnemonic( _( "Limit _upload speed (KB/s):" ) );
d->upLimitedCheck = tb;
tag = g_signal_connect( tb, "toggled", G_CALLBACK( up_speed_toggled_cb ), d );
d->upLimitedCheckTag = tag;
tb = gtk_check_button_new_with_mnemonic( _( "Limit _upload speed (KB/s):" ) );
d->upLimitedCheck = tb;
tag = g_signal_connect( tb, "toggled", G_CALLBACK( up_speed_toggled_cb ), d );
d->upLimitedCheckTag = tag;
w = gtk_spin_button_new_with_range( 1, INT_MAX, 5 );
gtk_spin_button_set_value( GTK_SPIN_BUTTON( w ), i );
tag = g_signal_connect( w, "value-changed", G_CALLBACK( up_speed_spun_cb ), d );
d->upLimitSpinTag = tag;
hig_workarea_add_row_w( t, &row, tb, w, NULL );
d->upLimitSpin = w;
w = gtk_spin_button_new_with_range( 1, INT_MAX, 5 );
gtk_spin_button_set_value( GTK_SPIN_BUTTON( w ), i );
tag = g_signal_connect( w, "value-changed", G_CALLBACK( up_speed_spun_cb ), d );
d->upLimitSpinTag = tag;
hig_workarea_add_row_w( t, &row, tb, w, NULL );
d->upLimitSpin = w;
w = new_priority_combo( d );
hig_workarea_add_row( t, &row, _( "_Bandwidth priority:" ), w, NULL );
d->bandwidthCombo = w;
w = new_priority_combo( d );
hig_workarea_add_row( t, &row, _( "_Bandwidth priority:" ), w, NULL );
d->bandwidthCombo = w;
hig_workarea_add_section_divider( t, &row );
hig_workarea_add_section_title( t, &row, _( "Seed-Until Ratio" ) );
group = NULL;
s = get_global_ratio_radiobutton_string( );
w = gtk_radio_button_new_with_mnemonic( group, s );
tag = g_signal_connect( d->core, "prefs-changed", G_CALLBACK( prefsChanged ), w );
d->prefs_changed_tag = tag;
group = gtk_radio_button_get_group( GTK_RADIO_BUTTON( w ) );
hig_workarea_add_wide_control( t, &row, w );
g_free( s );
g_object_set_data( G_OBJECT( w ), RATIO_MODE_KEY, GINT_TO_POINTER( TR_RATIOLIMIT_GLOBAL ) );
tag = g_signal_connect( w, "toggled", G_CALLBACK( ratio_mode_changed_cb ), d );
d->seedGlobalRadio = w;
d->seedGlobalRadioTag = tag;
group = NULL;
s = get_global_ratio_radiobutton_string( );
w = gtk_radio_button_new_with_mnemonic( group, s );
tag = g_signal_connect( d->core, "prefs-changed", G_CALLBACK( prefsChanged ), w );
d->prefs_changed_tag = tag;
group = gtk_radio_button_get_group( GTK_RADIO_BUTTON( w ) );
hig_workarea_add_wide_control( t, &row, w );
g_free( s );
g_object_set_data( G_OBJECT( w ), RATIO_KEY, GINT_TO_POINTER( TR_RATIOLIMIT_GLOBAL ) );
tag = g_signal_connect( w, "toggled", G_CALLBACK( ratio_mode_changed_cb ), d );
d->seedGlobalRadio = w;
d->seedGlobalRadioTag = tag;
w = gtk_radio_button_new_with_mnemonic( group, _( "Seed _regardless of ratio" ) );
group = gtk_radio_button_get_group( GTK_RADIO_BUTTON( w ) );
hig_workarea_add_wide_control( t, &row, w );
g_object_set_data( G_OBJECT( w ), RATIO_MODE_KEY, GINT_TO_POINTER( TR_RATIOLIMIT_UNLIMITED ) );
tag = g_signal_connect( w, "toggled", G_CALLBACK( ratio_mode_changed_cb ), d );
d->seedForeverRadio = w;
d->seedForeverRadioTag = tag;
w = gtk_radio_button_new_with_mnemonic( group, _( "Seed _regardless of ratio" ) );
group = gtk_radio_button_get_group( GTK_RADIO_BUTTON( w ) );
hig_workarea_add_wide_control( t, &row, w );
g_object_set_data( G_OBJECT( w ), RATIO_KEY, GINT_TO_POINTER( TR_RATIOLIMIT_UNLIMITED ) );
tag = g_signal_connect( w, "toggled", G_CALLBACK( ratio_mode_changed_cb ), d );
d->seedForeverRadio = w;
d->seedForeverRadioTag = tag;
h = gtk_hbox_new( FALSE, GUI_PAD );
w = gtk_radio_button_new_with_mnemonic( group, _( "_Stop seeding when a torrent's ratio reaches" ) );
d->seedCustomRadio = w;
g_object_set_data( G_OBJECT( w ), RATIO_MODE_KEY, GINT_TO_POINTER( TR_RATIOLIMIT_SINGLE ) );
tag = g_signal_connect( w, "toggled", G_CALLBACK( ratio_mode_changed_cb ), d );
d->seedCustomRadioTag = tag;
group = gtk_radio_button_get_group( GTK_RADIO_BUTTON( w ) );
gtk_box_pack_start( GTK_BOX( h ), w, FALSE, FALSE, 0 );
w = gtk_spin_button_new_with_range( 0.5, INT_MAX, .05 );
gtk_spin_button_set_digits( GTK_SPIN_BUTTON( w ), 2 );
tag = g_signal_connect( w, "value-changed", G_CALLBACK( ratio_spun_cb ), d );
gtk_box_pack_start( GTK_BOX( h ), w, FALSE, FALSE, 0 );
hig_workarea_add_wide_control( t, &row, h );
d->seedCustomSpin = w;
d->seedCustomSpinTag = tag;
h = gtk_hbox_new( FALSE, GUI_PAD );
w = gtk_radio_button_new_with_mnemonic( group, _( "_Stop seeding when a torrent's ratio reaches" ) );
d->seedCustomRadio = w;
g_object_set_data( G_OBJECT( w ), RATIO_KEY, GINT_TO_POINTER( TR_RATIOLIMIT_SINGLE ) );
tag = g_signal_connect( w, "toggled", G_CALLBACK( ratio_mode_changed_cb ), d );
d->seedCustomRadioTag = tag;
group = gtk_radio_button_get_group( GTK_RADIO_BUTTON( w ) );
gtk_box_pack_start( GTK_BOX( h ), w, FALSE, FALSE, 0 );
w = gtk_spin_button_new_with_range( 0.5, INT_MAX, .05 );
gtk_spin_button_set_digits( GTK_SPIN_BUTTON( w ), 2 );
tag = g_signal_connect( w, "value-changed", G_CALLBACK( ratio_spun_cb ), d );
gtk_box_pack_start( GTK_BOX( h ), w, FALSE, FALSE, 0 );
hig_workarea_add_wide_control( t, &row, h );
d->seedCustomSpin = w;
d->seedCustomSpinTag = tag;
hig_workarea_add_section_divider( t, &row );
hig_workarea_add_section_title( t, &row, _( "Peer Connections" ) );
w = gtk_spin_button_new_with_range( 1, 3000, 5 );
hig_workarea_add_row( t, &row, _( "_Maximum peers:" ), w, w );
g_signal_connect( w, "value-changed", G_CALLBACK( max_peers_spun_cb ), d );
d->maxPeersSpin = w;
w = gtk_spin_button_new_with_range( 1, 3000, 5 );
hig_workarea_add_row( t, &row, _( "_Maximum peers:" ), w, w );
g_signal_connect( w, "value-changed", G_CALLBACK( max_peers_spun_cb ), d );
d->maxPeersSpin = w;
hig_workarea_finish( t, &row );
return t;
@ -688,7 +697,8 @@ refreshActivity( struct DetailsImpl * di, tr_torrent ** torrents, int n )
tr_strlsize( buf2, haveValid, sizeof( buf2 ) );
i = (int) ceil( verifiedPieces );
g_snprintf( buf, sizeof( buf ), ngettext( "%1$s (%2$s verified in %3$d piece)",
"%1$s (%2$s verified in %3$d pieces)", verifiedPieces ),
"%1$s (%2$s verified in %3$d pieces)",
verifiedPieces ),
buf1, buf2, i );
str = buf;
}
@ -735,13 +745,13 @@ refreshActivity( struct DetailsImpl * di, tr_torrent ** torrents, int n )
if( n <= 0 )
str = none;
else {
uint64_t downloadedEver = 0;
uint64_t uploadedEver = 0;
uint64_t up = 0;
uint64_t down = 0;
for( i=0; i<n; ++i ) {
downloadedEver += stats[i]->downloadedEver;
uploadedEver += stats[i]->uploadedEver;
up += stats[i]->uploadedEver;
down += stats[i]->downloadedEver;
}
tr_strlratio( buf, tr_getRatio( uploadedEver, downloadedEver ), sizeof( buf ) );
tr_strlratio( buf, tr_getRatio( up, down ), sizeof( buf ) );
str = buf;
}
gtk_label_set_text( GTK_LABEL( di->ratio_lb ), str );
@ -931,10 +941,12 @@ refreshInfo( struct DetailsImpl * di, tr_torrent ** torrents, int n )
for( i=1; i<n; ++i )
if( baseline != infos[i]->isPrivate )
break;
if( i==n )
str = baseline ? _( "Private to this tracker -- PEX disabled" ) : _( "Public torrent" );
else
if( i!=n )
str = mixed;
else if( baseline )
str = _( "Private to this tracker -- PEX disabled" );
else
str = _( "Public torrent" );
}
gtk_label_set_text( GTK_LABEL( di->privacy_lb ), str );
@ -944,11 +956,9 @@ refreshInfo( struct DetailsImpl * di, tr_torrent ** torrents, int n )
str = "";
else {
const char * baseline = infos[0]->comment ? infos[0]->comment : "";
for( i=1; i<n; ++i ) {
const char * tmp = infos[i]->comment ? infos[i]->comment : "";
if( strcmp( baseline, tmp ) )
for( i=1; i<n; ++i )
if( strcmp( baseline, infos[i]->comment ? infos[i]->comment : "" ) )
break;
}
if( i==n )
str = baseline;
else
@ -962,11 +972,9 @@ refreshInfo( struct DetailsImpl * di, tr_torrent ** torrents, int n )
str = none;
else {
const char * baseline = infos[0]->creator ? infos[0]->creator : "";
for( i=1; i<n; ++i ) {
const char * tmp = infos[i]->creator ? infos[i]->creator : "";
if( strcmp( baseline, tmp ) )
for( i=1; i<n; ++i )
if( strcmp( baseline, infos[i]->creator ? infos[i]->creator : "" ) )
break;
}
if( i==n )
str = baseline;
else
@ -1021,6 +1029,8 @@ refreshInfo( struct DetailsImpl * di, tr_torrent ** torrents, int n )
else
str = mixed;
gtk_label_set_text( GTK_LABEL( di->torrentfile_lb ), str );
g_free( infos );
}
static GtkWidget*
@ -1098,9 +1108,10 @@ info_page_new( struct DetailsImpl * di )
enum
{
WEBSEED_COL_KEY,
WEBSEED_COL_WAS_UPDATED,
WEBSEED_COL_URL,
WEBSEED_COL_DOWNLOAD_RATE,
WEBSEED_COL_WAS_UPDATED,
N_WEBSEED_COLS
};
@ -1119,13 +1130,16 @@ static GtkListStore*
webseed_model_new( void )
{
return gtk_list_store_new( N_WEBSEED_COLS,
G_TYPE_STRING,
G_TYPE_FLOAT,
G_TYPE_BOOLEAN );
G_TYPE_STRING, /* key */
G_TYPE_BOOLEAN, /* was-updated */
G_TYPE_STRING, /* url */
G_TYPE_FLOAT); /* download rate */
}
enum
{
PEER_COL_KEY,
PEER_COL_WAS_UPDATED,
PEER_COL_ADDRESS,
PEER_COL_ADDRESS_COLLATED,
PEER_COL_DOWNLOAD_RATE_DOUBLE,
@ -1136,8 +1150,6 @@ enum
PEER_COL_PROGRESS,
PEER_COL_IS_ENCRYPTED,
PEER_COL_STATUS,
PEER_COL_WAS_UPDATED,
PEER_COL_KEY,
N_PEER_COLS
};
@ -1162,6 +1174,8 @@ static GtkListStore*
peer_store_new( void )
{
return gtk_list_store_new( N_PEER_COLS,
G_TYPE_STRING, /* key */
G_TYPE_BOOLEAN, /* was-updated */
G_TYPE_STRING, /* address */
G_TYPE_STRING, /* collated address */
G_TYPE_FLOAT, /* download speed float */
@ -1171,30 +1185,31 @@ peer_store_new( void )
G_TYPE_STRING, /* client */
G_TYPE_INT, /* progress [0..100] */
G_TYPE_BOOLEAN, /* isEncrypted */
G_TYPE_STRING, /* flagString */
G_TYPE_BOOLEAN, /* was-updated */
G_TYPE_STRING); /* key */
G_TYPE_STRING); /* flagString */
}
static void
init_peer( GtkListStore * store, GtkTreeIter * iter, const char * key, const tr_peer_stat * peer )
init_peer( GtkListStore * store,
GtkTreeIter * iter,
const char * key,
const tr_peer_stat * peer )
{
int quads[4];
char upload_speed[128];
int q[4];
char up_speed[128];
char down_speed[128];
char collated_name[128];
char download_speed[128];
const char * client = peer->client;
if( !client || !strcmp( client, "Unknown Client" ) )
client = "";
tr_strlspeed( upload_speed, peer->rateToPeer, sizeof( upload_speed ) );
tr_strlspeed( download_speed, peer->rateToClient, sizeof( download_speed ) );
if( sscanf( peer->addr, "%d.%d.%d.%d", quads, quads+1, quads+2, quads+3 ) != 4 )
tr_strlspeed( up_speed, peer->rateToPeer, sizeof( up_speed ) );
tr_strlspeed( down_speed, peer->rateToClient, sizeof( down_speed ) );
if( sscanf( peer->addr, "%d.%d.%d.%d", q, q+1, q+2, q+3 ) != 4 )
g_strlcpy( collated_name, peer->addr, sizeof( collated_name ) );
else
g_snprintf( collated_name, sizeof( collated_name ),
"%03d.%03d.%03d.%03d", quads[0], quads[1], quads[2], quads[3] );
"%03d.%03d.%03d.%03d", q[0], q[1], q[2], q[3] );
gtk_list_store_set( store, iter,
PEER_COL_ADDRESS, peer->addr,
@ -1212,89 +1227,39 @@ render_encrypted( GtkTreeViewColumn * column UNUSED,
GtkTreeIter * iter,
gpointer data UNUSED )
{
gboolean is_encrypted = FALSE;
gboolean b = FALSE;
gtk_tree_model_get( tree_model, iter, PEER_COL_IS_ENCRYPTED, &is_encrypted, -1 );
gtk_tree_model_get( tree_model, iter, PEER_COL_IS_ENCRYPTED, &b, -1 );
g_object_set( renderer, "xalign", (gfloat)0.0,
"yalign", (gfloat)0.5,
"stock-id", ( is_encrypted ? "transmission-lock" : NULL ),
"stock-id", ( b ? "transmission-lock" : NULL ),
NULL );
}
#if GTK_CHECK_VERSION( 2,12,0 )
static gboolean
onPeerViewQueryTooltip( GtkWidget * widget,
gint x,
gint y,
gboolean keyboard_tip,
GtkTooltip * tooltip,
gpointer user_data UNUSED )
{
gboolean show_tip = FALSE;
GtkTreeModel * model;
GtkTreeIter iter;
if( gtk_tree_view_get_tooltip_context( GTK_TREE_VIEW( widget ),
&x, &y, keyboard_tip,
&model, NULL, &iter ) )
{
const char * pch;
char * str = NULL;
GString * gstr = g_string_new( NULL );
gtk_tree_model_get( model, &iter, PEER_COL_STATUS, &str, -1 );
for( pch = str; pch && *pch; ++pch )
{
const char * txt = NULL;
switch( *pch )
{
case 'O': txt = _( "Optimistic unchoke" ); break;
case 'D': txt = _( "Downloading from this peer" ); break;
case 'd': txt = _( "We would download from this peer if they would let us" ); break;
case 'U': txt = _( "Uploading to peer" ); break;
case 'u': txt = _( "We would upload to this peer if they asked" ); break;
case 'K': txt = _( "Peer has unchoked us, but we're not interested" ); break;
case '?': txt = _( "We unchoked this peer, but they're not interested" ); break;
case 'E': txt = _( "Encrypted connection" ); break;
case 'X': txt = _( "Peer was discovered through Peer Exchange (PEX)" ); break;
case 'I': txt = _( "Peer is an incoming connection" ); break;
}
if( txt )
g_string_append_printf( gstr, "%c: %s\n", *pch, txt );
}
if( gstr->len ) /* remove the last linefeed */
g_string_set_size( gstr, gstr->len - 1 );
gtk_tooltip_set_text( tooltip, gstr->str );
g_string_free( gstr, TRUE );
g_free( str );
show_tip = TRUE;
}
return show_tip;
}
#endif
static void
refreshPeerRow( GtkListStore * store, GtkTreeIter * iter, const tr_peer_stat * peer )
refreshPeerRow( GtkListStore * store,
GtkTreeIter * iter,
const tr_peer_stat * peer )
{
char upload_speed[128];
char download_speed[128];
char up_speed[128];
char down_speed[128];
if( peer->rateToPeer > 0.01 )
tr_strlspeed( upload_speed, peer->rateToPeer, sizeof( upload_speed ) );
tr_strlspeed( up_speed, peer->rateToPeer, sizeof( up_speed ) );
else
*upload_speed = '\0';
*up_speed = '\0';
if( peer->rateToClient > 0.01 )
tr_strlspeed( download_speed, peer->rateToClient, sizeof( download_speed ) );
tr_strlspeed( down_speed, peer->rateToClient, sizeof( down_speed ) );
else
*download_speed = '\0';
*down_speed = '\0';
gtk_list_store_set( store, iter,
PEER_COL_PROGRESS, (int)( 100.0 * peer->progress ),
PEER_COL_DOWNLOAD_RATE_DOUBLE, peer->rateToClient,
PEER_COL_DOWNLOAD_RATE_STRING, download_speed,
PEER_COL_DOWNLOAD_RATE_STRING, down_speed,
PEER_COL_UPLOAD_RATE_DOUBLE, peer->rateToPeer,
PEER_COL_UPLOAD_RATE_STRING, upload_speed,
PEER_COL_UPLOAD_RATE_STRING, up_speed,
PEER_COL_STATUS, peer->flagStr,
PEER_COL_WAS_UPDATED, TRUE,
-1 );
@ -1306,7 +1271,9 @@ refreshPeerList( struct DetailsImpl * di, tr_torrent ** torrents, int n )
int i;
int * peerCount;
GtkTreeIter iter;
GtkTreeModel * model;
GHashTable * hash = di->peer_hash;
GtkListStore * store = di->peer_store;
GtkTreeModel * model = GTK_TREE_MODEL( store );
struct tr_peer_stat ** peers;
/* step 1: get all the peers */
@ -1316,9 +1283,9 @@ refreshPeerList( struct DetailsImpl * di, tr_torrent ** torrents, int n )
peers[i] = tr_torrentPeers( torrents[i], &peerCount[i] );
/* step 2: mark all the peers in the list as not-updated */
model = GTK_TREE_MODEL( di->peer_store );
model = GTK_TREE_MODEL( store );
if( gtk_tree_model_get_iter_first( model, &iter ) ) do
gtk_list_store_set( di->peer_store, &iter, PEER_COL_WAS_UPDATED, FALSE, -1 );
gtk_list_store_set( store, &iter, PEER_COL_WAS_UPDATED, FALSE, -1 );
while( gtk_tree_model_iter_next( model, &iter ) );
/* step 3: add any new peers */
@ -1329,13 +1296,14 @@ refreshPeerList( struct DetailsImpl * di, tr_torrent ** torrents, int n )
const tr_peer_stat * s = &peers[i][j];
char key[128];
g_snprintf( key, sizeof( key ), "%d.%s", tr_torrentId(tor), s->addr );
if( g_hash_table_lookup( di->peer_hash, key ) == NULL ) {
if( g_hash_table_lookup( hash, key ) == NULL ) {
GtkTreePath * path;
gtk_list_store_append( di->peer_store, &iter );
init_peer( di->peer_store, &iter, key, s );
gtk_list_store_append( store, &iter );
init_peer( store, &iter, key, s );
/* fprintf( stderr, "adding peer key %s\n", key ); */
path = gtk_tree_model_get_path( model, &iter );
g_hash_table_insert( di->peer_hash, g_strdup( key ), gtk_tree_row_reference_new( model, path ) );
g_hash_table_insert( hash, g_strdup( key ),
gtk_tree_row_reference_new( model, path ) );
gtk_tree_path_free( path );
}
}
@ -1351,31 +1319,31 @@ refreshPeerList( struct DetailsImpl * di, tr_torrent ** torrents, int n )
GtkTreeRowReference * ref;
GtkTreePath * path;
g_snprintf( key, sizeof( key ), "%d.%s", tr_torrentId(tor), s->addr );
ref = g_hash_table_lookup( di->peer_hash, key );
ref = g_hash_table_lookup( hash, key );
assert( ref != NULL );
path = gtk_tree_row_reference_get_path( ref );
assert( path );
gtk_tree_model_get_iter( model, &iter, path );
refreshPeerRow( di->peer_store, &iter, s );
refreshPeerRow( store, &iter, s );
gtk_tree_path_free( path );
}
}
/* step 5: remove peers that have disappeared */
model = GTK_TREE_MODEL( di->peer_store );
model = GTK_TREE_MODEL( store );
if( gtk_tree_model_get_iter_first( model, &iter ) ) {
gboolean more = TRUE;
while( more ) {
gboolean wasUpdated;
gtk_tree_model_get( model, &iter, PEER_COL_WAS_UPDATED, &wasUpdated, -1 );
if( wasUpdated )
gboolean b;
gtk_tree_model_get( model, &iter, PEER_COL_WAS_UPDATED, &b, -1 );
if( b )
more = gtk_tree_model_iter_next( model, &iter );
else {
char * key;
gtk_tree_model_get( model, &iter, PEER_COL_KEY, &key, -1 );
/* fprintf( stderr, "removing key %s\n", key ); */
g_hash_table_remove( di->peer_hash, key );
more = gtk_list_store_remove( di->peer_store, &iter );
g_hash_table_remove( hash, key );
more = gtk_list_store_remove( store, &iter );
g_free( key );
}
}
@ -1397,6 +1365,7 @@ refreshWebseedList( struct DetailsImpl * di, tr_torrent ** torrents, int n )
GHashTable * hash = di->webseed_hash;
GtkListStore * store = di->webseed_store;
GtkTreeModel * model = GTK_TREE_MODEL( store );
/* step 1: mark all webseeds as not-updated */
if( gtk_tree_model_get_iter_first( model, &iter ) ) do
@ -1418,7 +1387,8 @@ refreshWebseedList( struct DetailsImpl * di, tr_torrent ** torrents, int n )
gtk_list_store_append( store, &iter );
gtk_list_store_set( store, &iter, WEBSEED_COL_URL, url, -1 );
path = gtk_tree_model_get_path( model, &iter );
g_hash_table_insert( hash, g_strdup( key ), gtk_tree_row_reference_new( model, path ) );
g_hash_table_insert( hash, g_strdup( key ),
gtk_tree_row_reference_new( model, path ) );
gtk_tree_path_free( path );
}
}
@ -1449,13 +1419,13 @@ refreshWebseedList( struct DetailsImpl * di, tr_torrent ** torrents, int n )
if( gtk_tree_model_get_iter_first( model, &iter ) ) {
gboolean more = TRUE;
while( more ) {
gboolean wasUpdated;
gtk_tree_model_get( model, &iter, WEBSEED_COL_WAS_UPDATED, &wasUpdated, -1 );
if( wasUpdated )
gboolean b;
gtk_tree_model_get( model, &iter, WEBSEED_COL_WAS_UPDATED, &b, -1 );
if( b )
more = gtk_tree_model_iter_next( model, &iter );
else {
char * key;
gtk_tree_model_get( model, &iter, PEER_COL_KEY, &key, -1 );
gtk_tree_model_get( model, &iter, WEBSEED_COL_KEY, &key, -1 );
g_hash_table_remove( hash, key );
more = gtk_list_store_remove( store, &iter );
g_free( key );
@ -1505,15 +1475,68 @@ refreshPeers( struct DetailsImpl * di, tr_torrent ** torrents, int n )
refreshWebseedList( di, torrents, n );
}
#if GTK_CHECK_VERSION( 2,12,0 )
static gboolean
onPeerViewQueryTooltip( GtkWidget * widget,
gint x,
gint y,
gboolean keyboard_tip,
GtkTooltip * tooltip,
gpointer user_data UNUSED )
{
gboolean show_tip = FALSE;
GtkTreeModel * model;
GtkTreeIter iter;
if( gtk_tree_view_get_tooltip_context( GTK_TREE_VIEW( widget ),
&x, &y, keyboard_tip,
&model, NULL, &iter ) )
{
const char * pch;
char * str = NULL;
GString * gstr = g_string_new( NULL );
gtk_tree_model_get( model, &iter, PEER_COL_STATUS, &str, -1 );
for( pch = str; pch && *pch; ++pch )
{
const char * txt = NULL;
switch( *pch )
{
case 'O': txt = _( "Optimistic unchoke" ); break;
case 'D': txt = _( "Downloading from this peer" ); break;
case 'd': txt = _( "We would download from this peer if they would let us" ); break;
case 'U': txt = _( "Uploading to peer" ); break;
case 'u': txt = _( "We would upload to this peer if they asked" ); break;
case 'K': txt = _( "Peer has unchoked us, but we're not interested" ); break;
case '?': txt = _( "We unchoked this peer, but they're not interested" ); break;
case 'E': txt = _( "Encrypted connection" ); break;
case 'X': txt = _( "Peer was discovered through Peer Exchange (PEX)" ); break;
case 'I': txt = _( "Peer is an incoming connection" ); break;
}
if( txt )
g_string_append_printf( gstr, "%c: %s\n", *pch, txt );
}
if( gstr->len ) /* remove the last linefeed */
g_string_set_size( gstr, gstr->len - 1 );
gtk_tooltip_set_text( tooltip, gstr->str );
g_string_free( gstr, TRUE );
g_free( str );
show_tip = TRUE;
}
return show_tip;
}
#endif
static GtkWidget*
peer_page_new( struct DetailsImpl * di )
{
guint i;
const char * str;
GtkListStore *store;
GtkWidget *v, *w, *ret, *sw, *l, *vbox, *hbox;
GtkWidget *webtree = NULL;
/* TODO: make this configurable? */
GtkTreeViewColumn * c;
GtkCellRenderer * r;
int view_columns[] = { PEER_COL_IS_ENCRYPTED,
PEER_COL_UPLOAD_RATE_STRING,
PEER_COL_DOWNLOAD_RATE_STRING,
@ -1523,43 +1546,40 @@ peer_page_new( struct DetailsImpl * di )
PEER_COL_CLIENT };
{
GtkListStore * store;
GtkTreeViewColumn * c;
GtkCellRenderer * r;
const char * t;
GtkWidget * w;
GtkWidget * v;
/* webseeds */
store = di->webseed_store = webseed_model_new( );
v = gtk_tree_view_new_with_model( GTK_TREE_MODEL( store ) );
g_signal_connect( v, "button-release-event", G_CALLBACK( on_tree_view_button_released ), NULL );
gtk_tree_view_set_rules_hint( GTK_TREE_VIEW( v ), TRUE );
//p->webseeds = GTK_LIST_STORE( GTK_TREE_MODEL( store ) );
g_object_unref( G_OBJECT( store ) );
store = di->webseed_store = webseed_model_new( );
v = gtk_tree_view_new_with_model( GTK_TREE_MODEL( store ) );
g_signal_connect( v, "button-release-event", G_CALLBACK( on_tree_view_button_released ), NULL );
gtk_tree_view_set_rules_hint( GTK_TREE_VIEW( v ), TRUE );
g_object_unref( G_OBJECT( store ) );
t = getWebseedColumnNames( WEBSEED_COL_URL );
r = gtk_cell_renderer_text_new( );
g_object_set( G_OBJECT( r ), "ellipsize", PANGO_ELLIPSIZE_END, NULL );
c = gtk_tree_view_column_new_with_attributes( t, r, "text", WEBSEED_COL_URL, NULL );
g_object_set( G_OBJECT( c ), "expand", TRUE, NULL );
gtk_tree_view_column_set_sort_column_id( c, WEBSEED_COL_URL );
gtk_tree_view_append_column( GTK_TREE_VIEW( v ), c );
str = getWebseedColumnNames( WEBSEED_COL_URL );
r = gtk_cell_renderer_text_new( );
g_object_set( G_OBJECT( r ), "ellipsize", PANGO_ELLIPSIZE_END, NULL );
c = gtk_tree_view_column_new_with_attributes( str, r, "text", WEBSEED_COL_URL, NULL );
g_object_set( G_OBJECT( c ), "expand", TRUE, NULL );
gtk_tree_view_column_set_sort_column_id( c, WEBSEED_COL_URL );
gtk_tree_view_append_column( GTK_TREE_VIEW( v ), c );
t = getWebseedColumnNames( WEBSEED_COL_DOWNLOAD_RATE );
r = gtk_cell_renderer_text_new( );
c = gtk_tree_view_column_new_with_attributes( t, r, "text", WEBSEED_COL_DOWNLOAD_RATE, NULL );
gtk_tree_view_column_set_sort_column_id( c, WEBSEED_COL_DOWNLOAD_RATE );
gtk_tree_view_append_column( GTK_TREE_VIEW( v ), c );
str = getWebseedColumnNames( WEBSEED_COL_DOWNLOAD_RATE );
r = gtk_cell_renderer_text_new( );
c = gtk_tree_view_column_new_with_attributes( str, r, "text", WEBSEED_COL_DOWNLOAD_RATE, NULL );
gtk_tree_view_column_set_sort_column_id( c, WEBSEED_COL_DOWNLOAD_RATE );
gtk_tree_view_append_column( GTK_TREE_VIEW( v ), c );
w = gtk_scrolled_window_new( NULL, NULL );
gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( w ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( w ), GTK_SHADOW_IN );
gtk_container_add( GTK_CONTAINER( w ), v );
w = gtk_scrolled_window_new( NULL, NULL );
gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( w ),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC );
gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( w ),
GTK_SHADOW_IN );
gtk_container_add( GTK_CONTAINER( w ), v );
webtree = w;
di->webseed_view = w;
}
webtree = w;
di->webseed_view = w;
/* peers */
store = di->peer_store = peer_store_new( );
v = GTK_WIDGET( g_object_new( GTK_TYPE_TREE_VIEW,
@ -1578,13 +1598,11 @@ peer_page_new( struct DetailsImpl * di )
g_signal_connect( v, "button-release-event",
G_CALLBACK( on_tree_view_button_released ), NULL );
for( i = 0; i < G_N_ELEMENTS( view_columns ); ++i )
for( i=0; i<G_N_ELEMENTS( view_columns ); ++i )
{
const int col = view_columns[i];
const char * t = getPeerColumnName( col );
int sort_col = col;
GtkTreeViewColumn * c;
GtkCellRenderer * r;
switch( col )
{
@ -1676,13 +1694,15 @@ peer_page_new( struct DetailsImpl * di )
gtk_box_pack_start( GTK_BOX( hbox ), l, FALSE, FALSE, 0 );
l = di->seeders_lb = gtk_label_new( NULL );
gtk_box_pack_start( GTK_BOX( hbox ), l, FALSE, FALSE, 0 );
gtk_box_pack_start( GTK_BOX( hbox ), gtk_alignment_new( 0.0f, 0.0f, 0.0f, 0.0f ), TRUE, TRUE, 0 );
w = gtk_alignment_new( 0.0f, 0.0f, 0.0f, 0.0f );
gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
l = gtk_label_new( NULL );
gtk_label_set_markup( GTK_LABEL( l ), _( "<b>Leechers:</b>" ) );
gtk_box_pack_start( GTK_BOX( hbox ), l, FALSE, FALSE, 0 );
l = di->leechers_lb = gtk_label_new( NULL );
gtk_box_pack_start( GTK_BOX( hbox ), l, FALSE, FALSE, 0 );
gtk_box_pack_start( GTK_BOX( hbox ), gtk_alignment_new( 0.0f, 0.0f, 0.0f, 0.0f ), TRUE, TRUE, 0 );
w = gtk_alignment_new( 0.0f, 0.0f, 0.0f, 0.0f );
gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
l = gtk_label_new( NULL );
gtk_label_set_markup( GTK_LABEL( l ), _( "<b>Times Completed:</b>" ) );
gtk_box_pack_start( GTK_BOX( hbox ), l, FALSE, FALSE, 0 );
@ -1712,21 +1732,6 @@ peer_page_new( struct DetailsImpl * di )
***** TRACKER
****/
#define TRACKER_PAGE "tracker-page"
struct tracker_page
{
GtkTreeView * view;
GtkListStore * store;
GtkTreeSelection * sel;
GtkWidget * add_button;
GtkWidget * remove_button;
GtkWidget * save_button;
GtkWidget * revert_button;
};
static void
refreshTracker( struct DetailsImpl * di, tr_torrent ** torrents, int n )
{
@ -1879,7 +1884,7 @@ refreshTracker( struct DetailsImpl * di, tr_torrent ** torrents, int n )
gtk_label_set_text( GTK_LABEL( di->manual_announce_countdown_lb ), str );
/* tracker list */
g_free( stats );
}
static GtkWidget*
@ -1975,7 +1980,7 @@ periodic_refresh( gpointer data )
}
static void
details_impl_free( gpointer gdata )
details_free( gpointer gdata )
{
struct DetailsImpl * data = gdata;
g_signal_handler_disconnect( data->core, data->prefs_changed_tag );
@ -1995,22 +2000,21 @@ response_cb( GtkDialog * dialog, int a UNUSED, gpointer b UNUSED )
GtkWidget*
torrent_inspector_new( GtkWindow * parent, TrCore * core )
{
char title[512];
guint tag;
struct DetailsImpl * data;
GtkWidget * d, * n, * w, * l;
/* create the dialog */
data = g_new0( struct DetailsImpl, 1 );
data->core = core;
/*FIXME g_snprintf( title, sizeof( title ), _( "%s Properties" ), info->name );*/
d = gtk_dialog_new_with_buttons( title, parent, 0,
d = gtk_dialog_new_with_buttons( NULL, 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 ), NULL );
gtk_dialog_set_has_separator( GTK_DIALOG( d ), FALSE );
gtk_container_set_border_width( GTK_CONTAINER( d ), GUI_PAD );
g_object_set_data_full( G_OBJECT( d ), DETAILS_KEY, data, details_impl_free );
g_object_set_data_full( G_OBJECT( d ), DETAILS_KEY, data, details_free );
n = gtk_notebook_new( );
gtk_container_set_border_width( GTK_CONTAINER( n ), GUI_PAD );
@ -2043,7 +2047,8 @@ torrent_inspector_new( GtkWindow * parent, TrCore * core )
gtk_box_pack_start( GTK_BOX( GTK_DIALOG( d )->vbox ), n, TRUE, TRUE, 0 );
data->periodic_refresh_tag = gtr_timeout_add_seconds( UPDATE_INTERVAL_SECONDS, periodic_refresh, data );
tag = gtr_timeout_add_seconds( UPDATE_INTERVAL_SECONDS, periodic_refresh, data );
data->periodic_refresh_tag = tag;
periodic_refresh( data );
gtk_widget_show_all( GTK_DIALOG( d )->vbox );
return d;
@ -2053,18 +2058,32 @@ void
torrent_inspector_set_torrents( GtkWidget * w, GSList * ids )
{
struct DetailsImpl * data = g_object_get_data( G_OBJECT( w ), DETAILS_KEY );
const int len = g_slist_length( ids );
char title[256];
g_slist_free( data->ids );
data->ids = g_slist_copy( ids );
if( g_slist_length( ids ) == 1 ) {
if( len == 1 )
{
const int id = GPOINTER_TO_INT( ids->data );
tr_session * session = tr_core_session( data->core );
tr_torrent * tor = tr_torrentFindFromId( session, id );
const tr_info * inf = tr_torrentInfo( tor );
g_snprintf( title, sizeof( title ), _( "%s Properties" ), inf->name );
file_list_set_torrent( data->file_list, id );
tracker_list_set_torrent( data->tracker_list, id );
} else {
}
else
{
file_list_clear( data->file_list );
tracker_list_clear( data->tracker_list );
g_snprintf( title, sizeof( title ), _( "%'d Torrent Properties" ), len );
}
gtk_window_set_title( GTK_WINDOW( w ), title );
refresh( data );
}

View File

@ -319,8 +319,8 @@ refresh( FileData * data )
gtk_tree_model_foreach( data->model, addSubForeach, data );
/* clean up the temporary variables */
data->refresh_file_stat = NULL;
tr_torrentFilesFree( data->refresh_file_stat, fileCount );
data->refresh_file_stat = NULL;
data->tor = NULL;
}
}

View File

@ -24,7 +24,6 @@
#include "details.h"
#include "file-list.h"
#include "tracker-list.h"
#include "tr-torrent.h"
#include "hig.h"
#include "util.h"
@ -334,8 +333,6 @@ tracker_list_new( tr_session * session, int torrentId, gboolean isNew )
box = gtk_vbox_new( FALSE, GUI_PAD );
buttons = gtk_vbox_new( TRUE, GUI_PAD );
//m = tracker_model_new( tr_torrent_handle( gtor ) );
//page->store = GTK_LIST_STORE( m );
w = gtk_tree_view_new( );
g_signal_connect( w, "button-release-event",
G_CALLBACK( on_tree_view_button_released ), NULL );