first cut at adopting some of the OS X client's main list UI into the gtk+ client

This commit is contained in:
Charles Kerr
2007-12-19 02:46:30 +00:00
parent e9ca72237f
commit 1a1eb1fa67
21 changed files with 1189 additions and 789 deletions

View File

@@ -20,8 +20,8 @@ noinst_HEADERS = \
makemeta-ui.h \
msgwin.h \
stats.h \
torrent-cell-renderer.h \
torrent-inspector.h \
tr_cell_renderer_progress.h \
tr_core.h \
tr_icon.h \
tr_prefs.h \
@@ -43,8 +43,8 @@ transmission_SOURCES = \
makemeta-ui.c \
msgwin.c \
stats.c \
torrent-cell-renderer.c \
torrent-inspector.c \
tr_cell_renderer_progress.c \
tr_core.c \
tr_icon.c \
tr_prefs.c \

View File

@@ -14,7 +14,9 @@
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include <libtransmission/transmission.h>
#include "conf.h"
#include "torrent-inspector.h"
#include "tr_prefs.h"
#include "lock.h"
#include "logo.h"
@@ -53,6 +55,24 @@ priority_changed_cb (GtkAction *action UNUSED, GtkRadioAction *current)
set_selected_file_priority ( priority );
}
static GtkRadioActionEntry sort_radio_entries[] =
{
{ "sort-by-activity", NULL, N_("Sort by _Activity"), NULL, NULL, 0 },
{ "sort-by-date-added", NULL, N_("Sort by _Date Added"), NULL, NULL, 1 },
{ "sort-by-name", NULL, N_("Sort by _Name"), NULL, NULL, 2 },
{ "sort-by-progress", NULL, N_("Sort by _Progress"), NULL, NULL, 3 },
{ "sort-by-state", NULL, N_("Sort by _State"), NULL, NULL, 4 },
{ "sort-by-tracker", NULL, N_("Sort by _Tracker"), NULL, NULL, 5 }
};
static void
sort_changed_cb( GtkAction * action UNUSED, GtkRadioAction * current, gpointer user_data )
{
const int i = gtk_radio_action_get_current_value( current );
const char * name = sort_radio_entries[i].name;
doAction ( name, user_data );
}
static GtkToggleActionEntry show_toggle_entries[] =
{
{ "toggle-main-window", NULL,
@@ -61,9 +81,19 @@ static GtkToggleActionEntry show_toggle_entries[] =
N_("Show Message _Log"), NULL, NULL, G_CALLBACK(action_cb), FALSE }
};
static GtkToggleActionEntry persistent_toggle_entries[] =
{
{ "minimal-view", NULL,
N_("_Minimal View"), "<control>M", NULL, G_CALLBACK(action_cb), FALSE },
{ "reverse-sort-order", NULL,
N_("_Reverse Sort Order"), NULL, NULL, G_CALLBACK(action_cb), FALSE }
};
static GtkActionEntry entries[] =
{
{ "torrent-menu", NULL, N_("_Torrent"), NULL, NULL, NULL },
{ "view-menu", NULL, N_("_View"), NULL, NULL, NULL },
{ "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 },
@@ -160,7 +190,9 @@ static GtkUIManager * myUIManager = NULL;
void
actions_init( GtkUIManager * ui_manager, gpointer callback_user_data )
{
int i;
int i, n;
int active;
char * match;
const int n_entries = G_N_ELEMENTS( entries );
GtkActionGroup * action_group;
@@ -180,10 +212,34 @@ actions_init( GtkUIManager * ui_manager, gpointer callback_user_data )
TR_PRI_NORMAL,
G_CALLBACK(priority_changed_cb), NULL);
gtk_action_group_add_toggle_actions ( action_group,
show_toggle_entries,
G_N_ELEMENTS(show_toggle_entries),
callback_user_data );
match = pref_string_get( PREF_KEY_SORT_MODE );
for( i=0, n=G_N_ELEMENTS(sort_radio_entries), active=-1; active==-1 && i<n; ++i ) {
if( !strcmp( sort_radio_entries[i].name, match ) ) {
g_message( "found %s in index %d", match, i );
active = i;
}
}
gtk_action_group_add_radio_actions( action_group,
sort_radio_entries,
G_N_ELEMENTS(sort_radio_entries),
active,
G_CALLBACK(sort_changed_cb),
callback_user_data );
gtk_action_group_add_toggle_actions( action_group,
show_toggle_entries,
G_N_ELEMENTS(show_toggle_entries),
callback_user_data );
for( i=0, n=G_N_ELEMENTS(persistent_toggle_entries); i<n; ++i )
persistent_toggle_entries[i].is_active = pref_flag_get( persistent_toggle_entries[i].name );
gtk_action_group_add_toggle_actions( action_group,
persistent_toggle_entries,
G_N_ELEMENTS(persistent_toggle_entries),
callback_user_data );
gtk_action_group_add_actions( action_group,
entries, n_entries,

View File

@@ -35,7 +35,6 @@
#include "conf.h"
#include "dialogs.h"
#include "hig.h"
#include "tr_cell_renderer_progress.h"
#include "tr_core.h"
#include "tr_icon.h"
#include "tr_prefs.h"

View File

@@ -43,7 +43,6 @@
#include "msgwin.h"
#include "stats.h"
#include "torrent-inspector.h"
#include "tr_cell_renderer_progress.h"
#include "tr_core.h"
#include "tr_icon.h"
#include "tr_prefs.h"
@@ -169,10 +168,13 @@ accumulateStatusForeach (GtkTreeModel * model,
{
int status = 0;
struct counts_data * counts = user_data;
tr_torrent * tor;
++counts->totalCount;
gtk_tree_model_get( model, iter, MC_STAT, &status, -1 );
gtk_tree_model_get( model, iter, MC_TORRENT_RAW, &tor, -1 );
status = tr_torrentStat( tor )->status;
if( TR_STATUS_IS_ACTIVE( status ) )
++counts->activeCount;
else
@@ -247,6 +249,8 @@ main( int argc, char ** argv )
/* initialize gtk */
g_thread_init( NULL );
gtk_init_with_args( &argc, &argv, _("[torrent files]"), entries, domain, NULL );
didinit = cf_init( tr_getPrefsDirectory(), NULL ); /* must come before actions_init */
tr_prefs_init_global( );
myUIManager = gtk_ui_manager_new ();
actions_init ( myUIManager, cbdata );
gtk_ui_manager_add_ui_from_string (myUIManager, fallback_ui_file, -1, NULL);
@@ -254,7 +258,6 @@ main( int argc, char ** argv )
gtk_window_set_default_icon_name ( "transmission-logo" );
argfiles = checkfilenames( argc-1, argv+1 );
didinit = cf_init( tr_getPrefsDirectory(), NULL );
didlock = didinit && sendremote( argfiles, sendquit );
setupsighandlers( ); /* set up handlers for fatal signals */
@@ -264,8 +267,6 @@ main( int argc, char ** argv )
/* create main window now to be a parent to any error dialogs */
GtkWindow * mainwind = GTK_WINDOW( tr_window_new( myUIManager ) );
tr_prefs_init_global( );
/* set message level here before tr_init() */
msgwin_loadpref( );
appsetup( mainwind, argfiles, cbdata, startpaused );
@@ -665,7 +666,9 @@ initializeFromPrefs( struct cbdata * cbdata )
PREF_KEY_NAT,
PREF_KEY_PEX,
PREF_KEY_SYSTRAY,
PREF_KEY_SORT_COLUMN,
PREF_KEY_SORT_MODE,
PREF_KEY_SORT_REVERSED,
PREF_KEY_MINIMAL_VIEW,
PREF_KEY_ENCRYPTED_ONLY
};
@@ -727,15 +730,20 @@ prefschanged( TrCore * core UNUSED, const char * key, gpointer data )
cbdata->icon = NULL;
}
}
else if( !strcmp( key, PREF_KEY_SORT_COLUMN ) )
else if( !strcmp( key, PREF_KEY_SORT_MODE ) || !strcmp( key, PREF_KEY_SORT_REVERSED ) )
{
tr_core_set_sort_column_from_prefs( cbdata->core );
tr_core_resort( cbdata->core );
}
else if( !strcmp( key, PREF_KEY_PEX ) )
{
gboolean enabled = pref_flag_get( key );
tr_torrentIterate( tr, setpex, &enabled );
}
else if( !strcmp( key, PREF_KEY_MINIMAL_VIEW ) )
{
const gboolean enabled = pref_flag_get( key );
g_message( "minimal view: %d", enabled );
}
}
void
@@ -1016,6 +1024,23 @@ doAction ( const char * action_name, gpointer user_data )
else
gtk_window_present (GTK_WINDOW(w));
}
else if( !strcmp( action_name, "sort-by-activity" )
|| !strcmp( action_name, "sort-by-date-added" )
|| !strcmp( action_name, "sort-by-name" )
|| !strcmp( action_name, "sort-by-progress" )
|| !strcmp( action_name, "sort-by-state" )
|| !strcmp( action_name, "sort-by-tracker" ) )
{
tr_core_set_pref( data->core, PREF_KEY_SORT_MODE, action_name );
}
else if( !strcmp( action_name, "reverse-sort-order" ) )
{
tr_core_toggle_pref_bool( data->core, PREF_KEY_SORT_REVERSED );
}
else if( !strcmp( action_name, "minimal-view" ) )
{
tr_core_toggle_pref_bool( data->core, PREF_KEY_MINIMAL_VIEW );
}
else g_error ("Unhandled action: %s", action_name );
if( changed )

View File

@@ -173,8 +173,8 @@ static void
file_selection_changed_cb( GtkFileChooser *chooser, gpointer user_data )
{
MakeMetaUI * ui = (MakeMetaUI *) user_data;
char * pch;
char * filename;
char sizeStr[128];
char buf[512];
uint64_t totalSize=0;
int fileCount=0, pieceCount=0, pieceSize=0;
@@ -194,20 +194,18 @@ file_selection_changed_cb( GtkFileChooser *chooser, gpointer user_data )
pieceSize = ui->builder->pieceSize;
}
pch = readablesize( totalSize );
tr_strlsize( sizeStr, totalSize, sizeof(sizeStr) );
g_snprintf( buf, sizeof(buf), "<i>%s; %d %s</i>",
pch, fileCount,
sizeStr, fileCount,
ngettext("File", "Files", fileCount) );
gtk_label_set_markup ( GTK_LABEL(ui->size_lb), buf );
g_free( pch );
pch = readablesize( pieceSize );
tr_strlsize( sizeStr, pieceSize, sizeof(sizeStr) );
g_snprintf( buf, sizeof(buf), "<i>%d %s @ %s</i>",
pieceCount,
ngettext("Piece", "Pieces", pieceCount),
pch );
sizeStr );
gtk_label_set_markup ( GTK_LABEL(ui->pieces_lb), buf );
g_free( pch );
}
static void

View File

@@ -49,18 +49,20 @@ setLabelFromRatio( GtkWidget * w, double d )
static gboolean
updateStats( gpointer gdata )
{
char buf[128];
struct stat_ui * ui = gdata;
tr_session_stats one, all;
tr_getSessionStats( ui->core->handle, &one );
tr_getCumulativeSessionStats( ui->core->handle, &all );
setLabel( ui->one_up_lb, readablesize( one.uploadedBytes ) );
setLabel( ui->one_down_lb, readablesize( one.downloadedBytes ) );
setLabel( ui->one_time_lb, readabletime( one.secondsActive ) );
setLabel( ui->one_up_lb, tr_strlsize( buf, one.uploadedBytes, sizeof(buf) ) );
setLabel( ui->one_down_lb, tr_strlsize( buf, one.downloadedBytes, sizeof(buf) ) );
setLabel( ui->one_time_lb, tr_strlsize( buf, one.secondsActive, sizeof(buf) ) );
setLabelFromRatio( ui->one_ratio_lb, one.ratio );
setLabel( ui->all_sessions_lb, g_strdup_printf( _("Started %d times"), (int)all.sessionCount ) );
setLabel( ui->all_up_lb, readablesize( all.uploadedBytes ) );
setLabel( ui->all_down_lb, readablesize( all.downloadedBytes ) );
setLabel( ui->all_up_lb, tr_strlsize( buf, all.uploadedBytes, sizeof(buf) ) );
setLabel( ui->all_down_lb, tr_strlsize( buf, all.downloadedBytes, sizeof(buf) ) );
setLabel( ui->all_time_lb, readabletime( all.secondsActive ) );
setLabelFromRatio( ui->all_ratio_lb, all.ratio );

726
gtk/torrent-cell-renderer.c Normal file
View File

@@ -0,0 +1,726 @@
/*
* This file Copyright (C) 2007 Charles Kerr <charles@rebelbase.com>
*
* This file is licensed by the GPL version 2. Works owned by the
* Transmission project are granted a special exemption to clause 2(b)
* so that the bulk of its code can remain under the MIT license.
* This exemption does not extend to derived works not owned by
* the Transmission project.
*
* $Id:$
*/
#include "assert.h"
#include <gtk/gtk.h>
#include <gtk/gtkcellrenderertext.h>
#include <glib/gi18n.h>
#include <libtransmission/transmission.h>
#include "torrent-cell-renderer.h"
#include "tr_torrent.h"
#include "util.h"
enum
{
P_TORRENT = 1,
P_BAR_HEIGHT,
P_MINIMAL,
P_SHOW_UNAVAILABLE,
P_GRADIENT,
P_COLOR_VERIFIED,
P_COLOR_VERIFIED_2,
P_COLOR_MISSING,
P_COLOR_MISSING_2,
P_COLOR_UNWANTED,
P_COLOR_UNWANTED_2,
P_COLOR_UNAVAILABLE,
P_COLOR_UNAVAILABLE_2,
P_COLOR_PAUSED,
P_COLOR_PAUSED_2,
P_COLOR_VERIFYING,
P_COLOR_VERIFYING_2,
P_COLOR_SEEDING,
P_COLOR_SEEDING_2
};
#define DEFAULT_BAR_HEIGHT 12
#define DEFAULT_COLOR_VERIFIED "#77aaff"
#define DEFAULT_COLOR_VERIFIED_2 "#002277"
#define DEFAULT_COLOR_SEEDING "#77ff88"
#define DEFAULT_COLOR_SEEDING_2 "#007700"
#define DEFAULT_COLOR_MISSING "#fcfcfc"
#define DEFAULT_COLOR_MISSING_2 "#acacac"
#define DEFAULT_COLOR_UNWANTED "#e0e0e0"
#define DEFAULT_COLOR_UNWANTED_2 "#808080"
#define DEFAULT_COLOR_UNAVAILABLE "#ff7788"
#define DEFAULT_COLOR_UNAVAILABLE_2 "#770000"
#define DEFAULT_COLOR_PAUSED "#959595"
#define DEFAULT_COLOR_PAUSED_2 "#555555"
#define DEFAULT_COLOR_VERIFYING "#ffff77"
#define DEFAULT_COLOR_VERIFYING_2 "#777700"
/***
****
***/
static char*
getProgressString( const tr_torrent * tor, const tr_stat * torStat )
{
const int allDownloaded = torStat->leftUntilDone == 0;
const uint64_t haveTotal = torStat->haveUnchecked + torStat->haveValid;
const tr_info * info = tr_torrentInfo( tor );
const int isSeed = torStat->haveValid >= info->totalSize;
char buf1[128], buf2[128], buf3[128];
char * str;
if( !allDownloaded )
str = g_strdup_printf(
_("%s of %s (%.2f%%)"),
tr_strlsize( buf1, haveTotal, sizeof(buf1) ),
tr_strlsize( buf2, torStat->desiredSize, sizeof(buf2) ),
torStat->percentDone * 100.0 );
else if( !isSeed )
str = g_strdup_printf(
_("%s of %s (%.2f%%), uploaded %s (Ratio: %.2f"),
tr_strlsize( buf1, haveTotal, sizeof(buf1) ),
tr_strlsize( buf2, info->totalSize, sizeof(buf2) ),
torStat->percentComplete * 100.0,
tr_strlsize( buf3, torStat->uploadedEver, sizeof(buf3) ),
torStat->ratio * 100.0 );
else
str = g_strdup_printf(
_("%s, uploaded %s (Ratio: %.2f)"),
tr_strlsize( buf1, info->totalSize, sizeof(buf1) ),
tr_strlsize( buf2, torStat->uploadedEver, sizeof(buf2) ),
torStat->ratio * 100.0 );
return str;
}
static char*
getStatusString( const tr_torrent * tor UNUSED, const tr_stat * torStat )
{
const int isActive = torStat->status != TR_STATUS_STOPPED;
const int isChecking = torStat->status == TR_STATUS_CHECK
|| torStat->status == TR_STATUS_CHECK_WAIT;
GString * gstr = g_string_new( NULL );
if( torStat->error )
{
g_string_assign( gstr, torStat->errorString );
}
else switch( torStat->status )
{
case TR_STATUS_STOPPED:
g_string_assign( gstr, _("Paused" ) );
break;
case TR_STATUS_CHECK_WAIT:
g_string_assign( gstr, _( "Waiting to Verify local data" ) );
break;
case TR_STATUS_CHECK:
g_string_append_printf( gstr, _("Verifying local data (%.1f%% tested)"),
torStat->recheckProgress * 100.0 );
break;
case TR_STATUS_DOWNLOAD:
g_string_append_printf( gstr,
ngettext( _("Downloading from %d of %d peer" ),
_("Downloading from %d of %d peers" ),
torStat->peersConnected ),
torStat->peersSendingToUs,
torStat->peersConnected );
break;
case TR_STATUS_DONE:
case TR_STATUS_SEED:
g_string_append_printf( gstr,
ngettext( _( "Seeding to %d of %d peer" ),
_( "Seeding to %d of %d peers" ),
torStat->peersGettingFromUs ),
torStat->peersGettingFromUs,
torStat->peersConnected );
break;
}
if( isActive && !isChecking )
{
char ulbuf[64], dlbuf[64];
if (torStat->status == TR_STATUS_DOWNLOAD)
g_string_append_printf( gstr, _(" - DL: %s, UL: %s" ),
tr_strlspeed( dlbuf, torStat->rateDownload, sizeof(dlbuf) ),
tr_strlspeed( ulbuf, torStat->rateUpload, sizeof(ulbuf) ) );
else
g_string_append_printf( gstr, _(" - UL: %s" ),
tr_strlspeed( ulbuf, torStat->rateUpload, sizeof(ulbuf) ) );
}
return g_string_free( gstr, FALSE );
}
/***
****
***/
static GtkCellRendererClass * parent_class = NULL;
struct TorrentCellRendererPrivate
{
tr_torrent * tor;
GtkCellRenderer * text_renderer;
int bar_height;
gboolean minimal;
gboolean show_unavailable;
gboolean gradient;
GdkColor color_paused[2];
GdkColor color_verified[2];
GdkColor color_missing[2];
GdkColor color_unwanted[2];
GdkColor color_unavailable[2];
GdkColor color_verifying[2];
GdkColor color_seeding[2];
};
static void
torrent_cell_renderer_get_size( GtkCellRenderer * cell,
GtkWidget * widget,
GdkRectangle * cell_area,
gint * x_offset,
gint * y_offset,
gint * width,
gint * height)
{
TorrentCellRenderer * self = TORRENT_CELL_RENDERER( cell );
int xpad, ypad;
g_object_get( self, "xpad", &xpad, "ypad", &ypad, NULL );
if( self && self->priv->tor )
{
const tr_torrent * tor = self->priv->tor;
const tr_info * info = tr_torrentInfo( tor );
const char * name = info->name;
const tr_stat * torStat = tr_torrentStat( (tr_torrent*)tor );
char * progressString = getProgressString( tor, torStat );
char * statusString = getStatusString( tor, torStat );
char * str;
int tmp_w, tmp_h;
int w=0, h=0;
/* above the progressbar */
str = g_markup_printf_escaped( "<b>%s</b>\n<small>%s</small>",
name, progressString );
g_object_set( self->priv->text_renderer, "markup", str, NULL );
gtk_cell_renderer_get_size( self->priv->text_renderer,
widget, NULL, NULL, NULL, &tmp_w, &tmp_h );
h += tmp_h;
w = MAX( w, tmp_w );
g_free( str );
/* below the progressbar */
str = g_markup_printf_escaped( "<small>%s</small>", statusString );
g_object_set( self->priv->text_renderer, "markup", str, NULL );
gtk_cell_renderer_get_size( self->priv->text_renderer,
widget, NULL, NULL, NULL, &tmp_w, &tmp_h );
h += tmp_h;
w = MAX( w, tmp_w );
g_free( str );
/* make the progressbar the same height as the below */
h += self->priv->bar_height;
if( cell_area ) {
if( x_offset ) *x_offset = 0;
if( y_offset ) {
*y_offset = 0.5 * (cell_area->height - (h + (2 * ypad)));
*y_offset = MAX( *y_offset, 0 );
}
}
*width = w + xpad*2;
*height = h + ypad*2;
/* cleanup */
g_free( statusString );
g_free( progressString );
}
}
static void
fillRect( TorrentCellRenderer * self,
GdkGC * gc,
GdkDrawable * drawable,
const GdkRectangle * area_in,
const GdkColor * colors,
size_t n_colors )
{
const int drawGradient = self->priv->gradient && ( n_colors > 1 );
assert( n_colors==1 || n_colors==2 );
if( !drawGradient )
{
gdk_gc_set_rgb_fg_color( gc, colors );
gdk_draw_rectangle( drawable, gc, TRUE,
area_in->x, area_in->y,
area_in->width, area_in->height );
}
else
{
int i;
const int steps = area_in->height;
const int step_height = area_in->height / steps;
const int r_inc = ((int)colors[1].red - (int)colors[0].red) / steps;
const int g_inc = ((int)colors[1].green - (int)colors[0].green) / steps;
const int b_inc = ((int)colors[1].blue - (int)colors[0].blue) / steps;
GdkRectangle area = *area_in;
GdkColor color = colors[0];
area.height = step_height;
for( i=0; i<steps; ++i ) {
gdk_gc_set_rgb_fg_color( gc, &color );
gdk_draw_rectangle( drawable, gc, TRUE,
area.x, area.y, area.width, area.height );
area.y += step_height;
color.red += r_inc;
color.green += g_inc;
color.blue += b_inc;
}
}
}
static void
drawRegularBar( TorrentCellRenderer * self,
const tr_info * info,
const tr_stat * torStat,
GdkDrawable * drawable,
GtkWidget * widget,
const GdkRectangle * area )
{
#if 1
const double verified = torStat->haveValid / (double)info->totalSize;
const double unverified = torStat->haveUnchecked / (double)info->totalSize;
const double unavailable = ( torStat->desiredSize - torStat->desiredAvailable ) / (double)info->totalSize;
const double unwanted = ( info->totalSize - torStat->desiredSize ) / (double)info->totalSize;
#else /* for testing */
const double verified = 0.5;
const double unverified = 0.1;
const double unavailable = 0.1;
const double unwanted = 0.1;
#endif
const double missing = 1.0 - verified - unverified - unavailable - unwanted;
const int verifiedWidth = (int)( verified * area->width );
const int unverifiedWidth = (int)( unverified * area->width );
const int unavailableWidth = (int)( unavailable * area->width );
const int unwantedWidth = (int)( unwanted * area->width );
const int missingWidth = (int)( missing * area->width );
const gboolean isActive = torStat->status == TR_STATUS_DOWNLOAD
|| torStat->status == TR_STATUS_DONE
|| torStat->status == TR_STATUS_SEED;
const gboolean isChecking = torStat->status == TR_STATUS_CHECK
|| torStat->status == TR_STATUS_CHECK_WAIT;
int x = area->x;
int w = 0;
GdkGC * gc = gdk_gc_new( drawable );
GdkRectangle rect = *area;
if(( w = verifiedWidth )) {
const GdkColor * colors;
if( !isActive )
colors = self->priv->color_paused;
else if( torStat->status == TR_STATUS_DOWNLOAD )
colors = self->priv->color_verified;
else
colors = self->priv->color_seeding;
rect.x = x;
rect.width = w;
fillRect( self, gc, drawable, &rect, colors, 2 );
x += w;
}
if(( w = unverifiedWidth )) {
const GdkColor * colors = isActive ? self->priv->color_verifying
: self->priv->color_paused;
rect.x = x;
rect.width = w;
fillRect( self, gc, drawable, &rect, colors, 2 );
x += w;
}
if(( w = missingWidth )) {
rect.x = x;
rect.width = w;
fillRect( self, gc, drawable, &rect, self->priv->color_missing, 2 );
x += w;
}
if(( w = unwantedWidth )) {
rect.x = x;
rect.width = w;
fillRect( self, gc, drawable, &rect, self->priv->color_unwanted, 2 );
x += w;
}
if(( w = unavailableWidth )) {
const GdkColor * colors = isActive && self->priv->show_unavailable
? self->priv->color_unavailable
: self->priv->color_missing;
rect.x = x;
rect.width = w;
fillRect( self, gc, drawable, &rect, colors, 2 );
x += w;
}
if( isChecking ) {
const int checkedWidth = torStat->recheckProgress * area->width;
int h2 = area->height / 2;
rect = *area;
rect.y += h2;
rect.height -= h2;
fillRect( self, gc, drawable, &rect, self->priv->color_missing, 2 );
rect.width = checkedWidth;
fillRect( self, gc, drawable, &rect, self->priv->color_verifying, 2 );
}
gtk_paint_shadow( gtk_widget_get_style( widget ),
drawable,
GTK_STATE_NORMAL,
GTK_SHADOW_IN,
NULL,
widget,
NULL,
area->x, area->y, area->width, area->height );
gdk_gc_unref( gc );
}
static void
torrent_cell_renderer_render( GtkCellRenderer * cell,
GdkDrawable * window,
GtkWidget * widget,
GdkRectangle * background_area,
GdkRectangle * cell_area,
GdkRectangle * expose_area,
GtkCellRendererState flags)
{
TorrentCellRenderer * self = TORRENT_CELL_RENDERER( cell );
if( self && self->priv->tor )
{
const tr_torrent * tor = self->priv->tor;
const tr_info * info = tr_torrentInfo( tor );
const char * name = info->name;
const tr_stat * torStat = tr_torrentStat( (tr_torrent*)tor );
char * progressString = getProgressString( tor, torStat );
char * statusString = getStatusString( tor, torStat );
char * str;
GdkRectangle my_bg;
GdkRectangle my_cell;
GdkRectangle my_expose;
int xpad, ypad;
int w, h;
g_object_get( self, "xpad", &xpad, "ypad", &ypad, NULL );
my_bg = *background_area;
my_bg.x += xpad;
my_bg.y += ypad;
my_bg.width -= xpad*2;
my_cell = *cell_area;
my_cell.x += xpad;
my_cell.y += ypad;
my_cell.width -= xpad*2;
my_expose = *expose_area;
my_expose.x += xpad;
my_expose.y += ypad;
my_expose.width -= xpad*2;
/* above the progressbar */
str = g_markup_printf_escaped( "<b>%s</b>\n<small>%s</small>",
name, progressString );
g_object_set( self->priv->text_renderer, "markup", str, NULL );
gtk_cell_renderer_get_size( self->priv->text_renderer,
widget, NULL, NULL, NULL, &w, &h );
my_bg.height = h;
my_cell.height = h;
my_expose.height = h;
gtk_cell_renderer_render( self->priv->text_renderer,
window, widget,
&my_bg, &my_cell, &my_expose, flags );
my_bg.y += h;
my_cell.y += h;
my_expose.y += h;
/* the progressbar */
my_cell.height = self->priv->bar_height;
drawRegularBar( self, info, torStat, window, widget, &my_cell );
my_bg.y += my_cell.height;
my_cell.y += my_cell.height;
my_expose.y += my_cell.height;
/* below progressbar */
str = g_markup_printf_escaped( "<small>%s</small>", statusString );
g_object_set( self->priv->text_renderer, "markup", str, NULL );
gtk_cell_renderer_get_size( self->priv->text_renderer,
widget, NULL, NULL, NULL, &w, &h );
my_bg.height = h;
my_cell.height = h;
my_expose.height = h;
gtk_cell_renderer_render( self->priv->text_renderer,
window, widget,
&my_bg, &my_cell, &my_expose, flags );
g_free( statusString );
g_free( progressString );
}
}
static void
setColor( GdkColor * color, const GValue * value )
{
gdk_color_parse( g_value_get_string( value ), color );
}
static void
torrent_cell_renderer_set_property( GObject * object,
guint property_id,
const GValue * value,
GParamSpec * pspec)
{
TorrentCellRenderer * self = TORRENT_CELL_RENDERER( object );
struct TorrentCellRendererPrivate * p = self->priv;
switch( property_id )
{
case P_TORRENT: p->tor = g_value_get_pointer( value ); break;
case P_BAR_HEIGHT: p->bar_height = g_value_get_int( value ); break;
case P_MINIMAL: p->minimal = g_value_get_boolean( value ); break;
case P_GRADIENT: p->gradient = g_value_get_boolean( value ); break;
case P_SHOW_UNAVAILABLE: p->show_unavailable = g_value_get_boolean( value ); break;
case P_COLOR_MISSING: setColor( &p->color_missing[0], value ); break;
case P_COLOR_MISSING_2: setColor( &p->color_missing[1], value ); break;
case P_COLOR_UNWANTED: setColor( &p->color_unwanted[0], value ); break;
case P_COLOR_UNWANTED_2: setColor( &p->color_unwanted[1], value ); break;
case P_COLOR_PAUSED: setColor( &p->color_paused[0], value ); break;
case P_COLOR_PAUSED_2: setColor( &p->color_paused[1], value ); break;
case P_COLOR_VERIFIED: setColor( &p->color_verified[0], value ); break;
case P_COLOR_VERIFIED_2: setColor( &p->color_verified[1], value ); break;
case P_COLOR_UNAVAILABLE: setColor( &p->color_unavailable[0], value ); break;
case P_COLOR_UNAVAILABLE_2: setColor( &p->color_unavailable[1], value ); break;
case P_COLOR_VERIFYING: setColor( &p->color_verifying[0], value ); break;
case P_COLOR_VERIFYING_2: setColor( &p->color_verifying[1], value ); break;
case P_COLOR_SEEDING: setColor( &p->color_seeding[0], value ); break;
case P_COLOR_SEEDING_2: setColor( &p->color_seeding[1], value ); break;
default: G_OBJECT_WARN_INVALID_PROPERTY_ID( object, property_id, pspec ); break;
}
}
static void
setValueColor( GValue * value, const GdkColor * color )
{
char buf[16];
g_snprintf( buf, sizeof(buf), "#%2.2x%2.2x%2.2x",
(color->red >> 8) & 0xff,
(color->green >> 8) & 0xff,
(color->blue >> 8) & 0xff );
g_value_set_string( value, buf );
}
static void
torrent_cell_renderer_get_property( GObject * object,
guint property_id,
GValue * value,
GParamSpec * pspec)
{
const TorrentCellRenderer * self = TORRENT_CELL_RENDERER( object );
struct TorrentCellRendererPrivate * p = self->priv;
switch( property_id )
{
case P_TORRENT: g_value_set_pointer( value, p->tor ); break;
case P_BAR_HEIGHT: g_value_set_int( value, p->bar_height ); break;
case P_MINIMAL: g_value_set_boolean( value, p->minimal ); break;
case P_GRADIENT: g_value_set_boolean( value, p->gradient ); break;
case P_SHOW_UNAVAILABLE: g_value_set_boolean( value, p->show_unavailable ); break;
case P_COLOR_MISSING: setValueColor( value, &p->color_missing[0] ); break;
case P_COLOR_MISSING_2: setValueColor( value, &p->color_missing[1] ); break;
case P_COLOR_UNWANTED: setValueColor( value, &p->color_unwanted[0] ); break;
case P_COLOR_UNWANTED_2: setValueColor( value, &p->color_unwanted[1] ); break;
case P_COLOR_PAUSED: setValueColor( value, &p->color_paused[0] ); break;
case P_COLOR_PAUSED_2: setValueColor( value, &p->color_paused[1] ); break;
case P_COLOR_VERIFIED: setValueColor( value, &p->color_verified[0] ); break;
case P_COLOR_VERIFIED_2: setValueColor( value, &p->color_verified[1] ); break;
case P_COLOR_UNAVAILABLE: setValueColor( value, &p->color_unavailable[0] ); break;
case P_COLOR_UNAVAILABLE_2: setValueColor( value, &p->color_unavailable[1] ); break;
case P_COLOR_VERIFYING: setValueColor( value, &p->color_verifying[0] ); break;
case P_COLOR_VERIFYING_2: setValueColor( value, &p->color_verifying[1] ); break;
case P_COLOR_SEEDING: setValueColor( value, &p->color_seeding[0] ); break;
case P_COLOR_SEEDING_2: setValueColor( value, &p->color_seeding[1] ); break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID( object, property_id, pspec );
break;
}
}
static void
torrent_cell_renderer_class_init( TorrentCellRendererClass * klass )
{
GObjectClass * gobject_class = G_OBJECT_CLASS( klass );
GtkCellRendererClass * cell_renderer_class = GTK_CELL_RENDERER_CLASS( klass );
g_type_class_add_private( klass, sizeof(struct TorrentCellRendererPrivate) );
parent_class = (GtkCellRendererClass*) g_type_class_peek_parent( klass );
cell_renderer_class->render = torrent_cell_renderer_render;
cell_renderer_class->get_size = torrent_cell_renderer_get_size;
gobject_class->set_property = torrent_cell_renderer_set_property;
gobject_class->get_property = torrent_cell_renderer_get_property;
g_object_class_install_property( gobject_class, P_TORRENT,
g_param_spec_pointer( "torrent", NULL, "tr_torrent*",
G_PARAM_READWRITE ) );
g_object_class_install_property( gobject_class, P_BAR_HEIGHT,
g_param_spec_int( "bar-height", NULL, "Bar Height",
1, INT_MAX, DEFAULT_BAR_HEIGHT, G_PARAM_READWRITE ) );
g_object_class_install_property( gobject_class, P_MINIMAL,
g_param_spec_boolean( "minimal", NULL, "Minimal Mode",
FALSE, G_PARAM_READWRITE ) );
g_object_class_install_property( gobject_class, P_GRADIENT,
g_param_spec_boolean( "gradient", NULL, "Render Progress as a Gradient",
TRUE, G_PARAM_READWRITE ) );
g_object_class_install_property( gobject_class, P_SHOW_UNAVAILABLE,
g_param_spec_boolean( "unavailable", NULL, "Show Unavailable",
FALSE, G_PARAM_READWRITE ) );
g_object_class_install_property( gobject_class, P_COLOR_MISSING,
g_param_spec_string( "missing-color", NULL, "Color for Missing Data",
DEFAULT_COLOR_MISSING, G_PARAM_READWRITE ) );
g_object_class_install_property( gobject_class, P_COLOR_MISSING,
g_param_spec_string( "missing-color-2", NULL, "Gradient Color for Missing Data",
DEFAULT_COLOR_MISSING_2, G_PARAM_READWRITE ) );
g_object_class_install_property( gobject_class, P_COLOR_UNWANTED,
g_param_spec_string( "unwanted-color", NULL, "Color for Unwanted Data",
DEFAULT_COLOR_UNWANTED, G_PARAM_READWRITE ) );
g_object_class_install_property( gobject_class, P_COLOR_UNWANTED_2,
g_param_spec_string( "unwanted-color-2", NULL, "Gradient Color for Unwanted Data",
DEFAULT_COLOR_UNWANTED_2, G_PARAM_READWRITE ) );
g_object_class_install_property( gobject_class, P_COLOR_PAUSED,
g_param_spec_string( "paused-color", NULL, "Color for Paused Data",
DEFAULT_COLOR_PAUSED, G_PARAM_READWRITE ) );
g_object_class_install_property( gobject_class, P_COLOR_PAUSED_2,
g_param_spec_string( "paused-color-2", NULL, "Gradient Color for Paused Data",
DEFAULT_COLOR_PAUSED_2, G_PARAM_READWRITE ) );
g_object_class_install_property( gobject_class, P_COLOR_VERIFIED,
g_param_spec_string( "verified-color", NULL, "Color for Verified Data",
DEFAULT_COLOR_VERIFIED, G_PARAM_READWRITE ) );
g_object_class_install_property( gobject_class, P_COLOR_VERIFIED_2,
g_param_spec_string( "verified-color-2", NULL, "Gradient Color for Verified Data",
DEFAULT_COLOR_VERIFIED_2, G_PARAM_READWRITE ) );
g_object_class_install_property( gobject_class, P_COLOR_UNAVAILABLE,
g_param_spec_string( "unavailable-color", NULL, "Color for Unavailable Data",
DEFAULT_COLOR_UNAVAILABLE, G_PARAM_READWRITE ) );
g_object_class_install_property( gobject_class, P_COLOR_UNAVAILABLE_2,
g_param_spec_string( "unavailable-color-2", NULL, "Gradient Color for Unavailable Data",
DEFAULT_COLOR_UNAVAILABLE_2, G_PARAM_READWRITE ) );
g_object_class_install_property( gobject_class, P_COLOR_VERIFYING,
g_param_spec_string( "verifying-color", NULL, "Color for Verifying Data",
DEFAULT_COLOR_VERIFYING, G_PARAM_READWRITE ) );
g_object_class_install_property( gobject_class, P_COLOR_VERIFYING_2,
g_param_spec_string( "verifying-color-2", NULL, "Gradient Color for Verifying Data",
DEFAULT_COLOR_VERIFYING_2, G_PARAM_READWRITE ) );
g_object_class_install_property( gobject_class, P_COLOR_SEEDING,
g_param_spec_string( "seeding-color", NULL, "Color for Seeding Data",
DEFAULT_COLOR_SEEDING, G_PARAM_READWRITE ) );
g_object_class_install_property( gobject_class, P_COLOR_SEEDING_2,
g_param_spec_string( "seeding-color-2", NULL, "Second Color for Seeding Data",
DEFAULT_COLOR_SEEDING_2, G_PARAM_READWRITE ) );
}
static void
torrent_cell_renderer_init( GTypeInstance * instance, gpointer g_class UNUSED )
{
TorrentCellRenderer * self = TORRENT_CELL_RENDERER( instance );
struct TorrentCellRendererPrivate * p;
p = self->priv = G_TYPE_INSTANCE_GET_PRIVATE( self,
TORRENT_CELL_RENDERER_TYPE,
struct TorrentCellRendererPrivate );
p->tor = NULL;
p->text_renderer = gtk_cell_renderer_text_new( );
p->gradient = TRUE;
p->show_unavailable = TRUE;
p->bar_height = DEFAULT_BAR_HEIGHT;
g_object_set( p->text_renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL );
gdk_color_parse( DEFAULT_COLOR_VERIFIED, &p->color_verified[0] );
gdk_color_parse( DEFAULT_COLOR_VERIFIED_2, &p->color_verified[1] );
gdk_color_parse( DEFAULT_COLOR_MISSING, &p->color_missing[0] );
gdk_color_parse( DEFAULT_COLOR_MISSING_2, &p->color_missing[1] );
gdk_color_parse( DEFAULT_COLOR_UNWANTED, &p->color_unwanted[0] );
gdk_color_parse( DEFAULT_COLOR_UNWANTED_2, &p->color_unwanted[1] );
gdk_color_parse( DEFAULT_COLOR_UNAVAILABLE, &p->color_unavailable[0] );
gdk_color_parse( DEFAULT_COLOR_UNAVAILABLE_2, &p->color_unavailable[1] );
gdk_color_parse( DEFAULT_COLOR_VERIFYING, &p->color_verifying[0] );
gdk_color_parse( DEFAULT_COLOR_VERIFYING_2, &p->color_verifying[1] );
gdk_color_parse( DEFAULT_COLOR_SEEDING, &p->color_seeding[0] );
gdk_color_parse( DEFAULT_COLOR_SEEDING_2, &p->color_seeding[1] );
gdk_color_parse( DEFAULT_COLOR_PAUSED, &p->color_paused[0] );
gdk_color_parse( DEFAULT_COLOR_PAUSED_2, &p->color_paused[1] );
}
GType
torrent_cell_renderer_get_type( void )
{
static GType type = 0;
if( !type )
{
static const GTypeInfo info =
{
sizeof( TorrentCellRendererClass ),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc)torrent_cell_renderer_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof( TorrentCellRenderer ),
0, /* n_preallocs */
(GInstanceInitFunc)torrent_cell_renderer_init,
NULL
};
type = g_type_register_static( GTK_TYPE_CELL_RENDERER,
"TorrentCellRenderer",
&info, (GTypeFlags)0 );
}
return type;
}
GtkCellRenderer *
torrent_cell_renderer_new( void )
{
return (GtkCellRenderer *) g_object_new( TORRENT_CELL_RENDERER_TYPE, NULL );
}

View File

@@ -0,0 +1,44 @@
/*
* This file Copyright (C) 2007 Charles Kerr <charles@rebelbase.com>
*
* This file is licensed by the GPL version 2. Works owned by the
* Transmission project are granted a special exemption to clause 2(b)
* so that the bulk of its code can remain under the MIT license.
* This exemption does not extend to derived works not owned by
* the Transmission project.
*
* $Id:$
*/
#ifndef TORRENT_CELL_RENDERER_H
#define TORRENT_CELL_RENDERER_H
#include <glib-object.h>
#include <gtk/gtkcellrenderer.h>
#define TORRENT_CELL_RENDERER_TYPE (torrent_cell_renderer_get_type())
#define TORRENT_CELL_RENDERER(o) ( G_TYPE_CHECK_INSTANCE_CAST((o), \
TORRENT_CELL_RENDERER_TYPE, \
TorrentCellRenderer ) )
typedef struct TorrentCellRenderer TorrentCellRenderer;
typedef struct TorrentCellRendererClass TorrentCellRendererClass;
struct TorrentCellRenderer
{
GtkCellRenderer parent;
struct TorrentCellRendererPrivate * priv;
};
struct TorrentCellRendererClass
{
GtkCellRendererClass parent;
};
GType torrent_cell_renderer_get_type( void );
GtkCellRenderer * torrent_cell_renderer_new( void );
#endif

View File

@@ -398,9 +398,9 @@ render_ul_rate (GtkTreeViewColumn * column UNUSED,
if( rate < 0.01 )
g_object_set (renderer, "text", "", NULL);
else {
char * pch = readablespeed (rate);
g_object_set (renderer, "text", pch, NULL);
g_free (pch);
char speedStr[64];
tr_strlspeed( speedStr, rate, sizeof(speedStr) );
g_object_set( renderer, "text", speedStr, NULL );
}
}
@@ -416,9 +416,9 @@ render_dl_rate (GtkTreeViewColumn * column UNUSED,
if( rate < 0.01 )
g_object_set (renderer, "text", "", NULL);
else {
char * pch = readablespeed (rate);
g_object_set (renderer, "text", pch, NULL);
g_free (pch);
char speedStr[64];
tr_strlspeed( speedStr, rate, sizeof(speedStr) );
g_object_set( renderer, "text", speedStr, NULL );
}
}
@@ -739,6 +739,7 @@ static GtkWidget* info_page_new (tr_torrent * tor)
GtkWidget *l, *w, *fr;
char *pch;
char *dname, *bname;
char sizeStr[128];
char buf[256];
char name[128];
const char * namefmt = "%s:";
@@ -759,11 +760,10 @@ static GtkWidget* info_page_new (tr_torrent * tor)
g_free (pch);
g_snprintf (name, sizeof(name), namefmt, _("Pieces"));
pch = readablesize (info->pieceSize);
g_snprintf (buf, sizeof(buf), "%d (%s)", info->pieceCount, pch);
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_free (pch);
g_snprintf (name, sizeof(name), namefmt, _("Hash"));
l = gtk_label_new (info->hashString);
@@ -853,7 +853,10 @@ 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, *pch2, *pch3;
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);
@@ -863,33 +866,25 @@ refresh_activity (GtkWidget * top)
gtk_label_set_text (GTK_LABEL(a->progress_lb), pch);
g_free (pch);
pch = readablesize( stat->haveValid + stat->haveUnchecked );
pch2 = readablesize( stat->haveValid );
pch3 = g_strdup_printf( _("%s (%s verified)"), pch, pch2 );
gtk_label_set_text( GTK_LABEL( a->have_lb ), pch3 );
g_free( pch3 );
g_free( pch2 );
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 );
pch = readablesize (stat->downloadedEver);
gtk_label_set_text (GTK_LABEL(a->dl_lb), pch);
g_free (pch);
tr_strlsize( sizeStr, stat->downloadedEver, sizeof(sizeStr) );
gtk_label_set_text( GTK_LABEL(a->dl_lb), sizeStr );
pch = readablesize (stat->uploadedEver);
gtk_label_set_text (GTK_LABEL(a->ul_lb), pch);
g_free (pch);
tr_strlsize( sizeStr, stat->uploadedEver, sizeof(sizeStr) );
gtk_label_set_text( GTK_LABEL(a->ul_lb), sizeStr );
pch = readablesize (stat->corruptEver);
gtk_label_set_text (GTK_LABEL(a->failed_lb), pch);
g_free (pch);
tr_strlsize( sizeStr, stat->corruptEver, sizeof(sizeStr) );
gtk_label_set_text (GTK_LABEL(a->failed_lb), sizeStr );
pch = g_strdup_printf( "%.1f", stat->ratio );
gtk_label_set_text (GTK_LABEL(a->ratio_lb), pch);
g_free (pch);
g_snprintf( buf, sizeof(buf), "%.1f", stat->ratio );
gtk_label_set_text (GTK_LABEL(a->ratio_lb), buf );
pch = readablespeed (stat->swarmspeed);
gtk_label_set_text (GTK_LABEL(a->swarm_lb), pch);
g_free (pch);
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"));
@@ -1112,12 +1107,13 @@ getdirtotals( GtkTreeStore * store, GtkTreeIter * parent )
GtkTreeModel * model;
GtkTreeIter iter;
uint64_t mysize, subsize;
char * sizestr, * name, * label;
char * name, * label;
model = GTK_TREE_MODEL( store );
mysize = 0;
if( gtk_tree_model_iter_children( model, &iter, parent ) ) do
{
char sizeStr[64];
if( gtk_tree_model_iter_has_child( model, &iter ) )
{
subsize = getdirtotals( store, &iter );
@@ -1128,10 +1124,9 @@ getdirtotals( GtkTreeStore * store, GtkTreeIter * parent )
gtk_tree_model_get( model, &iter, FC_SIZE, &subsize, -1 );
}
gtk_tree_model_get( model, &iter, FC_LABEL, &name, -1 );
sizestr = readablesize( subsize );
tr_strlsize( sizeStr, subsize, sizeof( sizeStr ) );
label = g_markup_printf_escaped( "<small>%s (%s)</small>",
name, sizestr );
g_free( sizestr );
name, sizeStr );
g_free( name );
gtk_tree_store_set( store, &iter, FC_LABEL, label, -1 );
g_free( label );
@@ -1679,22 +1674,21 @@ GtkWidget*
torrent_inspector_new ( GtkWindow * parent, TrTorrent * gtor )
{
guint tag;
char *size, *pch;
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 */
size = readablesize( info->totalSize );
pch = g_strdup_printf( _( "Details for %s (%s)" ), info->name, size );
d = gtk_dialog_new_with_buttons (pch, parent, 0,
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);
g_object_weak_ref (G_OBJECT(gtor), torrent_destroyed, d);
g_free( pch );
g_free( size );
/* add the notebook */
n = gtk_notebook_new ();

View File

@@ -1,352 +0,0 @@
/******************************************************************************
* $Id$
*
* Copyright (c) 2006-2007 Transmission authors and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*****************************************************************************/
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <libtransmission/transmission.h>
#include "tr_cell_renderer_progress.h"
#include "util.h"
enum {
P_TEXT = 1,
P_MARKUP,
P_SPACER,
P_PROG,
P_SINGLE,
};
static void
class_init(gpointer gclass, gpointer gdata);
static void
init(GTypeInstance *instance, gpointer gclass);
static void
set_property(GObject *obj, guint id, const GValue *val, GParamSpec *pspec);
static void
get_property(GObject *obj, guint id, GValue *val, GParamSpec *pspec);
static void
dispose(GObject *obj);
static void
finalize(GObject *obj);
static void
get_size(GtkCellRenderer *rend, GtkWidget *widget, GdkRectangle *cell,
gint *xoff, gint *yoff, gint *width, gint *height);
static void
render(GtkCellRenderer *rend, GdkWindow *win, GtkWidget *widget,
GdkRectangle *bg, GdkRectangle *cell, GdkRectangle *expose,
GtkCellRendererState flags);
static GtkStyle *
getstyle(TrCellRendererProgress *self, GtkWidget *wid, GdkWindow *win);
GType
tr_cell_renderer_progress_get_type(void) {
static GType type = 0;
if(0 == type) {
static const GTypeInfo info = {
sizeof (TrCellRendererProgressClass),
NULL, /* base_init */
NULL, /* base_finalize */
class_init, /* class_init */
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (TrCellRendererProgress),
0, /* n_preallocs */
init, /* instance_init */
NULL,
};
type = g_type_register_static(GTK_TYPE_CELL_RENDERER,
"TrCellRendererProgress", &info, 0);
}
return type;
}
static void
class_init(gpointer gclass, gpointer gdata SHUTUP) {
GObjectClass *gobjclass = G_OBJECT_CLASS(gclass);
GtkCellRendererClass *rendclass = GTK_CELL_RENDERER_CLASS(gclass);
GParamSpec *pspec;
gobjclass->set_property = set_property;
gobjclass->get_property = get_property;
gobjclass->dispose = dispose;
gobjclass->finalize = finalize;
rendclass->get_size = get_size;
rendclass->render = render;
pspec = g_param_spec_string("markup", "Markup", "Marked up text to render",
NULL, G_PARAM_READWRITE);
g_object_class_install_property(gobjclass, P_MARKUP, pspec);
pspec = g_param_spec_string("bar-sizing", "Bar sizing",
"Text to determine size of progress bar",
NULL, G_PARAM_READWRITE);
g_object_class_install_property(gobjclass, P_SPACER, pspec);
pspec = g_param_spec_float("progress", "Progress", "Progress",
0.0, 1.0, 0.0, G_PARAM_READWRITE);
g_object_class_install_property(gobjclass, P_PROG, pspec);
pspec = g_param_spec_boolean("show-text", "Show text", "Show marked up text",
TRUE, G_PARAM_READWRITE);
g_object_class_install_property(gobjclass, P_SINGLE, pspec);
}
static void
init(GTypeInstance *instance, gpointer gclass SHUTUP) {
TrCellRendererProgress *self = (TrCellRendererProgress *)instance;
self->rend = gtk_cell_renderer_text_new();
self->style = NULL;
self->text = NULL;
self->spacer = NULL;
self->barwidth = -1;
self->barheight = -1;
self->progress = 0.0;
self->single = FALSE;
self->disposed = FALSE;
g_object_ref(self->rend);
gtk_object_sink(GTK_OBJECT(self->rend));
}
static void
set_property(GObject *obj, guint id, const GValue *val,
GParamSpec *pspec) {
TrCellRendererProgress *self = (TrCellRendererProgress*)obj;
if(self->disposed)
return;
switch(id) {
case P_MARKUP:
g_free(self->text);
self->text = g_value_dup_string(val);
g_object_set_property(G_OBJECT(self->rend), "markup", val);
break;
case P_SPACER:
g_free(self->spacer);
self->spacer = g_value_dup_string(val);
self->barwidth = -1;
self->barheight = -1;
break;
case P_PROG:
self->progress = g_value_get_float(val);
break;
case P_SINGLE:
self->single = !g_value_get_boolean(val);
self->barwidth = -1;
self->barheight = -1;
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, id, pspec);
break;
}
}
static void
get_property(GObject *obj, guint id, GValue *val,
GParamSpec *pspec) {
TrCellRendererProgress *self = (TrCellRendererProgress*)obj;
if(self->disposed)
return;
switch(id) {
case P_MARKUP:
g_value_set_string(val, self->text);
break;
case P_SPACER:
g_value_set_string(val, self->spacer);
break;
case P_PROG:
g_value_set_float(val, self->progress);
break;
case P_SINGLE:
g_value_set_boolean(val, !self->single);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, id, pspec);
break;
}
}
static void
dispose(GObject *obj) {
GObjectClass *parent =
g_type_class_peek(g_type_parent(TR_CELL_RENDERER_PROGRESS_TYPE));
TrCellRendererProgress *self = (TrCellRendererProgress*)obj;
if(self->disposed)
return;
self->disposed = TRUE;
g_object_unref(self->rend);
tr_cell_renderer_progress_reset_style(self);
/* Chain up to the parent class */
parent->dispose(obj);
}
static void
finalize(GObject *obj) {
GObjectClass *parent =
g_type_class_peek(g_type_parent(TR_CELL_RENDERER_PROGRESS_TYPE));
TrCellRendererProgress *self = (TrCellRendererProgress*)obj;
g_free(self->text);
g_free(self->spacer);
/* Chain up to the parent class */
parent->finalize(obj);
}
GtkCellRenderer *
tr_cell_renderer_progress_new(void) {
return g_object_new(TR_CELL_RENDERER_PROGRESS_TYPE, NULL);
}
static void
get_size(GtkCellRenderer *rend, GtkWidget *wid, GdkRectangle *cell,
gint *xoff, gint *yoff, gint *width, gint *height) {
TrCellRendererProgress *self;
GdkRectangle rect;
int xpad, ypad;
char *sizing;
TR_IS_CELL_RENDERER_PROGRESS(rend);
self = TR_CELL_RENDERER_PROGRESS(rend);
sizing = (self->single ? self->spacer : self->text);
/* calculate and cache minimum bar width and height */
if(0 > self->barwidth || 0 > self->barheight) {
xpad = self->rend->xpad;
ypad = self->rend->ypad;
g_object_set(self->rend, "markup", self->spacer, "xpad", 0, "ypad", 0,
NULL);
gtk_cell_renderer_get_size(self->rend, wid, NULL, NULL, NULL,
&self->barwidth, &self->barheight);
g_object_set(self->rend, "markup", sizing, "xpad", xpad, "ypad", ypad,
NULL);
}
if(self->single) {
gtk_cell_renderer_get_size(self->rend, wid, cell,
xoff, yoff, width, height);
} else {
/* get the text size */
if(NULL != cell) {
rect = *cell;
rect.height -= self->barheight;
cell = &rect;
}
gtk_cell_renderer_get_size(self->rend, wid, cell,
xoff, yoff, width, height);
if(NULL != width && self->barwidth > *width)
*width = self->barwidth;
if(NULL != height)
*height += self->barheight + (NULL == yoff ? 0 : *yoff);
}
}
static void
render(GtkCellRenderer *rend, GdkWindow *win, GtkWidget *wid, GdkRectangle *bg,
GdkRectangle *cell, GdkRectangle *expose, GtkCellRendererState flags) {
TrCellRendererProgress *self;
GdkRectangle rect, full, empty;
GtkStyle *style;
TR_IS_CELL_RENDERER_PROGRESS(rend);
self = TR_CELL_RENDERER_PROGRESS(rend);
style = getstyle(self, wid, win);
/* make sure we have a cached bar width */
if(0 > self->barwidth || 0 > self->barheight)
get_size(rend, wid, NULL, NULL, NULL, NULL, NULL);
g_assert(0 < self->barwidth && 0 < self->barheight);
/* set up the dimensions for the bar and text */
rect = *cell;
rect.x += rend->xpad + style->xthickness;
rect.y += rend->ypad + style->ythickness;
rect.width -= (rend->xpad + style->xthickness) * 2;
rect.height -= (rend->ypad + style->ythickness) * 2;
empty = rect;
empty.height = self->barheight;
full = empty;
full.x += style->xthickness;
full.y += style->ythickness;
full.width -= style->xthickness * 2;
full.height -= style->ythickness * 2;
rect.y += self->barheight;
rect.height -= self->barheight;
/* draw the bar background */
gtk_paint_box(style, win, GTK_STATE_NORMAL, GTK_SHADOW_IN, NULL, wid,
"trough", empty.x, empty.y, empty.width, empty.height);
/* figure the width of the complete portion of the bar */
if(PANGO_DIRECTION_RTL ==
pango_context_get_base_dir(gtk_widget_get_pango_context(wid)))
full.x += full.width - (full.width * self->progress);
full.width *= self->progress;
/* draw the complete portion of the bar */
if(0 < full.width)
gtk_paint_box(style, win, GTK_STATE_PRELIGHT, GTK_SHADOW_OUT, NULL,
wid, "bar", full.x, full.y, full.width, full.height);
/* draw the text */
if(!self->single && 0 < rect.height)
gtk_cell_renderer_render(self->rend, win, wid, bg, &rect, expose, flags);
}
/* ugly hack to get the style for GtkProgressBar */
static GtkStyle *
getstyle(TrCellRendererProgress *self, GtkWidget *wid, GdkWindow *win) {
if(NULL == self->style) {
self->style = gtk_rc_get_style_by_paths(gtk_widget_get_settings(wid), NULL,
NULL, GTK_TYPE_PROGRESS_BAR);
if(NULL != self->style)
self->style = gtk_style_attach(gtk_style_ref(self->style), win);
}
return (NULL == self->style ? wid->style : self->style);
}
/* hack to make the GtkProgressBar style hack work when the theme changes */
void
tr_cell_renderer_progress_reset_style(TrCellRendererProgress *self) {
TR_IS_CELL_RENDERER_PROGRESS(self);
if(NULL != self->style) {
gtk_style_detach(self->style);
gtk_style_unref(self->style);
self->style = NULL;
}
}

View File

@@ -1,76 +0,0 @@
/******************************************************************************
* $Id$
*
* Copyright (c) 2006-2007 Transmission authors and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*****************************************************************************/
#ifndef TR_CELL_RENDERER_PROGRESS_H
#define TR_CELL_RENDERER_PROGRESS_H
#include <glib-object.h>
#include <gtk/gtk.h>
#define TR_CELL_RENDERER_PROGRESS_TYPE (tr_cell_renderer_progress_get_type())
#define TR_CELL_RENDERER_PROGRESS(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), TR_CELL_RENDERER_PROGRESS_TYPE, \
TrCellRendererProgress))
#define TR_CELL_RENDERER_PROGRESS_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass), TR_CELL_RENDERER_PROGRESS_TYPE,\
TrCellRendererProgressClass))
#define TR_IS_CELL_RENDERER_PROGRESS(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj), TR_CELL_RENDERER_PROGRESS_TYPE))
#define TR_IS_CELL_RENDERER_PROGRESS_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass), TR_CELL_RENDERER_PROGRESS_TYPE))
#define TR_CELL_RENDERER_PROGRESS_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS((obj), TR_CELL_RENDERER_PROGRESS_TYPE, \
TrCellRendererProgressClass))
typedef struct _TrCellRendererProgress TrCellRendererProgress;
typedef struct _TrCellRendererProgressClass TrCellRendererProgressClass;
/* treat the contents of this structure as private */
struct _TrCellRendererProgress {
GtkCellRenderer parent;
GtkCellRenderer *rend;
GtkStyle *style;
char *text;
char *spacer;
int barwidth;
int barheight;
gfloat progress;
gboolean single;
gboolean disposed;
};
struct _TrCellRendererProgressClass {
GtkCellRendererClass parent;
};
GType
tr_cell_renderer_progress_get_type(void);
GtkCellRenderer *
tr_cell_renderer_progress_new(void);
void
tr_cell_renderer_progress_reset_style(TrCellRendererProgress *self);
#endif

View File

@@ -171,84 +171,169 @@ tr_core_class_init( gpointer g_class, gpointer g_class_data SHUTUP )
}
static int
compareProgress( GtkTreeModel * model,
GtkTreeIter * a,
GtkTreeIter * b,
gpointer user_data UNUSED )
compareByActivity( GtkTreeModel * model,
GtkTreeIter * a,
GtkTreeIter * b,
gpointer user_data UNUSED )
{
int ia, ib;
gfloat rateUpA, rateUpB;
gfloat rateDownA, rateDownB;
gfloat percentDoneA, percentDoneB;
guint64 uploadedEverA, uploadedEverB;
tr_torrent *ta, *tb;
const tr_stat *sa, *sb;
gtk_tree_model_get( model, a, MC_PROG_D, &percentDoneA,
MC_DRATE, &rateDownA,
MC_URATE, &rateUpA,
MC_UP, &uploadedEverA,
-1 );
gtk_tree_model_get( model, b, MC_PROG_D, &percentDoneB,
MC_DRATE, &rateDownB,
MC_URATE, &rateUpB,
MC_UP, &uploadedEverB,
-1 );
gtk_tree_model_get( model, a, MC_TORRENT_RAW, &ta, -1 );
gtk_tree_model_get( model, b, MC_TORRENT_RAW, &tb, -1 );
ia = (int)( rateUpA + rateDownA );
ib = (int)( rateUpB + rateDownB );
sa = tr_torrentStat( ta );
sb = tr_torrentStat( tb );
ia = (int)( 1024 * ( sa->rateUpload + sa->rateDownload ) );
ib = (int)( 1024 * ( sb->rateUpload + sb->rateDownload ) );
if( ia != ib )
return ia - ib;
ia = (int)( 100.0 * percentDoneA );
ib = (int)( 100.0 * percentDoneB );
if( ia != ib )
return ia - ib;
if( uploadedEverA != uploadedEverB )
return uploadedEverA < uploadedEverB ? -1 : 1;
if( sa->uploadedEver != sb->uploadedEver )
return sa->uploadedEver < sa->uploadedEver ? -1 : 1;
return 0;
}
#define STR_REVERSE "reverse-"
#define STR_PROGRESS "progress"
#define STR_NAME "name"
static int
compareByDateAdded( GtkTreeModel * model UNUSED,
GtkTreeIter * a UNUSED,
GtkTreeIter * b UNUSED,
gpointer user_data UNUSED )
{
/* FIXME */
return 0;
}
static int
compareByName( GtkTreeModel * model,
GtkTreeIter * a,
GtkTreeIter * b,
gpointer user_data UNUSED )
{
int ret;
char *ca, *cb;
gtk_tree_model_get( model, a, MC_NAME_COLLATED, &ca, -1 );
gtk_tree_model_get( model, b, MC_NAME_COLLATED, &cb, -1 );
ret = strcmp( ca, cb );
g_free( cb );
g_free( ca );
return ret;
}
static int
compareByProgress( GtkTreeModel * model,
GtkTreeIter * a,
GtkTreeIter * b,
gpointer user_data UNUSED )
{
TrTorrent *ta, *tb;
const tr_stat *sa, *sb;
int ret;
gtk_tree_model_get( model, a, MC_TORRENT, &ta, -1 );
gtk_tree_model_get( model, b, MC_TORRENT, &tb, -1 );
sa = tr_torrent_stat( ta );
sb = tr_torrent_stat( tb );
if( sa->percentDone < sb->percentDone ) ret = -1;
else if( sa->percentDone > sb->percentDone ) ret = 1;
else ret = 0;
g_object_unref( G_OBJECT( tb ) );
g_object_unref( G_OBJECT( ta ) );
return ret;
}
static int
compareByState( GtkTreeModel * model,
GtkTreeIter * a,
GtkTreeIter * b,
gpointer user_data UNUSED )
{
TrTorrent *ta, *tb;
const tr_stat *sa, *sb;
int ret;
gtk_tree_model_get( model, a, MC_TORRENT, &ta, -1 );
gtk_tree_model_get( model, b, MC_TORRENT, &tb, -1 );
sa = tr_torrent_stat( ta );
sb = tr_torrent_stat( tb );
ret = sa->status - sb->status;
g_object_unref( G_OBJECT( ta ) );
g_object_unref( G_OBJECT( tb ) );
return ret;
}
static int
compareByTracker( GtkTreeModel * model,
GtkTreeIter * a,
GtkTreeIter * b,
gpointer user_data UNUSED )
{
TrTorrent *ta, *tb;
const tr_info *ia, *ib;
int ret;
gtk_tree_model_get( model, a, MC_TORRENT, &ta, -1 );
gtk_tree_model_get( model, b, MC_TORRENT, &tb, -1 );
ia = tr_torrent_info( ta );
ib = tr_torrent_info( tb );
ret = strcmp( ia->primaryAddress, ib->primaryAddress );
g_object_unref( G_OBJECT( ta ) );
g_object_unref( G_OBJECT( tb ) );
return ret;
}
/***
****
***/
static void
onSortColumnChanged( GtkTreeSortable * sortable, gpointer unused UNUSED )
setSort( TrCore * core, const char * mode, gboolean isReversed )
{
int column;
GtkSortType order;
if( gtk_tree_sortable_get_sort_column_id( sortable, &column, &order ) )
int col = MC_TORRENT_RAW;
GtkSortType type;
GtkTreeSortable * sortable = GTK_TREE_SORTABLE( core->model );
if( !strcmp( mode, "sort-by-activity" ) )
{
GString * gstr = g_string_new( NULL );
switch( column ) {
case MC_PROG_D: g_string_assign( gstr, STR_PROGRESS ); break;
default: g_string_assign( gstr, STR_NAME ); break;
}
if( order == GTK_SORT_DESCENDING )
g_string_prepend( gstr, STR_REVERSE );
pref_string_set( PREF_KEY_SORT_COLUMN, gstr->str );
g_string_free( gstr, TRUE );
type = isReversed ? GTK_SORT_ASCENDING : GTK_SORT_DESCENDING;
gtk_tree_sortable_set_sort_func( sortable, col, compareByActivity, NULL, NULL );
}
else if( !strcmp( mode, "sort-by-date-added" ) )
{
type = isReversed ? GTK_SORT_ASCENDING : GTK_SORT_DESCENDING;
gtk_tree_sortable_set_sort_func( sortable, col, compareByDateAdded, NULL, NULL );
}
else if( !strcmp( mode, "sort-by-progress" ) )
{
type = isReversed ? GTK_SORT_ASCENDING : GTK_SORT_DESCENDING;
gtk_tree_sortable_set_sort_func( sortable, col, compareByProgress, NULL, NULL );
}
else if( !strcmp( mode, "sort-by-state" ) )
{
type = isReversed ? GTK_SORT_ASCENDING : GTK_SORT_DESCENDING;
gtk_tree_sortable_set_sort_func( sortable, col, compareByState, NULL, NULL );
}
else if( !strcmp( mode, "sort-by-tracker" ) )
{
type = isReversed ? GTK_SORT_ASCENDING : GTK_SORT_DESCENDING;
gtk_tree_sortable_set_sort_func( sortable, col, compareByTracker, NULL, NULL );
}
else
{
type = isReversed ? GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;
gtk_tree_sortable_set_sort_func( sortable, col, compareByName, NULL, NULL );
}
gtk_tree_sortable_set_sort_column_id( sortable, col, type );
}
void
tr_core_set_sort_column_from_prefs( TrCore * core )
tr_core_resort( TrCore * core )
{
char * val = pref_string_get( PREF_KEY_SORT_COLUMN );
char * freeme = val;
gint column;
GtkSortType order = GTK_SORT_ASCENDING;
if( g_str_has_prefix( val, STR_REVERSE ) ) {
order = GTK_SORT_DESCENDING;
val += strlen( STR_REVERSE );
}
if( !strcmp( val, STR_PROGRESS ) )
column = MC_PROG_D;
else /* default */
column = MC_NAME;
gtk_tree_sortable_set_sort_column_id ( GTK_TREE_SORTABLE( core->model ), column, order );
g_free( freeme );
char * mode = pref_string_get( PREF_KEY_SORT_MODE );
gboolean isReversed = pref_flag_get( PREF_KEY_SORT_REVERSED );
setSort( core, mode, isReversed );
g_free( mode );
}
static void
@@ -259,18 +344,13 @@ tr_core_init( GTypeInstance * instance, gpointer g_class SHUTUP )
/* column types for the model used to store torrent information */
/* keep this in sync with the enum near the bottom of tr_core.h */
GType types[] =
{
/* info->name, info->totalSize, info->hashString, status, */
G_TYPE_STRING, G_TYPE_UINT64, G_TYPE_STRING, G_TYPE_INT,
/* error, errorString, percentComplete, percentDone, rateDownload, rateUpload, */
G_TYPE_INT, G_TYPE_STRING, G_TYPE_FLOAT, G_TYPE_FLOAT, G_TYPE_FLOAT, G_TYPE_FLOAT,
/* eta, peersConnected, peersUploading, peersDownloading, seeders, */
G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT,
/* leechers, completedFromTracker, downloaded, uploaded */
G_TYPE_INT, G_TYPE_INT, G_TYPE_UINT64, G_TYPE_UINT64,
/* ratio, left, TrTorrent object, ID for IPC */
G_TYPE_FLOAT, G_TYPE_UINT64, TR_TORRENT_TYPE, G_TYPE_INT,
GType types[] = {
G_TYPE_STRING, /* name */
G_TYPE_STRING, /* collated name */
G_TYPE_STRING, /* hash string */
TR_TORRENT_TYPE, /* TrTorrent object */
G_TYPE_POINTER, /* tr_torrent* */
G_TYPE_INT /* ID for IPC */
};
#ifdef REFDBG
@@ -280,12 +360,6 @@ tr_core_init( GTypeInstance * instance, gpointer g_class SHUTUP )
/* create the model used to store torrent data */
g_assert( ALEN( types ) == MC_ROW_COUNT );
store = gtk_list_store_newv( MC_ROW_COUNT, types );
g_signal_connect( store, "sort-column-changed", G_CALLBACK(onSortColumnChanged), NULL );
gtk_tree_sortable_set_sort_func( GTK_TREE_SORTABLE(store),
MC_PROG_D,
compareProgress,
NULL, NULL );
self->model = GTK_TREE_MODEL( store );
self->handle = tr_init( "gtk" );
@@ -346,20 +420,47 @@ tr_core_handle( TrCore * self )
return self->disposed ? NULL : self->handle;
}
static char*
doCollate( const char * in )
{
const char * end = in + strlen( in );
char * casefold;
char * ret;
while( in < end ) {
const gunichar ch = g_utf8_get_char( in );
if (!g_unichar_isalnum (ch)) // eat everything before the first alnum
in += g_unichar_to_utf8( ch, NULL );
else
break;
}
if ( in == end )
return g_strdup ("");
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 )
{
const tr_info * inf = tr_torrent_info( tor );
char * collated = doCollate( inf->name );
GtkTreeIter unused;
gtk_list_store_insert_with_values( GTK_LIST_STORE( self->model ), &unused, 0,
MC_NAME, inf->name,
MC_SIZE, inf->totalSize,
MC_HASH, inf->hashString,
MC_TORRENT, tor,
MC_ID, self->nextid,
MC_NAME, inf->name,
MC_NAME_COLLATED, collated,
MC_HASH, inf->hashString,
MC_TORRENT, tor,
MC_TORRENT_RAW, tor->handle,
MC_ID, self->nextid,
-1);
g_object_unref( tor );
self->nextid++;
g_object_unref( tor );
g_free( collated );
}
int
@@ -532,34 +633,11 @@ update_foreach( GtkTreeModel * model,
gpointer data UNUSED)
{
TrTorrent * tor;
const tr_stat * st;
gtk_tree_model_get( model, iter, MC_TORRENT, &tor, -1 );
st = tr_torrent_stat( tor );
tr_torrent_check_seeding_cap ( tor );
g_object_unref( tor );
gtk_list_store_set( GTK_LIST_STORE( model ), iter,
MC_STAT, st->status,
MC_ERR, st->error,
MC_TERR, st->errorString,
MC_PROG_C, st->percentComplete,
MC_PROG_D, st->percentDone,
MC_DRATE, st->rateDownload,
MC_URATE, st->rateUpload,
MC_ETA, st->eta,
MC_PEERS, st->peersConnected,
MC_UPEERS, st->peersGettingFromUs,
MC_DPEERS, st->peersSendingToUs,
MC_SEED, st->seeders,
MC_LEECH, st->leechers,
MC_DONE, st->completedFromTracker,
MC_DOWN, st->downloadedEver,
MC_UP, st->uploadedEver,
MC_RATIO, st->ratio,
MC_LEFT, st->leftUntilDone,
-1 );
return FALSE;
}
@@ -628,6 +706,15 @@ tr_core_set_pref_bool( TrCore * self, const char * key, gboolean newval )
}
}
gboolean
tr_core_toggle_pref_bool( TrCore * core, const char * key )
{
const gboolean newval = !pref_flag_get( key );
pref_flag_set( key, newval );
commitPrefsChange( core, key );
return newval;
}
void
tr_core_set_pref_int( TrCore * self, const char * key, int newval )
{

View File

@@ -162,6 +162,8 @@ tr_core_quit( TrCore * self );
void
tr_core_set_pref( TrCore * self, const char * key, const char * val );
gboolean
tr_core_toggle_pref_bool( TrCore * core, const char * key );
/* Set a boolean preference value, save the prefs file, and emit the
"prefs-changed" signal */
void
@@ -173,16 +175,19 @@ void
tr_core_set_pref_int( TrCore * self, const char * key, int val );
void
tr_core_set_sort_column_from_prefs( TrCore * core );
tr_core_resort( TrCore * core );
/* 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 */
enum {
MC_NAME, MC_SIZE, MC_HASH, MC_STAT, MC_ERR, MC_TERR,
MC_PROG_C, MC_PROG_D, MC_DRATE, MC_URATE, MC_ETA, MC_PEERS,
MC_UPEERS, MC_DPEERS, MC_SEED, MC_LEECH, MC_DONE,
MC_DOWN, MC_UP, MC_RATIO, MC_LEFT, MC_TORRENT, MC_ID,
MC_ROW_COUNT
enum
{
MC_NAME,
MC_NAME_COLLATED,
MC_HASH,
MC_TORRENT,
MC_TORRENT_RAW,
MC_ID,
MC_ROW_COUNT
};
#endif

View File

@@ -50,7 +50,9 @@ tr_prefs_init_global( void )
pref_int_set_default ( PREF_KEY_MSGLEVEL, TR_MSG_INF );
pref_string_set_default ( PREF_KEY_SORT_COLUMN, "name" );
pref_string_set_default ( PREF_KEY_SORT_MODE, "sort-by-name" );
pref_flag_set_default ( PREF_KEY_SORT_REVERSED, FALSE );
pref_flag_set_default ( PREF_KEY_MINIMAL_VIEW, FALSE );
pref_save( NULL );
}
@@ -219,7 +221,7 @@ test_port( gpointer l )
g_usleep( G_USEC_PER_SEC * 3 ); /* give portmapping time to kick in */
snprintf( url, sizeof(url), "http://transmission.m0k.org/PortCheck.php?port=%d", port );
text = miniwget( url, &size );
g_message(" got len %d, [%*.*s]", size, size, size, text );
/*g_message(" got len %d, [%*.*s]", size, size, size, text );*/
isOpen = text && *text=='1';
gtk_label_set_markup( GTK_LABEL(l), isOpen ? _("Port is <b>open</b>") : _("Port is <b>closed</b>") );

View File

@@ -35,7 +35,9 @@ GtkWidget * tr_prefs_dialog_new( GObject * core, GtkWindow * parent );
#define PREF_KEY_ASKQUIT "prompt-before-exit"
#define PREF_KEY_ENCRYPTED_ONLY "encrypted-connections-only"
#define PREF_KEY_MSGLEVEL "debug-message-level"
#define PREF_KEY_SORT_COLUMN "sort-column"
#define PREF_KEY_SORT_MODE "sort-mode"
#define PREF_KEY_SORT_REVERSED "sort-reversed"
#define PREF_KEY_MINIMAL_VIEW "minimal-view"
void tr_prefs_init_global( void );

View File

@@ -45,7 +45,6 @@ tr_torrent_init(GTypeInstance *instance, gpointer g_class UNUSED )
#endif
self->handle = NULL;
self->lastStatTime = 0;
self->delfile = NULL;
self->severed = FALSE;
self->disposed = FALSE;
@@ -130,9 +129,6 @@ tr_torrent_handle(TrTorrent *tor)
static tr_stat*
refreshStat( TrTorrent * tor )
{
tor->lastStatTime= time( NULL );
tor->stat = *tr_torrentStat( tor->handle );
return &tor->stat;
}
const tr_stat *
@@ -140,10 +136,7 @@ tr_torrent_stat(TrTorrent *tor)
{
g_assert( TR_IS_TORRENT(tor) );
if( !tor->severed && tor->lastStatTime!=time(NULL) )
refreshStat( tor );
return &tor->stat;
return tor->severed ? NULL : tr_torrentStat( tor->handle );
}
const tr_info *

View File

@@ -50,8 +50,6 @@ struct _TrTorrent {
GObject parent;
tr_torrent *handle;
char *delfile;
tr_stat stat;
time_t lastStatTime;
/* FIXME: hm, are these heavyweight enough to deserve their own properties? */
gboolean severed;

View File

@@ -31,7 +31,7 @@
#include "actions.h"
#include "hig.h"
#include "tr_cell_renderer_progress.h"
#include "torrent-cell-renderer.h"
#include "tr_core.h"
#include "tr_torrent.h"
#include "tr_window.h"
@@ -45,7 +45,6 @@ typedef struct
GtkWidget * ul_lb;
GtkWidget * dl_lb;
GtkTreeSelection * selection;
GtkCellRenderer * namerend;
}
PrivateData;
@@ -61,91 +60,6 @@ get_private_data( TrWindow * w )
****
***/
static void
formatname( GtkTreeViewColumn * col UNUSED, GtkCellRenderer * rend,
GtkTreeModel * model, GtkTreeIter * iter, gpointer data UNUSED )
{
TrTorrent * gtor;
char * name, * mb, * str, * top, * bottom;
const char * fmt;
guint64 size;
int status, err, eta, tpeers, upeers, dpeers;
gtk_tree_model_get( model, iter, MC_NAME, &name, MC_STAT, &status,
MC_ERR, &err, MC_SIZE, &size,
MC_ETA, &eta, MC_PEERS, &tpeers, MC_UPEERS, &upeers,
MC_DPEERS, &dpeers, MC_TORRENT, &gtor, -1 );
tpeers = MAX( tpeers, 0 );
upeers = MAX( upeers, 0 );
dpeers = MAX( dpeers, 0 );
mb = readablesize(size);
top = tr_torrent_status_str ( gtor );
if( TR_OK != err )
{
char * terr;
gtk_tree_model_get( model, iter, MC_TERR, &terr, -1 );
bottom = g_strconcat( _("Error"), ": ", terr, NULL );
g_free( terr );
}
else if( TR_STATUS_DOWNLOAD & status )
{
bottom = g_strdup_printf( ngettext( "Downloading from %i of %i connections",
"Downloading from %i of %i connections",
tpeers ), dpeers, tpeers );
}
else
{
bottom = NULL;
}
fmt = err==TR_OK
? "<b>%s (%s)</b>\n<small>%s\n%s</small>"
: "<span color='red'><b>%s (%s)</b>\n<small>%s\n%s</small></span>";
str = g_markup_printf_escaped( fmt, name, mb, top, (bottom ? bottom : "") );
g_object_set( rend, "markup", str, NULL );
g_free( name );
g_free( mb );
g_free( str );
g_free( top );
g_free( bottom );
g_object_unref( gtor );
}
static void
formatprog( GtkTreeViewColumn * col UNUSED, GtkCellRenderer * rend,
GtkTreeModel * model, GtkTreeIter * iter, gpointer data UNUSED )
{
char * dlstr, * ulstr, * str, * marked;
gfloat prog, dl, ul, ratio;
guint64 down, up;
gtk_tree_model_get( model, iter, MC_PROG_D, &prog, MC_DRATE, &dl,
MC_URATE, &ul, MC_DOWN, &down, MC_UP, &up, MC_RATIO, &ratio, -1 );
prog = MAX( prog, 0.0 );
prog = MIN( prog, 1.0 );
ulstr = readablespeed (ul);
if( 1.0 == prog )
{
dlstr = g_strdup_printf( "%.1f", ratio );
str = g_strdup_printf( _("Ratio: %s\nUL: %s"), dlstr, ulstr );
}
else
{
dlstr = readablespeed( dl );
str = g_strdup_printf( _("DL: %s\nUL: %s"), dlstr, ulstr );
}
marked = g_markup_printf_escaped( "<small>%s</small>", str );
g_object_set( rend, "markup", str, "progress", prog, NULL );
g_free( dlstr );
g_free( ulstr );
g_free( str );
g_free( marked );
}
static void
on_popup_menu ( GtkWidget * self UNUSED, GdkEventButton * event )
{
@@ -170,36 +84,18 @@ makeview( PrivateData * p )
GtkWidget * view;
GtkTreeViewColumn * col;
GtkTreeSelection * sel;
GtkCellRenderer * namerend, * progrend;
char * str;
GtkCellRenderer * r;
view = gtk_tree_view_new();
gtk_tree_view_set_headers_visible( GTK_TREE_VIEW(view), FALSE );
p->selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(view) );
namerend = gtk_cell_renderer_text_new();
p->namerend = namerend;
/* note that this renderer is set to ellipsize, just not here */
col = gtk_tree_view_column_new_with_attributes( _("Name"), namerend,
NULL );
gtk_tree_view_column_set_cell_data_func( col, namerend, formatname,
NULL, NULL );
gtk_tree_view_column_set_expand( col, TRUE );
gtk_tree_view_column_set_sizing( col, GTK_TREE_VIEW_COLUMN_AUTOSIZE );
gtk_tree_view_column_set_sort_column_id( col, MC_NAME );
gtk_tree_view_append_column( GTK_TREE_VIEW( view ), col );
progrend = tr_cell_renderer_progress_new();
/* this string is only used to determine the size of the progress bar */
str = g_markup_printf_escaped( "<big>%s</big>", " fnord fnord " );
g_object_set( progrend, "bar-sizing", str, NULL );
g_free(str);
col = gtk_tree_view_column_new_with_attributes( _("Progress"), progrend,
NULL);
gtk_tree_view_column_set_cell_data_func( col, progrend, formatprog,
NULL, NULL );
r = torrent_cell_renderer_new( );
col = gtk_tree_view_column_new_with_attributes( _("Torrent"), r, "torrent", MC_TORRENT_RAW, NULL );
gtk_tree_view_column_set_sizing( col, GTK_TREE_VIEW_COLUMN_AUTOSIZE );
gtk_tree_view_column_set_sort_column_id( col, MC_PROG_D );
gtk_tree_view_append_column( GTK_TREE_VIEW( view ), col );
g_object_set( r, "xpad", GUI_PAD_SMALL, "ypad", GUI_PAD_SMALL, NULL );
gtk_tree_view_set_rules_hint( GTK_TREE_VIEW( view ), TRUE );
sel = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) );
@@ -222,7 +118,6 @@ realized_cb ( GtkWidget * wind, gpointer unused UNUSED )
PrivateData * p = get_private_data( GTK_WINDOW( wind ) );
sizingmagic( GTK_WINDOW(wind), GTK_SCROLLED_WINDOW( p->scroll ),
GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS );
g_object_set( p->namerend, "ellipsize", PANGO_ELLIPSIZE_END, NULL );
}
/***
@@ -289,19 +184,16 @@ void
tr_window_update( TrWindow * self, float downspeed, float upspeed )
{
PrivateData * p = get_private_data( self );
char *tmp1, *tmp2;
char speedStr[64];
char buf[128];
tmp1 = readablespeed( downspeed );
tmp2 = g_strdup_printf( _("Total DL: %s"), tmp1 );
gtk_label_set_text( GTK_LABEL(p->dl_lb), tmp2 );
g_free( tmp2 );
g_free( tmp1 );
tr_strlspeed( speedStr, downspeed, sizeof(speedStr) );
g_snprintf( buf, sizeof(buf), _("Total DL: %s"), speedStr );
gtk_label_set_text( GTK_LABEL(p->dl_lb), buf );
tmp1 = readablespeed( upspeed );
tmp2 = g_strdup_printf( _("Total UL: %s"), tmp1 );
gtk_label_set_text( GTK_LABEL(p->ul_lb), tmp2 );
g_free( tmp2 );
g_free( tmp1 );
tr_strlspeed( speedStr, upspeed, sizeof(speedStr) );
g_snprintf( buf, sizeof(buf), _("Total UL: %s"), speedStr );
gtk_label_set_text( GTK_LABEL(p->ul_lb), buf );
}
GtkTreeSelection*

View File

@@ -8,13 +8,24 @@ const char * fallback_ui_file =
" <menuitem action='pause-torrent'/>\n"
" <menuitem action='verify-torrent'/>\n"
" <menuitem action='remove-torrent'/>\n"
" <menuitem action='show-torrent-details'/>\n"
" <separator/>\n"
" <menuitem action='create-torrent'/>\n"
" <separator/>\n"
" <menuitem action='close'/>\n"
" <menuitem action='quit'/>\n"
" </menu>\n"
" <menu action='view-menu'>\n"
" <menuitem action='show-torrent-details'/>\n"
" <separator/>\n"
" <menuitem action='minimal-view'/>\n"
" <separator/>\n"
" <menuitem action='sort-by-activity'/>\n"
" <menuitem action='sort-by-name'/>\n"
" <menuitem action='sort-by-progress'/>\n"
" <menuitem action='sort-by-state'/>\n"
" <menuitem action='sort-by-tracker'/>\n"
" <menuitem action='reverse-sort-order'/>\n"
" </menu>\n"
" <menu action='edit-menu'>\n"
" <menuitem action='select-all'/>\n"
" <menuitem action='unselect-all'/>\n"

View File

@@ -38,8 +38,6 @@
#include "conf.h"
#include "util.h"
#define BESTDECIMAL(d) ( (d)<100 ? 1 : 0 )
static void
errcb(GtkWidget *wind, int resp, gpointer data);
@@ -52,40 +50,41 @@ tr_strcmp( const char * a, const char * b )
return 0;
}
static const char *sizestrs[] = {
N_("B"), N_("KiB"), N_("MiB"), N_("GiB"), N_("TiB"), N_("PiB"), N_("EiB"),
};
char *
readablesize(guint64 size) {
int ii;
double small = size;
if( !size )
return g_strdup_printf( _("None") );
for(ii = 0; ii + 1 < ALEN(sizestrs) && 1024.0 <= small / 1024.0; ii++)
small /= 1024.0;
if(1024.0 <= small) {
small /= 1024.0;
ii++;
}
return g_strdup_printf("%.*f %s", BESTDECIMAL(small), small,
gettext(sizestrs[ii]));
}
char *
readablespeed (double KiBps)
char*
tr_strlsize( char * buf, guint64 size, size_t buflen )
{
const guint64 bps = KiBps * 1024;
char * str = readablesize (bps);
char * ret = bps ? g_strdup_printf ("%s/s", str) : g_strdup( str );
g_free (str);
return ret;
if( !size )
g_strlcpy( buf, _("None"), buflen );
else {
static const char *units[] = {
N_("B"), N_("KiB"), N_("MiB"), N_("GiB"), N_("TiB"),
N_("PiB"), N_("EiB"), N_("ZiB"), N_("YiB")
};
unsigned int i;
guint64 small = size;
for( i=0; i<G_N_ELEMENTS(units) && small>=1024; ++i )
small >>= 10;
if( small < 100 )
g_snprintf( buf, buflen, "%.1f %s", (double)small, _(units[i]) );
else
g_snprintf( buf, buflen, "%d %s", (int)small, _(units[i]) );
}
return buf;
}
char*
tr_strlspeed( char * buf, double KiBps, size_t buflen )
{
const guint64 bps = KiBps * 1024;
if( !bps )
g_strlcpy( buf, _("None"), buflen );
else {
char bbuf[64];
tr_strlsize( bbuf, (guint64)(KiBps*1024), sizeof(bbuf) );
g_snprintf( buf, buflen, "%s/s", bbuf );
}
return buf;
}
#define SECONDS(s) ((s) % 60)
#define MINUTES(s) ((s) / 60 % 60)

View File

@@ -44,16 +44,11 @@ enum tr_torrent_action { TR_TOR_LEAVE, TR_TOR_COPY, TR_TOR_MOVE };
/* used for a callback function with a data parameter */
typedef void (*callbackfunc_t)(void*);
/* return a human-readable string for the size given in bytes.
the string must be g_free()d */
char *
readablesize(guint64 size);
/* return a human-readable string for the transfer rate given in bytes.
the string must be g_free()d */
char *
readablespeed (double KiBps);
/* return a human-readable string for the size given in bytes. */
char* tr_strlsize( char * buf, guint64 size, size_t buflen );
/* return a human-readable string for the transfer rate given in bytes. */
char* tr_strlspeed (char * buf, double KiBps, size_t buflen );
/* return a human-readable string for the time given in seconds.
the string must be g_free()d */