1 | /* |
---|
2 | * This file Copyright (C) 2007-2008 Charles Kerr <charles@rebelbase.com> |
---|
3 | * |
---|
4 | * This file is licensed by the GPL version 2. Works owned by the |
---|
5 | * Transmission project are granted a special exemption to clause 2(b) |
---|
6 | * so that the bulk of its code can remain under the MIT license. |
---|
7 | * This exemption does not extend to derived works not owned by |
---|
8 | * the Transmission project. |
---|
9 | * |
---|
10 | * $Id: details.c 6998 2008-10-31 18:25:21Z charles $ |
---|
11 | */ |
---|
12 | |
---|
13 | #include <errno.h> |
---|
14 | #include <stddef.h> |
---|
15 | #include <stdio.h> |
---|
16 | #include <stdlib.h> |
---|
17 | #include <glib/gi18n.h> |
---|
18 | #include <gtk/gtk.h> |
---|
19 | |
---|
20 | #include <libtransmission/transmission.h> |
---|
21 | #include <libtransmission/utils.h> /* tr_httpIsValidURL */ |
---|
22 | |
---|
23 | #include "actions.h" |
---|
24 | #include "details.h" |
---|
25 | #include "file-list.h" |
---|
26 | #include "tr-torrent.h" |
---|
27 | #include "tracker-list.h" |
---|
28 | #include "hig.h" |
---|
29 | #include "util.h" |
---|
30 | |
---|
31 | #define UPDATE_INTERVAL_MSEC 2000 |
---|
32 | |
---|
33 | /**** |
---|
34 | ***** PIECES VIEW |
---|
35 | ****/ |
---|
36 | |
---|
37 | /* define SHOW_PIECES */ |
---|
38 | |
---|
39 | #ifdef SHOW_PIECES |
---|
40 | static int |
---|
41 | getGridSize( int pieceCount, |
---|
42 | int * n_rows, |
---|
43 | int * n_cols ) |
---|
44 | { |
---|
45 | const int MAX_ACROSS = 16; |
---|
46 | |
---|
47 | if( pieceCount >= ( MAX_ACROSS * MAX_ACROSS ) ) |
---|
48 | { |
---|
49 | *n_rows = *n_cols = MAX_ACROSS; |
---|
50 | return MAX_ACROSS * MAX_ACROSS; |
---|
51 | } |
---|
52 | else |
---|
53 | { |
---|
54 | int i; |
---|
55 | for( i = 0; ( i * i ) < pieceCount; ++i ) ; |
---|
56 | *n_rows = *n_cols = i; |
---|
57 | return pieceCount; |
---|
58 | } |
---|
59 | } |
---|
60 | |
---|
61 | #define TO16( a ) ( (guint16)( ( a << 8 ) | ( a ) ) ) |
---|
62 | #define RGB_2_GDK( R, G, B ) { 0, TO16( R ), TO16( G ), TO16( B ) } |
---|
63 | |
---|
64 | enum { DRAW_AVAIL, DRAW_PROG }; |
---|
65 | |
---|
66 | static void |
---|
67 | release_gobject_array( gpointer data ) |
---|
68 | { |
---|
69 | int i; |
---|
70 | GObject **objects = (GObject**) data; |
---|
71 | |
---|
72 | for( i = 0; objects[i] != NULL; ++i ) |
---|
73 | g_object_unref ( G_OBJECT( objects[i] ) ); |
---|
74 | g_free ( objects ); |
---|
75 | } |
---|
76 | |
---|
77 | static gboolean |
---|
78 | refresh_pieces( GtkWidget * da, |
---|
79 | GdkEventExpose * event UNUSED, |
---|
80 | gpointer gtor ) |
---|
81 | { |
---|
82 | tr_torrent * tor = tr_torrent_handle( TR_TORRENT( gtor ) ); |
---|
83 | const tr_info * info = tr_torrent_info( TR_TORRENT( gtor ) ); |
---|
84 | int mode = |
---|
85 | GPOINTER_TO_INT ( g_object_get_data ( G_OBJECT( da ), "draw-mode" ) ); |
---|
86 | |
---|
87 | GdkColormap * colormap = gtk_widget_get_colormap ( da ); |
---|
88 | const int widget_w = da->allocation.width; |
---|
89 | const int widget_h = da->allocation.height; |
---|
90 | int n_rows, n_cols; |
---|
91 | const int n_cells = getGridSize ( info->pieceCount, &n_rows, |
---|
92 | &n_cols ); |
---|
93 | const GdkRectangle grid_bounds = { 0, 0, widget_w, widget_h }; |
---|
94 | const double piece_w = grid_bounds.width / (double)n_cols; |
---|
95 | const double piece_h = grid_bounds.height / (double)n_rows; |
---|
96 | const int piece_w_int = (int) ( piece_w + 1 ); /* pad for roundoff |
---|
97 | */ |
---|
98 | const int piece_h_int = (int) ( piece_h + 1 ); /* pad for roundoff |
---|
99 | */ |
---|
100 | const gboolean rtl = gtk_widget_get_direction( da ) == |
---|
101 | GTK_TEXT_DIR_RTL; |
---|
102 | |
---|
103 | guint8 * prev_color = NULL; |
---|
104 | gboolean first_time = FALSE; |
---|
105 | |
---|
106 | int i, x, y; |
---|
107 | int8_t * pieces = NULL; |
---|
108 | float * completeness = NULL; |
---|
109 | |
---|
110 | /** |
---|
111 | *** Get the Graphics Contexts... |
---|
112 | **/ |
---|
113 | |
---|
114 | enum { ALL, LOTS, SOME, FEW, NONE, |
---|
115 | BLACK, GRAY, BLINK, |
---|
116 | N_COLORS }; |
---|
117 | GdkGC ** gcs = (GdkGC**) g_object_get_data ( G_OBJECT( |
---|
118 | da ), |
---|
119 | "graphics-contexts" ); |
---|
120 | if( gcs == NULL ) |
---|
121 | { |
---|
122 | const GdkColor colors[N_COLORS] = { |
---|
123 | RGB_2_GDK ( 114, 159, 207 ), /* all */ |
---|
124 | RGB_2_GDK ( 52, 101, 164 ), /* lots */ |
---|
125 | RGB_2_GDK ( 32, 74, 135 ), /* some */ |
---|
126 | RGB_2_GDK ( 85, 87, 83 ), /* few */ |
---|
127 | RGB_2_GDK ( 238, 238, 236 ), /* none - tango aluminum highlight */ |
---|
128 | RGB_2_GDK ( 46, 52, 54 ), /* black - tango slate shadow */ |
---|
129 | RGB_2_GDK ( 186, 189, 182 ), /* gray - tango aluminum shadow */ |
---|
130 | RGB_2_GDK ( 252, 233, 79 ), /* blink - tango butter highlight */ |
---|
131 | }; |
---|
132 | |
---|
133 | gcs = g_new ( GdkGC *, N_COLORS + 1 ); |
---|
134 | |
---|
135 | for( i = 0; i < N_COLORS; ++i ) |
---|
136 | { |
---|
137 | gcs[i] = gdk_gc_new ( da->window ); |
---|
138 | gdk_gc_set_colormap ( gcs[i], colormap ); |
---|
139 | gdk_gc_set_rgb_fg_color ( gcs[i], &colors[i] ); |
---|
140 | gdk_gc_set_rgb_bg_color ( gcs[i], &colors[i] ); |
---|
141 | } |
---|
142 | |
---|
143 | gcs[N_COLORS] = NULL; /* a sentinel in the release function */ |
---|
144 | g_object_set_data_full ( G_OBJECT( da ), "graphics-contexts", |
---|
145 | gcs, release_gobject_array ); |
---|
146 | } |
---|
147 | |
---|
148 | /** |
---|
149 | *** Get the cells' previous colors... |
---|
150 | *** (this is used for blinking when the color changes) |
---|
151 | **/ |
---|
152 | |
---|
153 | prev_color = (guint8*) g_object_get_data ( G_OBJECT( da ), "prev-color" ); |
---|
154 | if( prev_color == NULL ) |
---|
155 | { |
---|
156 | first_time = TRUE; |
---|
157 | prev_color = g_new0 ( guint8, n_cells ); |
---|
158 | g_object_set_data_full ( G_OBJECT( |
---|
159 | da ), "prev-color", prev_color, g_free ); |
---|
160 | } |
---|
161 | |
---|
162 | /** |
---|
163 | *** Get the piece data values... |
---|
164 | **/ |
---|
165 | |
---|
166 | switch( mode ) |
---|
167 | { |
---|
168 | case DRAW_AVAIL: |
---|
169 | pieces = g_new ( int8_t, n_cells ); |
---|
170 | tr_torrentAvailability ( tor, pieces, n_cells ); |
---|
171 | break; |
---|
172 | |
---|
173 | case DRAW_PROG: |
---|
174 | completeness = g_new ( float, n_cells ); |
---|
175 | tr_torrentAmountFinished ( tor, completeness, n_cells ); |
---|
176 | break; |
---|
177 | |
---|
178 | default: |
---|
179 | g_error( "no mode defined!" ); |
---|
180 | } |
---|
181 | |
---|
182 | /** |
---|
183 | *** Draw... |
---|
184 | **/ |
---|
185 | |
---|
186 | i = 0; |
---|
187 | for( y = 0; y < n_rows; ++y ) |
---|
188 | { |
---|
189 | for( x = 0; x < n_cols; ++x ) |
---|
190 | { |
---|
191 | int draw_x = grid_bounds.x + (int)( x * piece_w ); |
---|
192 | int draw_y = grid_bounds.y + (int)( y * piece_h ); |
---|
193 | int color = BLACK; |
---|
194 | int border = BLACK; |
---|
195 | |
---|
196 | if( rtl ) |
---|
197 | draw_x = grid_bounds.x + grid_bounds.width - |
---|
198 | (int)( ( x + 1 ) * piece_w ); |
---|
199 | else |
---|
200 | draw_x = grid_bounds.x + (int)( x * piece_w ); |
---|
201 | draw_y = grid_bounds.y + (int)( y * piece_h ); |
---|
202 | |
---|
203 | if( i < n_cells ) |
---|
204 | { |
---|
205 | border = GRAY; |
---|
206 | |
---|
207 | if( mode == DRAW_AVAIL ) |
---|
208 | { |
---|
209 | const int8_t val = pieces[i]; |
---|
210 | if( val < 0 ) color = ALL; |
---|
211 | else if( val == 0 ) color = NONE; |
---|
212 | else if( val <= 4 ) color = FEW; |
---|
213 | else if( val <= 8 ) color = SOME; |
---|
214 | else color = LOTS; |
---|
215 | } |
---|
216 | else /* completeness */ |
---|
217 | { |
---|
218 | const float val = completeness[i]; |
---|
219 | if( val >= 1.00 ) color = ALL; |
---|
220 | else if( val >= 0.66 ) color = LOTS; |
---|
221 | else if( val >= 0.33 ) color = SOME; |
---|
222 | else if( val >= 0.01 ) color = FEW; |
---|
223 | else color = NONE; |
---|
224 | } |
---|
225 | |
---|
226 | /* draw a "blink" for one interval when a piece changes */ |
---|
227 | if( first_time ) |
---|
228 | prev_color[i] = color; |
---|
229 | else if( color != prev_color[i] ) |
---|
230 | { |
---|
231 | prev_color[i] = color; |
---|
232 | color = border = BLINK; |
---|
233 | } |
---|
234 | } |
---|
235 | |
---|
236 | gdk_draw_rectangle ( da->window, gcs[color], TRUE, |
---|
237 | draw_x, draw_y, |
---|
238 | piece_w_int, piece_h_int ); |
---|
239 | |
---|
240 | if( i < n_cells ) |
---|
241 | gdk_draw_rectangle ( da->window, gcs[border], FALSE, |
---|
242 | draw_x, draw_y, |
---|
243 | piece_w_int, piece_h_int ); |
---|
244 | |
---|
245 | ++i; |
---|
246 | } |
---|
247 | } |
---|
248 | |
---|
249 | gdk_draw_rectangle ( da->window, gcs[GRAY], FALSE, |
---|
250 | grid_bounds.x, grid_bounds.y, |
---|
251 | grid_bounds.width - 1, grid_bounds.height - 1 ); |
---|
252 | |
---|
253 | g_free ( pieces ); |
---|
254 | g_free ( completeness ); |
---|
255 | return FALSE; |
---|
256 | } |
---|
257 | |
---|
258 | #endif |
---|
259 | |
---|
260 | /**** |
---|
261 | ***** PEERS TAB |
---|
262 | ****/ |
---|
263 | |
---|
264 | enum |
---|
265 | { |
---|
266 | WEBSEED_COL_URL, |
---|
267 | WEBSEED_COL_DOWNLOAD_RATE, |
---|
268 | N_WEBSEED_COLS |
---|
269 | }; |
---|
270 | |
---|
271 | static const char * webseed_column_names[N_WEBSEED_COLS] = |
---|
272 | { |
---|
273 | N_( "Web Seeds" ), |
---|
274 | /* 'download speed' column header. terse to keep the column narrow. */ |
---|
275 | N_( "Down" ) |
---|
276 | }; |
---|
277 | |
---|
278 | static GtkTreeModel* |
---|
279 | webseed_model_new( const tr_torrent * tor ) |
---|
280 | { |
---|
281 | int i; |
---|
282 | const tr_info * inf = tr_torrentInfo( tor ); |
---|
283 | float * speeds = tr_torrentWebSpeeds( tor ); |
---|
284 | GtkListStore * store = gtk_list_store_new( N_WEBSEED_COLS, |
---|
285 | G_TYPE_STRING, |
---|
286 | G_TYPE_FLOAT ); |
---|
287 | |
---|
288 | for( i = 0; i < inf->webseedCount; ++i ) |
---|
289 | { |
---|
290 | GtkTreeIter iter; |
---|
291 | gtk_list_store_append( store, &iter ); |
---|
292 | gtk_list_store_set( store, &iter, WEBSEED_COL_URL, inf->webseeds[i], |
---|
293 | WEBSEED_COL_DOWNLOAD_RATE, speeds[i], |
---|
294 | -1 ); |
---|
295 | } |
---|
296 | |
---|
297 | tr_free( speeds ); |
---|
298 | return GTK_TREE_MODEL( store ); |
---|
299 | } |
---|
300 | |
---|
301 | enum |
---|
302 | { |
---|
303 | PEER_COL_ADDRESS, |
---|
304 | PEER_COL_DOWNLOAD_RATE, |
---|
305 | PEER_COL_UPLOAD_RATE, |
---|
306 | PEER_COL_CLIENT, |
---|
307 | PEER_COL_PROGRESS, |
---|
308 | PEER_COL_IS_ENCRYPTED, |
---|
309 | PEER_COL_STATUS, |
---|
310 | N_PEER_COLS |
---|
311 | }; |
---|
312 | |
---|
313 | static const char* peer_column_names[N_PEER_COLS] = |
---|
314 | { |
---|
315 | N_( "Address" ), |
---|
316 | /* 'download speed' column header. terse to keep the column narrow. */ |
---|
317 | N_( "Down" ), |
---|
318 | /* 'upload speed' column header. terse to keep the column narrow. */ |
---|
319 | N_( "Up" ), |
---|
320 | N_( "Client" ), |
---|
321 | /* 'percent done' column header. terse to keep the column narrow. */ |
---|
322 | N_( "%" ), |
---|
323 | " ", |
---|
324 | N_( "Status" ) |
---|
325 | }; |
---|
326 | |
---|
327 | static int |
---|
328 | compare_peers( const void * a, |
---|
329 | const void * b ) |
---|
330 | { |
---|
331 | const tr_peer_stat * pa = a; |
---|
332 | const tr_peer_stat * pb = b; |
---|
333 | |
---|
334 | return strcmp ( pa->addr, pb->addr ); |
---|
335 | } |
---|
336 | |
---|
337 | static int |
---|
338 | compare_addr_to_peer( const void * a, |
---|
339 | const void * b ) |
---|
340 | { |
---|
341 | const char * addr = (const char *) a; |
---|
342 | const tr_peer_stat * peer = b; |
---|
343 | |
---|
344 | return strcmp ( addr, peer->addr ); |
---|
345 | } |
---|
346 | |
---|
347 | static void |
---|
348 | peer_row_set( GtkListStore * store, |
---|
349 | GtkTreeIter * iter, |
---|
350 | const tr_peer_stat * peer ) |
---|
351 | { |
---|
352 | const char * client = peer->client; |
---|
353 | |
---|
354 | if( !client || !strcmp( client, "Unknown Client" ) ) |
---|
355 | client = " "; |
---|
356 | |
---|
357 | gtk_list_store_set( store, iter, |
---|
358 | PEER_COL_ADDRESS, peer->addr, |
---|
359 | PEER_COL_CLIENT, client, |
---|
360 | PEER_COL_IS_ENCRYPTED, peer->isEncrypted, |
---|
361 | PEER_COL_PROGRESS, (int)( 100.0 * peer->progress ), |
---|
362 | PEER_COL_DOWNLOAD_RATE, peer->rateToClient, |
---|
363 | PEER_COL_UPLOAD_RATE, peer->rateToPeer, |
---|
364 | PEER_COL_STATUS, peer->flagStr, |
---|
365 | -1 ); |
---|
366 | } |
---|
367 | |
---|
368 | static void |
---|
369 | append_peers_to_model( GtkListStore * store, |
---|
370 | const tr_peer_stat * peers, |
---|
371 | int n_peers ) |
---|
372 | { |
---|
373 | int i; |
---|
374 | |
---|
375 | for( i = 0; i < n_peers; ++i ) |
---|
376 | { |
---|
377 | GtkTreeIter iter; |
---|
378 | gtk_list_store_append( store, &iter ); |
---|
379 | peer_row_set ( store, &iter, &peers[i] ); |
---|
380 | } |
---|
381 | } |
---|
382 | |
---|
383 | static GtkTreeModel* |
---|
384 | peer_model_new( tr_torrent * tor ) |
---|
385 | { |
---|
386 | GtkListStore * m = gtk_list_store_new ( N_PEER_COLS, |
---|
387 | G_TYPE_STRING, /* addr */ |
---|
388 | G_TYPE_FLOAT, /* downloadFromRate */ |
---|
389 | G_TYPE_FLOAT, /* uploadToRate */ |
---|
390 | G_TYPE_STRING, /* client */ |
---|
391 | G_TYPE_INT, /* progress [0..100] |
---|
392 | */ |
---|
393 | G_TYPE_BOOLEAN, /* isEncrypted */ |
---|
394 | G_TYPE_STRING ); /* flagString */ |
---|
395 | |
---|
396 | int n_peers = 0; |
---|
397 | tr_peer_stat * peers = tr_torrentPeers ( tor, &n_peers ); |
---|
398 | |
---|
399 | qsort ( peers, n_peers, sizeof( tr_peer_stat ), compare_peers ); |
---|
400 | append_peers_to_model ( m, peers, n_peers ); |
---|
401 | tr_torrentPeersFree( peers, 0 ); |
---|
402 | return GTK_TREE_MODEL ( m ); |
---|
403 | } |
---|
404 | |
---|
405 | static void |
---|
406 | render_encrypted( GtkTreeViewColumn * column UNUSED, |
---|
407 | GtkCellRenderer * renderer, |
---|
408 | GtkTreeModel * tree_model, |
---|
409 | GtkTreeIter * iter, |
---|
410 | gpointer data UNUSED ) |
---|
411 | { |
---|
412 | gboolean is_encrypted = FALSE; |
---|
413 | |
---|
414 | gtk_tree_model_get ( tree_model, iter, PEER_COL_IS_ENCRYPTED, |
---|
415 | &is_encrypted, |
---|
416 | -1 ); |
---|
417 | g_object_set ( renderer, "xalign", (gfloat)0.0, |
---|
418 | "yalign", (gfloat)0.5, |
---|
419 | "stock-id", ( is_encrypted ? "transmission-lock" : NULL ), |
---|
420 | NULL ); |
---|
421 | } |
---|
422 | |
---|
423 | static void |
---|
424 | render_ul_rate( GtkTreeViewColumn * column UNUSED, |
---|
425 | GtkCellRenderer * renderer, |
---|
426 | GtkTreeModel * tree_model, |
---|
427 | GtkTreeIter * iter, |
---|
428 | gpointer data UNUSED ) |
---|
429 | { |
---|
430 | float rate = 0.0; |
---|
431 | |
---|
432 | gtk_tree_model_get ( tree_model, iter, PEER_COL_UPLOAD_RATE, &rate, -1 ); |
---|
433 | if( rate < 0.01 ) |
---|
434 | g_object_set ( renderer, "text", "", NULL ); |
---|
435 | else |
---|
436 | { |
---|
437 | char speedStr[64]; |
---|
438 | tr_strlspeed( speedStr, rate, sizeof( speedStr ) ); |
---|
439 | g_object_set( renderer, "text", speedStr, NULL ); |
---|
440 | } |
---|
441 | } |
---|
442 | |
---|
443 | static void |
---|
444 | render_dl_rate( GtkTreeViewColumn * column UNUSED, |
---|
445 | GtkCellRenderer * renderer, |
---|
446 | GtkTreeModel * tree_model, |
---|
447 | GtkTreeIter * iter, |
---|
448 | gpointer data UNUSED ) |
---|
449 | { |
---|
450 | float rate = 0.0; |
---|
451 | |
---|
452 | gtk_tree_model_get ( tree_model, iter, PEER_COL_DOWNLOAD_RATE, &rate, |
---|
453 | -1 ); |
---|
454 | if( rate < 0.01 ) |
---|
455 | g_object_set ( renderer, "text", "", NULL ); |
---|
456 | else |
---|
457 | { |
---|
458 | char speedStr[64]; |
---|
459 | tr_strlspeed( speedStr, rate, sizeof( speedStr ) ); |
---|
460 | g_object_set( renderer, "text", speedStr, NULL ); |
---|
461 | } |
---|
462 | } |
---|
463 | |
---|
464 | static void |
---|
465 | render_client( GtkTreeViewColumn * column UNUSED, |
---|
466 | GtkCellRenderer * renderer, |
---|
467 | GtkTreeModel * tree_model, |
---|
468 | GtkTreeIter * iter, |
---|
469 | gpointer data UNUSED ) |
---|
470 | { |
---|
471 | char * client = NULL; |
---|
472 | |
---|
473 | gtk_tree_model_get ( tree_model, iter, PEER_COL_CLIENT, &client, |
---|
474 | -1 ); |
---|
475 | g_object_set ( renderer, "text", ( client ? client : "" ), NULL ); |
---|
476 | g_free ( client ); |
---|
477 | } |
---|
478 | |
---|
479 | typedef struct |
---|
480 | { |
---|
481 | TrTorrent * gtor; |
---|
482 | GtkTreeModel * model; /* same object as store, but recast */ |
---|
483 | GtkListStore * store; /* same object as model, but recast */ |
---|
484 | GtkListStore * webseeds; |
---|
485 | GtkWidget * completeness; |
---|
486 | GtkWidget * seeders_lb; |
---|
487 | GtkWidget * leechers_lb; |
---|
488 | GtkWidget * completed_lb; |
---|
489 | } |
---|
490 | PeerData; |
---|
491 | |
---|
492 | static void |
---|
493 | fmtpeercount( GtkWidget * l, |
---|
494 | int count ) |
---|
495 | { |
---|
496 | if( 0 > count ) |
---|
497 | { |
---|
498 | gtk_label_set_text( GTK_LABEL( l ), "?" ); |
---|
499 | } |
---|
500 | else |
---|
501 | { |
---|
502 | char str[16]; |
---|
503 | g_snprintf( str, sizeof str, "%'d", count ); |
---|
504 | gtk_label_set_text( GTK_LABEL( l ), str ); |
---|
505 | } |
---|
506 | } |
---|
507 | |
---|
508 | static void |
---|
509 | refresh_peers( GtkWidget * top ) |
---|
510 | { |
---|
511 | int i; |
---|
512 | int n_peers; |
---|
513 | GtkTreeIter iter; |
---|
514 | PeerData * p = (PeerData*) g_object_get_data ( G_OBJECT( |
---|
515 | top ), |
---|
516 | "peer-data" ); |
---|
517 | tr_torrent * tor = tr_torrent_handle ( p->gtor ); |
---|
518 | GtkTreeModel * model = p->model; |
---|
519 | GtkListStore * store = p->store; |
---|
520 | tr_peer_stat * peers; |
---|
521 | const tr_stat * stat = tr_torrent_stat( p->gtor ); |
---|
522 | const tr_info * inf = tr_torrent_info( p->gtor ); |
---|
523 | |
---|
524 | if( inf->webseedCount ) |
---|
525 | { |
---|
526 | float * speeds = tr_torrentWebSpeeds( tor ); |
---|
527 | for( i = 0; i < inf->webseedCount; ++i ) |
---|
528 | { |
---|
529 | GtkTreeIter iter; |
---|
530 | gtk_tree_model_iter_nth_child( GTK_TREE_MODEL( |
---|
531 | p->webseeds ), &iter, NULL, |
---|
532 | i ); |
---|
533 | gtk_list_store_set( p->webseeds, &iter, |
---|
534 | WEBSEED_COL_DOWNLOAD_RATE, speeds[i], |
---|
535 | -1 ); |
---|
536 | } |
---|
537 | tr_free( speeds ); |
---|
538 | } |
---|
539 | |
---|
540 | /** |
---|
541 | *** merge the peer diffs into the tree model. |
---|
542 | *** |
---|
543 | *** this is more complicated than creating a new model, |
---|
544 | *** but is also (a) more efficient and (b) doesn't undo |
---|
545 | *** the view's visible area and sorting on every refresh. |
---|
546 | **/ |
---|
547 | |
---|
548 | n_peers = 0; |
---|
549 | peers = tr_torrentPeers ( tor, &n_peers ); |
---|
550 | qsort ( peers, n_peers, sizeof( tr_peer_stat ), compare_peers ); |
---|
551 | |
---|
552 | if( gtk_tree_model_get_iter_first ( model, &iter ) ) do |
---|
553 | { |
---|
554 | char * addr = NULL; |
---|
555 | tr_peer_stat * peer = NULL; |
---|
556 | gtk_tree_model_get ( model, &iter, PEER_COL_ADDRESS, &addr, -1 ); |
---|
557 | peer = bsearch ( addr, peers, n_peers, sizeof( tr_peer_stat ), |
---|
558 | compare_addr_to_peer ); |
---|
559 | g_free ( addr ); |
---|
560 | |
---|
561 | if( peer ) /* update a pre-existing row */ |
---|
562 | { |
---|
563 | const int pos = peer - peers; |
---|
564 | const int n_rhs = n_peers - ( pos + 1 ); |
---|
565 | g_assert ( n_rhs >= 0 ); |
---|
566 | |
---|
567 | peer_row_set ( store, &iter, peer ); |
---|
568 | |
---|
569 | /* remove it from the tr_peer_stat list */ |
---|
570 | g_memmove ( peer, peer + 1, sizeof( tr_peer_stat ) * n_rhs ); |
---|
571 | --n_peers; |
---|
572 | } |
---|
573 | else if( !gtk_list_store_remove ( store, &iter ) ) |
---|
574 | break; /* we removed the model's last item */ |
---|
575 | } |
---|
576 | while( gtk_tree_model_iter_next ( model, &iter ) ); |
---|
577 | |
---|
578 | append_peers_to_model ( store, peers, n_peers ); /* all these are new */ |
---|
579 | |
---|
580 | #ifdef SHOW_PIECES |
---|
581 | if( GDK_IS_DRAWABLE ( p->completeness->window ) ) |
---|
582 | refresh_pieces ( p->completeness, NULL, p->gtor ); |
---|
583 | #endif |
---|
584 | |
---|
585 | fmtpeercount ( p->seeders_lb, stat->seeders ); |
---|
586 | fmtpeercount ( p->leechers_lb, stat->leechers ); |
---|
587 | fmtpeercount ( p->completed_lb, stat->timesCompleted ); |
---|
588 | |
---|
589 | free( peers ); |
---|
590 | } |
---|
591 | |
---|
592 | #if GTK_CHECK_VERSION( 2, 12, 0 ) |
---|
593 | static gboolean |
---|
594 | onPeerViewQueryTooltip( GtkWidget * widget, |
---|
595 | gint x, |
---|
596 | gint y, |
---|
597 | gboolean keyboard_tip, |
---|
598 | GtkTooltip * tooltip, |
---|
599 | gpointer user_data UNUSED ) |
---|
600 | { |
---|
601 | gboolean show_tip = FALSE; |
---|
602 | GtkTreeModel * model; |
---|
603 | GtkTreeIter iter; |
---|
604 | |
---|
605 | if( gtk_tree_view_get_tooltip_context( GTK_TREE_VIEW( widget ), |
---|
606 | &x, &y, keyboard_tip, |
---|
607 | &model, NULL, &iter ) ) |
---|
608 | { |
---|
609 | const char * pch; |
---|
610 | char * str = NULL; |
---|
611 | GString * gstr = g_string_new( NULL ); |
---|
612 | gtk_tree_model_get( model, &iter, PEER_COL_STATUS, &str, -1 ); |
---|
613 | for( pch = str; pch && *pch; ++pch ) |
---|
614 | { |
---|
615 | const char * txt = NULL; |
---|
616 | switch( *pch ) |
---|
617 | { |
---|
618 | case 'O': |
---|
619 | txt = _( "Optimistic unchoke" ); break; |
---|
620 | |
---|
621 | case 'D': |
---|
622 | txt = _( "Downloading from this peer" ); break; |
---|
623 | |
---|
624 | case 'd': |
---|
625 | txt = _( |
---|
626 | "We would download from this peer if they would let us" ); |
---|
627 | break; |
---|
628 | |
---|
629 | case 'U': |
---|
630 | txt = _( "Uploading to peer" ); break; |
---|
631 | |
---|
632 | case 'u': |
---|
633 | txt = _( "We would upload to this peer if they asked" ); |
---|
634 | break; |
---|
635 | |
---|
636 | case 'K': |
---|
637 | txt = _( |
---|
638 | "Peer has unchoked us, but we're not interested" ); |
---|
639 | break; |
---|
640 | |
---|
641 | case '?': |
---|
642 | txt = _( |
---|
643 | "We unchoked this peer, but they're not interested" ); |
---|
644 | break; |
---|
645 | |
---|
646 | case 'E': |
---|
647 | txt = _( "Encrypted connection" ); break; |
---|
648 | |
---|
649 | case 'X': |
---|
650 | txt = _( |
---|
651 | "Peer was discovered through Peer Exchange (PEX)" ); |
---|
652 | break; |
---|
653 | |
---|
654 | case 'I': |
---|
655 | txt = _( "Peer is an incoming connection" ); break; |
---|
656 | } |
---|
657 | if( txt ) |
---|
658 | g_string_append_printf( gstr, "%c: %s\n", *pch, txt ); |
---|
659 | } |
---|
660 | if( gstr->len ) /* remove the last linefeed */ |
---|
661 | g_string_set_size( gstr, gstr->len - 1 ); |
---|
662 | gtk_tooltip_set_text( tooltip, gstr->str ); |
---|
663 | g_string_free( gstr, TRUE ); |
---|
664 | g_free( str ); |
---|
665 | show_tip = TRUE; |
---|
666 | } |
---|
667 | |
---|
668 | return show_tip; |
---|
669 | } |
---|
670 | |
---|
671 | #endif |
---|
672 | |
---|
673 | static GtkWidget* |
---|
674 | peer_page_new( TrTorrent * gtor ) |
---|
675 | { |
---|
676 | guint i; |
---|
677 | GtkTreeModel * m; |
---|
678 | GtkWidget * v, *w, *ret, *sw, *l, *vbox, *hbox; |
---|
679 | GtkWidget * webtree = NULL; |
---|
680 | tr_torrent * tor = tr_torrent_handle ( gtor ); |
---|
681 | PeerData * p = g_new ( PeerData, 1 ); |
---|
682 | const tr_info * inf = tr_torrent_info( gtor ); |
---|
683 | |
---|
684 | /* TODO: make this configurable? */ |
---|
685 | int view_columns[] = { PEER_COL_IS_ENCRYPTED, |
---|
686 | PEER_COL_UPLOAD_RATE, |
---|
687 | PEER_COL_DOWNLOAD_RATE, |
---|
688 | PEER_COL_PROGRESS, |
---|
689 | PEER_COL_STATUS, |
---|
690 | PEER_COL_ADDRESS, |
---|
691 | PEER_COL_CLIENT }; |
---|
692 | |
---|
693 | |
---|
694 | if( inf->webseedCount ) |
---|
695 | { |
---|
696 | GtkTreeViewColumn * c; |
---|
697 | GtkCellRenderer * r; |
---|
698 | const char * t; |
---|
699 | GtkWidget * fr; |
---|
700 | |
---|
701 | m = webseed_model_new( tr_torrent_handle( gtor ) ); |
---|
702 | webtree = gtk_tree_view_new_with_model( m ); |
---|
703 | g_signal_connect( webtree, "button-release-event", |
---|
704 | G_CALLBACK( on_tree_view_button_released ), NULL ); |
---|
705 | gtk_tree_view_set_rules_hint( GTK_TREE_VIEW( webtree ), TRUE ); |
---|
706 | p->webseeds = GTK_LIST_STORE( m ); |
---|
707 | g_object_unref( G_OBJECT( m ) ); |
---|
708 | |
---|
709 | t = _( webseed_column_names[WEBSEED_COL_URL] ); |
---|
710 | r = gtk_cell_renderer_text_new ( ); |
---|
711 | g_object_set( G_OBJECT( r ), "ellipsize", PANGO_ELLIPSIZE_END, NULL ); |
---|
712 | c = |
---|
713 | gtk_tree_view_column_new_with_attributes( t, r, "text", |
---|
714 | WEBSEED_COL_URL, |
---|
715 | NULL ); |
---|
716 | g_object_set( G_OBJECT( c ), "expand", TRUE, NULL ); |
---|
717 | gtk_tree_view_column_set_sort_column_id( c, WEBSEED_COL_URL ); |
---|
718 | gtk_tree_view_append_column( GTK_TREE_VIEW( webtree ), c ); |
---|
719 | |
---|
720 | t = _( webseed_column_names[WEBSEED_COL_DOWNLOAD_RATE] ); |
---|
721 | r = gtk_cell_renderer_text_new ( ); |
---|
722 | c = gtk_tree_view_column_new_with_attributes ( |
---|
723 | t, r, "text", WEBSEED_COL_DOWNLOAD_RATE, NULL ); |
---|
724 | gtk_tree_view_column_set_cell_data_func ( c, r, render_dl_rate, |
---|
725 | NULL, NULL ); |
---|
726 | gtk_tree_view_column_set_sort_column_id( c, |
---|
727 | WEBSEED_COL_DOWNLOAD_RATE ); |
---|
728 | gtk_tree_view_append_column( GTK_TREE_VIEW( webtree ), c ); |
---|
729 | |
---|
730 | fr = gtk_frame_new( NULL ); |
---|
731 | gtk_frame_set_shadow_type( GTK_FRAME( fr ), GTK_SHADOW_IN ); |
---|
732 | gtk_container_add( GTK_CONTAINER( fr ), webtree ); |
---|
733 | webtree = fr; |
---|
734 | } |
---|
735 | |
---|
736 | m = peer_model_new ( tor ); |
---|
737 | v = GTK_WIDGET( g_object_new( GTK_TYPE_TREE_VIEW, |
---|
738 | "model", m, |
---|
739 | "rules-hint", TRUE, |
---|
740 | #if GTK_CHECK_VERSION( 2, 12, 0 ) |
---|
741 | "has-tooltip", TRUE, |
---|
742 | #endif |
---|
743 | NULL ) ); |
---|
744 | #if GTK_CHECK_VERSION( 2, 12, 0 ) |
---|
745 | g_signal_connect( v, "query-tooltip", |
---|
746 | G_CALLBACK( onPeerViewQueryTooltip ), NULL ); |
---|
747 | #endif |
---|
748 | gtk_widget_set_size_request( v, 550, 0 ); |
---|
749 | g_object_unref ( G_OBJECT( m ) ); |
---|
750 | g_signal_connect( v, "button-release-event", |
---|
751 | G_CALLBACK( on_tree_view_button_released ), NULL ); |
---|
752 | |
---|
753 | for( i = 0; i < G_N_ELEMENTS( view_columns ); ++i ) |
---|
754 | { |
---|
755 | const int col = view_columns[i]; |
---|
756 | const char * t = _( peer_column_names[col] ); |
---|
757 | gboolean resizable = TRUE; |
---|
758 | GtkTreeViewColumn * c; |
---|
759 | GtkCellRenderer * r; |
---|
760 | |
---|
761 | switch( col ) |
---|
762 | { |
---|
763 | case PEER_COL_ADDRESS: |
---|
764 | r = gtk_cell_renderer_text_new ( ); |
---|
765 | c = |
---|
766 | gtk_tree_view_column_new_with_attributes ( t, r, "text", |
---|
767 | col, |
---|
768 | NULL ); |
---|
769 | break; |
---|
770 | |
---|
771 | case PEER_COL_CLIENT: |
---|
772 | r = gtk_cell_renderer_text_new ( ); |
---|
773 | c = |
---|
774 | gtk_tree_view_column_new_with_attributes ( t, r, "text", |
---|
775 | col, |
---|
776 | NULL ); |
---|
777 | gtk_tree_view_column_set_cell_data_func ( c, r, |
---|
778 | render_client, |
---|
779 | NULL, |
---|
780 | NULL ); |
---|
781 | break; |
---|
782 | |
---|
783 | case PEER_COL_PROGRESS: |
---|
784 | r = gtk_cell_renderer_progress_new ( ); |
---|
785 | c = gtk_tree_view_column_new_with_attributes ( |
---|
786 | t, r, "value", PEER_COL_PROGRESS, NULL ); |
---|
787 | break; |
---|
788 | |
---|
789 | case PEER_COL_IS_ENCRYPTED: |
---|
790 | resizable = FALSE; |
---|
791 | r = gtk_cell_renderer_pixbuf_new ( ); |
---|
792 | c = gtk_tree_view_column_new_with_attributes ( t, r, NULL ); |
---|
793 | gtk_tree_view_column_set_sizing ( |
---|
794 | c, GTK_TREE_VIEW_COLUMN_FIXED ); |
---|
795 | gtk_tree_view_column_set_fixed_width ( c, 20 ); |
---|
796 | gtk_tree_view_column_set_cell_data_func ( c, r, |
---|
797 | render_encrypted, |
---|
798 | NULL, |
---|
799 | NULL ); |
---|
800 | break; |
---|
801 | |
---|
802 | case PEER_COL_DOWNLOAD_RATE: |
---|
803 | r = gtk_cell_renderer_text_new ( ); |
---|
804 | c = |
---|
805 | gtk_tree_view_column_new_with_attributes ( t, r, "text", |
---|
806 | col, |
---|
807 | NULL ); |
---|
808 | gtk_tree_view_column_set_cell_data_func ( c, r, |
---|
809 | render_dl_rate, |
---|
810 | NULL, |
---|
811 | NULL ); |
---|
812 | break; |
---|
813 | |
---|
814 | case PEER_COL_UPLOAD_RATE: |
---|
815 | r = gtk_cell_renderer_text_new ( ); |
---|
816 | c = |
---|
817 | gtk_tree_view_column_new_with_attributes ( t, r, "text", |
---|
818 | col, |
---|
819 | NULL ); |
---|
820 | gtk_tree_view_column_set_cell_data_func ( c, r, |
---|
821 | render_ul_rate, |
---|
822 | NULL, |
---|
823 | NULL ); |
---|
824 | break; |
---|
825 | |
---|
826 | case PEER_COL_STATUS: |
---|
827 | r = gtk_cell_renderer_text_new( ); |
---|
828 | c = |
---|
829 | gtk_tree_view_column_new_with_attributes ( t, r, "text", |
---|
830 | col, |
---|
831 | NULL ); |
---|
832 | break; |
---|
833 | |
---|
834 | default: |
---|
835 | abort ( ); |
---|
836 | } |
---|
837 | |
---|
838 | gtk_tree_view_column_set_resizable ( c, resizable ); |
---|
839 | gtk_tree_view_column_set_sort_column_id ( c, col ); |
---|
840 | gtk_tree_view_append_column ( GTK_TREE_VIEW( v ), c ); |
---|
841 | } |
---|
842 | |
---|
843 | /* the 'expander' column has a 10-pixel margin on the left |
---|
844 | that doesn't look quite correct in any of these columns... |
---|
845 | so create a non-visible column and assign it as the |
---|
846 | 'expander column. */ |
---|
847 | { |
---|
848 | GtkTreeViewColumn *c = gtk_tree_view_column_new ( ); |
---|
849 | gtk_tree_view_column_set_visible ( c, FALSE ); |
---|
850 | gtk_tree_view_append_column ( GTK_TREE_VIEW( v ), c ); |
---|
851 | gtk_tree_view_set_expander_column ( GTK_TREE_VIEW( v ), c ); |
---|
852 | } |
---|
853 | |
---|
854 | w = sw = gtk_scrolled_window_new ( NULL, NULL ); |
---|
855 | gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW( w ), |
---|
856 | GTK_POLICY_NEVER, |
---|
857 | GTK_POLICY_AUTOMATIC ); |
---|
858 | gtk_scrolled_window_set_shadow_type ( GTK_SCROLLED_WINDOW( w ), |
---|
859 | GTK_SHADOW_IN ); |
---|
860 | gtk_container_add ( GTK_CONTAINER( w ), v ); |
---|
861 | |
---|
862 | |
---|
863 | vbox = gtk_vbox_new ( FALSE, GUI_PAD ); |
---|
864 | gtk_container_set_border_width ( GTK_CONTAINER( vbox ), GUI_PAD_BIG ); |
---|
865 | |
---|
866 | if( webtree ) |
---|
867 | gtk_box_pack_start( GTK_BOX( vbox ), webtree, FALSE, FALSE, 0 ); |
---|
868 | |
---|
869 | /* h = gtk_hbox_new (FALSE, GUI_PAD); */ |
---|
870 | /* gtk_box_pack_start_defaults (GTK_BOX(h), sw); */ |
---|
871 | gtk_box_pack_start( GTK_BOX( vbox ), sw, TRUE, TRUE, 0 ); |
---|
872 | |
---|
873 | hbox = gtk_hbox_new ( FALSE, GUI_PAD ); |
---|
874 | l = gtk_label_new ( NULL ); |
---|
875 | gtk_label_set_markup ( GTK_LABEL( l ), _( "<b>Seeders:</b>" ) ); |
---|
876 | gtk_box_pack_start ( GTK_BOX( hbox ), l, FALSE, FALSE, 0 ); |
---|
877 | l = p->seeders_lb = gtk_label_new ( NULL ); |
---|
878 | gtk_box_pack_start ( GTK_BOX( hbox ), l, FALSE, FALSE, 0 ); |
---|
879 | gtk_box_pack_start( GTK_BOX( hbox ), |
---|
880 | gtk_alignment_new ( 0.0f, 0.0f, 0.0f, 0.0f ), |
---|
881 | TRUE, TRUE, 0 ); |
---|
882 | l = gtk_label_new ( NULL ); |
---|
883 | gtk_label_set_markup ( GTK_LABEL( l ), _( "<b>Leechers:</b>" ) ); |
---|
884 | gtk_box_pack_start ( GTK_BOX( hbox ), l, FALSE, FALSE, 0 ); |
---|
885 | l = p->leechers_lb = gtk_label_new ( NULL ); |
---|
886 | gtk_box_pack_start ( GTK_BOX( hbox ), l, FALSE, FALSE, 0 ); |
---|
887 | gtk_box_pack_start( GTK_BOX( hbox ), |
---|
888 | gtk_alignment_new ( 0.0f, 0.0f, 0.0f, 0.0f ), |
---|
889 | TRUE, TRUE, 0 ); |
---|
890 | l = gtk_label_new ( NULL ); |
---|
891 | gtk_label_set_markup ( GTK_LABEL( l ), _( "<b>Times Completed:</b>" ) ); |
---|
892 | gtk_box_pack_start ( GTK_BOX( hbox ), l, FALSE, FALSE, 0 ); |
---|
893 | l = p->completed_lb = gtk_label_new ( NULL ); |
---|
894 | gtk_box_pack_start ( GTK_BOX( hbox ), l, FALSE, FALSE, 0 ); |
---|
895 | gtk_box_pack_start ( GTK_BOX( vbox ), hbox, FALSE, FALSE, 0 ); |
---|
896 | |
---|
897 | ret = vbox; |
---|
898 | p->gtor = gtor; |
---|
899 | p->model = m; |
---|
900 | p->store = GTK_LIST_STORE( m ); |
---|
901 | g_object_set_data_full ( G_OBJECT( ret ), "peer-data", p, g_free ); |
---|
902 | return ret; |
---|
903 | } |
---|
904 | |
---|
905 | /**** |
---|
906 | ***** INFO TAB |
---|
907 | ****/ |
---|
908 | |
---|
909 | static void |
---|
910 | refresh_time_lb( GtkWidget * l, |
---|
911 | time_t t ) |
---|
912 | { |
---|
913 | const char * never = _( "Never" ); |
---|
914 | |
---|
915 | if( !t ) |
---|
916 | gtk_label_set_text( GTK_LABEL( l ), never ); |
---|
917 | else |
---|
918 | { |
---|
919 | char * str = gtr_localtime( t ); |
---|
920 | gtk_label_set_text( GTK_LABEL( l ), str ); |
---|
921 | g_free( str ); |
---|
922 | } |
---|
923 | } |
---|
924 | |
---|
925 | static GtkWidget* |
---|
926 | info_page_new( tr_torrent * tor ) |
---|
927 | { |
---|
928 | int row = 0; |
---|
929 | GtkWidget * t = hig_workarea_create ( ); |
---|
930 | GtkWidget * l, *w, *fr; |
---|
931 | char * pch; |
---|
932 | char sizeStr[128]; |
---|
933 | char countStr[128]; |
---|
934 | char buf[256]; |
---|
935 | GtkTextBuffer * b; |
---|
936 | const tr_info * info = tr_torrentInfo( tor ); |
---|
937 | |
---|
938 | hig_workarea_add_section_title ( t, &row, _( "Details" ) ); |
---|
939 | |
---|
940 | g_snprintf( countStr, sizeof( countStr ), |
---|
941 | ngettext( "%'d Piece", "%'d Pieces", info->pieceCount ), |
---|
942 | info->pieceCount ); |
---|
943 | tr_strlsize( sizeStr, info->pieceSize, sizeof( sizeStr ) ); |
---|
944 | g_snprintf( buf, sizeof( buf ), |
---|
945 | /* %1$s is number of pieces; |
---|
946 | %2$s is how big each piece is */ |
---|
947 | _( "%1$s @ %2$s" ), |
---|
948 | countStr, sizeStr ); |
---|
949 | |
---|
950 | l = gtk_label_new ( buf ); |
---|
951 | hig_workarea_add_row ( t, &row, _( "Pieces:" ), l, NULL ); |
---|
952 | |
---|
953 | l = |
---|
954 | g_object_new( GTK_TYPE_LABEL, "label", info->hashString, |
---|
955 | "selectable", |
---|
956 | TRUE, |
---|
957 | "ellipsize", PANGO_ELLIPSIZE_END, |
---|
958 | NULL ); |
---|
959 | hig_workarea_add_row ( t, &row, _( "Hash:" ), l, NULL ); |
---|
960 | |
---|
961 | pch = ( info->isPrivate ) |
---|
962 | ? _( "Private to this tracker -- PEX disabled" ) |
---|
963 | : _( "Public torrent" ); |
---|
964 | l = gtk_label_new ( pch ); |
---|
965 | hig_workarea_add_row ( t, &row, _( "Privacy:" ), l, NULL ); |
---|
966 | |
---|
967 | b = gtk_text_buffer_new ( NULL ); |
---|
968 | if( info->comment ) |
---|
969 | gtk_text_buffer_set_text ( b, info->comment, -1 ); |
---|
970 | w = gtk_text_view_new_with_buffer ( b ); |
---|
971 | gtk_widget_set_size_request ( w, 0u, 100u ); |
---|
972 | gtk_text_view_set_wrap_mode ( GTK_TEXT_VIEW( w ), GTK_WRAP_WORD ); |
---|
973 | gtk_text_view_set_editable ( GTK_TEXT_VIEW( w ), FALSE ); |
---|
974 | fr = gtk_frame_new ( NULL ); |
---|
975 | gtk_frame_set_shadow_type ( GTK_FRAME( fr ), GTK_SHADOW_IN ); |
---|
976 | gtk_container_add ( GTK_CONTAINER( fr ), w ); |
---|
977 | w = hig_workarea_add_row ( t, &row, _( "Comment:" ), fr, NULL ); |
---|
978 | gtk_misc_set_alignment ( GTK_MISC( w ), 0.0f, 0.0f ); |
---|
979 | |
---|
980 | hig_workarea_add_section_divider ( t, &row ); |
---|
981 | hig_workarea_add_section_title ( t, &row, _( "Origins" ) ); |
---|
982 | |
---|
983 | l = gtk_label_new ( *info->creator ? info->creator : _( "Unknown" ) ); |
---|
984 | gtk_label_set_ellipsize( GTK_LABEL( l ), PANGO_ELLIPSIZE_END ); |
---|
985 | hig_workarea_add_row ( t, &row, _( "Creator:" ), l, NULL ); |
---|
986 | |
---|
987 | l = gtk_label_new( NULL ); |
---|
988 | refresh_time_lb( l, info->dateCreated ); |
---|
989 | hig_workarea_add_row ( t, &row, _( "Date:" ), l, NULL ); |
---|
990 | |
---|
991 | hig_workarea_add_section_divider ( t, &row ); |
---|
992 | hig_workarea_add_section_title ( t, &row, _( "Location" ) ); |
---|
993 | |
---|
994 | l = |
---|
995 | g_object_new( GTK_TYPE_LABEL, "label", tr_torrentGetDownloadDir( |
---|
996 | tor ), "selectable", TRUE, |
---|
997 | "ellipsize", PANGO_ELLIPSIZE_END, NULL ); |
---|
998 | hig_workarea_add_row ( t, &row, _( "Destination folder:" ), l, NULL ); |
---|
999 | |
---|
1000 | l = |
---|
1001 | g_object_new( GTK_TYPE_LABEL, "label", info->torrent, "selectable", |
---|
1002 | TRUE, |
---|
1003 | "ellipsize", PANGO_ELLIPSIZE_END, |
---|
1004 | NULL ); |
---|
1005 | hig_workarea_add_row ( t, &row, _( "Torrent file:" ), l, NULL ); |
---|
1006 | |
---|
1007 | hig_workarea_finish ( t, &row ); |
---|
1008 | return t; |
---|
1009 | } |
---|
1010 | |
---|
1011 | /**** |
---|
1012 | ***** ACTIVITY TAB |
---|
1013 | ****/ |
---|
1014 | |
---|
1015 | typedef struct |
---|
1016 | { |
---|
1017 | GtkWidget * state_lb; |
---|
1018 | GtkWidget * progress_lb; |
---|
1019 | GtkWidget * have_lb; |
---|
1020 | GtkWidget * dl_lb; |
---|
1021 | GtkWidget * ul_lb; |
---|
1022 | GtkWidget * failed_lb; |
---|
1023 | GtkWidget * ratio_lb; |
---|
1024 | GtkWidget * err_lb; |
---|
1025 | GtkWidget * swarm_lb; |
---|
1026 | GtkWidget * date_added_lb; |
---|
1027 | GtkWidget * last_activity_lb; |
---|
1028 | GtkWidget * availability_da; |
---|
1029 | TrTorrent * gtor; |
---|
1030 | } |
---|
1031 | Activity; |
---|
1032 | |
---|
1033 | static void |
---|
1034 | refresh_activity( GtkWidget * top ) |
---|
1035 | { |
---|
1036 | Activity * a = g_object_get_data ( G_OBJECT( |
---|
1037 | top ), "activity-data" ); |
---|
1038 | char * pch; |
---|
1039 | char sizeStr[64]; |
---|
1040 | char sizeStr2[64]; |
---|
1041 | char buf[128]; |
---|
1042 | const tr_stat * stat = tr_torrent_stat( a->gtor ); |
---|
1043 | const double complete = stat->percentComplete * 100.0; |
---|
1044 | const double done = stat->percentDone * 100.0; |
---|
1045 | |
---|
1046 | pch = tr_torrent_status_str( a->gtor ); |
---|
1047 | gtk_label_set_text ( GTK_LABEL( a->state_lb ), pch ); |
---|
1048 | g_free ( pch ); |
---|
1049 | |
---|
1050 | if( (int)complete == (int)done ) |
---|
1051 | pch = g_strdup_printf( _( "%.1f%%" ), complete ); |
---|
1052 | else |
---|
1053 | /* %1$.1f is percent of how much of what we want's been downloaded, |
---|
1054 | %2$.1f is percent of how much of the whole torrent we've downloaded |
---|
1055 | */ |
---|
1056 | pch = g_strdup_printf( _( |
---|
1057 | "%1$.1f%% (%2$.1f%% selected)" ), |
---|
1058 | complete, done ); |
---|
1059 | gtk_label_set_text ( GTK_LABEL( a->progress_lb ), pch ); |
---|
1060 | g_free ( pch ); |
---|
1061 | |
---|
1062 | tr_strlsize( sizeStr, stat->haveValid + stat->haveUnchecked, |
---|
1063 | sizeof( sizeStr ) ); |
---|
1064 | tr_strlsize( sizeStr2, stat->haveValid, |
---|
1065 | sizeof( sizeStr2 ) ); |
---|
1066 | /* %1$s is total size of what we've saved to disk |
---|
1067 | %2$s is how much of it's passed the checksum test */ |
---|
1068 | g_snprintf( buf, sizeof( buf ), _( |
---|
1069 | "%1$s (%2$s verified)" ), sizeStr, sizeStr2 ); |
---|
1070 | gtk_label_set_text( GTK_LABEL( a->have_lb ), buf ); |
---|
1071 | |
---|
1072 | tr_strlsize( sizeStr, stat->downloadedEver, sizeof( sizeStr ) ); |
---|
1073 | gtk_label_set_text( GTK_LABEL( a->dl_lb ), sizeStr ); |
---|
1074 | |
---|
1075 | tr_strlsize( sizeStr, stat->uploadedEver, sizeof( sizeStr ) ); |
---|
1076 | gtk_label_set_text( GTK_LABEL( a->ul_lb ), sizeStr ); |
---|
1077 | |
---|
1078 | tr_strlsize( sizeStr, stat->corruptEver, sizeof( sizeStr ) ); |
---|
1079 | gtk_label_set_text( GTK_LABEL( a->failed_lb ), sizeStr ); |
---|
1080 | |
---|
1081 | tr_strlratio( buf, stat->ratio, sizeof( buf ) ); |
---|
1082 | gtk_label_set_text( GTK_LABEL( a->ratio_lb ), buf ); |
---|
1083 | |
---|
1084 | tr_strlspeed( buf, stat->swarmSpeed, sizeof( buf ) ); |
---|
1085 | gtk_label_set_text ( GTK_LABEL( a->swarm_lb ), buf ); |
---|
1086 | |
---|
1087 | gtk_label_set_text ( GTK_LABEL( a->err_lb ), |
---|
1088 | *stat->errorString ? stat->errorString : _( "None" ) ); |
---|
1089 | |
---|
1090 | refresh_time_lb( a->date_added_lb, stat->addedDate ); |
---|
1091 | |
---|
1092 | refresh_time_lb( a->last_activity_lb, stat->activityDate ); |
---|
1093 | |
---|
1094 | #ifdef SHOW_PIECES |
---|
1095 | if( GDK_IS_DRAWABLE ( a->availability_da->window ) ) |
---|
1096 | refresh_pieces ( a->availability_da, NULL, a->gtor ); |
---|
1097 | #endif |
---|
1098 | } |
---|
1099 | |
---|
1100 | static GtkWidget* |
---|
1101 | activity_page_new( TrTorrent * gtor ) |
---|
1102 | { |
---|
1103 | Activity * a = g_new ( Activity, 1 ); |
---|
1104 | int row = 0; |
---|
1105 | GtkWidget *t = hig_workarea_create ( ); |
---|
1106 | GtkWidget *l; |
---|
1107 | |
---|
1108 | a->gtor = gtor; |
---|
1109 | |
---|
1110 | hig_workarea_add_section_title ( t, &row, _( "Transfer" ) ); |
---|
1111 | |
---|
1112 | l = a->state_lb = gtk_label_new ( NULL ); |
---|
1113 | hig_workarea_add_row ( t, &row, _( "State:" ), l, NULL ); |
---|
1114 | |
---|
1115 | l = a->progress_lb = gtk_label_new ( NULL ); |
---|
1116 | hig_workarea_add_row ( t, &row, _( "Progress:" ), l, NULL ); |
---|
1117 | |
---|
1118 | l = a->have_lb = gtk_label_new ( NULL ); |
---|
1119 | /* "Have" refers to how much of the torrent we have */ |
---|
1120 | hig_workarea_add_row ( t, &row, _( "Have:" ), l, NULL ); |
---|
1121 | |
---|
1122 | l = a->dl_lb = gtk_label_new ( NULL ); |
---|
1123 | hig_workarea_add_row ( t, &row, _( "Downloaded:" ), l, NULL ); |
---|
1124 | |
---|
1125 | l = a->ul_lb = gtk_label_new ( NULL ); |
---|
1126 | hig_workarea_add_row ( t, &row, _( "Uploaded:" ), l, NULL ); |
---|
1127 | |
---|
1128 | /* how much downloaded data was corrupt */ |
---|
1129 | l = a->failed_lb = gtk_label_new ( NULL ); |
---|
1130 | hig_workarea_add_row ( t, &row, _( "Failed DL:" ), l, NULL ); |
---|
1131 | |
---|
1132 | l = a->ratio_lb = gtk_label_new ( NULL ); |
---|
1133 | hig_workarea_add_row ( t, &row, _( "Ratio:" ), l, NULL ); |
---|
1134 | |
---|
1135 | l = a->swarm_lb = gtk_label_new ( NULL ); |
---|
1136 | hig_workarea_add_row ( t, &row, _( "Swarm rate:" ), l, NULL ); |
---|
1137 | |
---|
1138 | l = a->err_lb = gtk_label_new ( NULL ); |
---|
1139 | hig_workarea_add_row ( t, &row, _( "Error:" ), l, NULL ); |
---|
1140 | |
---|
1141 | #ifdef SHOW_PIECES |
---|
1142 | hig_workarea_add_section_divider ( t, &row ); |
---|
1143 | hig_workarea_add_section_title ( t, &row, _( "Completion" ) ); |
---|
1144 | |
---|
1145 | w = a->availability_da = gtk_drawing_area_new ( ); |
---|
1146 | gtk_widget_set_size_request ( w, 0u, 100u ); |
---|
1147 | g_object_set_data ( G_OBJECT( w ), "draw-mode", |
---|
1148 | GINT_TO_POINTER( DRAW_PROG ) ); |
---|
1149 | g_signal_connect ( w, "expose-event", G_CALLBACK( |
---|
1150 | refresh_pieces ), gtor ); |
---|
1151 | hig_workarea_add_wide_control( t, &row, w ); |
---|
1152 | #endif |
---|
1153 | |
---|
1154 | hig_workarea_add_section_divider ( t, &row ); |
---|
1155 | hig_workarea_add_section_title ( t, &row, _( "Dates" ) ); |
---|
1156 | |
---|
1157 | l = a->date_added_lb = gtk_label_new ( NULL ); |
---|
1158 | hig_workarea_add_row ( t, &row, _( "Started at:" ), l, NULL ); |
---|
1159 | |
---|
1160 | l = a->last_activity_lb = gtk_label_new ( NULL ); |
---|
1161 | hig_workarea_add_row ( t, &row, _( "Last activity at:" ), l, NULL ); |
---|
1162 | |
---|
1163 | hig_workarea_add_section_divider ( t, &row ); |
---|
1164 | hig_workarea_finish ( t, &row ); |
---|
1165 | g_object_set_data_full ( G_OBJECT( t ), "activity-data", a, g_free ); |
---|
1166 | return t; |
---|
1167 | } |
---|
1168 | |
---|
1169 | /**** |
---|
1170 | ***** OPTIONS |
---|
1171 | ****/ |
---|
1172 | |
---|
1173 | static void |
---|
1174 | speed_toggled_cb( GtkToggleButton * tb, |
---|
1175 | gpointer gtor, |
---|
1176 | int up_or_down ) |
---|
1177 | { |
---|
1178 | tr_torrent * tor = tr_torrent_handle ( gtor ); |
---|
1179 | gboolean b = gtk_toggle_button_get_active( tb ); |
---|
1180 | |
---|
1181 | tr_torrentSetSpeedMode( tor, up_or_down, b ? TR_SPEEDLIMIT_SINGLE |
---|
1182 | : TR_SPEEDLIMIT_GLOBAL ); |
---|
1183 | } |
---|
1184 | |
---|
1185 | static void |
---|
1186 | ul_speed_toggled_cb( GtkToggleButton *tb, |
---|
1187 | gpointer gtor ) |
---|
1188 | { |
---|
1189 | speed_toggled_cb( tb, gtor, TR_UP ); |
---|
1190 | } |
---|
1191 | |
---|
1192 | static void |
---|
1193 | dl_speed_toggled_cb( GtkToggleButton *tb, |
---|
1194 | gpointer gtor ) |
---|
1195 | { |
---|
1196 | speed_toggled_cb( tb, gtor, TR_DOWN ); |
---|
1197 | } |
---|
1198 | |
---|
1199 | static void |
---|
1200 | sensitize_from_check_cb( GtkToggleButton *toggle, |
---|
1201 | gpointer w ) |
---|
1202 | { |
---|
1203 | gtk_widget_set_sensitive ( GTK_WIDGET( w ), |
---|
1204 | gtk_toggle_button_get_active( toggle ) ); |
---|
1205 | } |
---|
1206 | |
---|
1207 | static void |
---|
1208 | setSpeedLimit( GtkSpinButton* spin, |
---|
1209 | gpointer gtor, |
---|
1210 | int up_or_down ) |
---|
1211 | { |
---|
1212 | tr_torrent * tor = tr_torrent_handle ( gtor ); |
---|
1213 | int kb_sec = gtk_spin_button_get_value_as_int ( spin ); |
---|
1214 | |
---|
1215 | tr_torrentSetSpeedLimit( tor, up_or_down, kb_sec ); |
---|
1216 | } |
---|
1217 | |
---|
1218 | static void |
---|
1219 | ul_speed_spun_cb( GtkSpinButton *spin, |
---|
1220 | gpointer gtor ) |
---|
1221 | { |
---|
1222 | setSpeedLimit( spin, gtor, TR_UP ); |
---|
1223 | } |
---|
1224 | |
---|
1225 | static void |
---|
1226 | dl_speed_spun_cb( GtkSpinButton *spin, |
---|
1227 | gpointer gtor ) |
---|
1228 | { |
---|
1229 | setSpeedLimit( spin, gtor, TR_DOWN ); |
---|
1230 | } |
---|
1231 | |
---|
1232 | static void |
---|
1233 | max_peers_spun_cb( GtkSpinButton * spin, |
---|
1234 | gpointer gtor ) |
---|
1235 | { |
---|
1236 | const uint16_t n = gtk_spin_button_get_value( spin ); |
---|
1237 | |
---|
1238 | tr_torrentSetPeerLimit( tr_torrent_handle( gtor ), n ); |
---|
1239 | } |
---|
1240 | |
---|
1241 | static GtkWidget* |
---|
1242 | options_page_new( TrTorrent * gtor ) |
---|
1243 | { |
---|
1244 | uint16_t maxConnectedPeers; |
---|
1245 | int i, row; |
---|
1246 | gboolean b; |
---|
1247 | GtkWidget * t, *w, *tb; |
---|
1248 | tr_torrent * tor = tr_torrent_handle ( gtor ); |
---|
1249 | |
---|
1250 | row = 0; |
---|
1251 | t = hig_workarea_create ( ); |
---|
1252 | hig_workarea_add_section_title ( t, &row, _( "Limits" ) ); |
---|
1253 | |
---|
1254 | tb = |
---|
1255 | gtk_check_button_new_with_mnemonic ( _( |
---|
1256 | "Limit _download speed (KB/s):" ) ); |
---|
1257 | b = tr_torrentGetSpeedMode( tor, TR_DOWN ) == TR_SPEEDLIMIT_SINGLE; |
---|
1258 | gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON( tb ), b ); |
---|
1259 | g_signal_connect ( tb, "toggled", G_CALLBACK( |
---|
1260 | dl_speed_toggled_cb ), gtor ); |
---|
1261 | |
---|
1262 | i = tr_torrentGetSpeedLimit( tor, TR_DOWN ); |
---|
1263 | w = gtk_spin_button_new_with_range( 1, INT_MAX, 5 ); |
---|
1264 | gtk_spin_button_set_value( GTK_SPIN_BUTTON( w ), i ); |
---|
1265 | |
---|
1266 | g_signal_connect ( w, "value-changed", G_CALLBACK( |
---|
1267 | dl_speed_spun_cb ), gtor ); |
---|
1268 | g_signal_connect ( tb, "toggled", G_CALLBACK( |
---|
1269 | sensitize_from_check_cb ), w ); |
---|
1270 | sensitize_from_check_cb ( GTK_TOGGLE_BUTTON( tb ), w ); |
---|
1271 | hig_workarea_add_row_w ( t, &row, tb, w, NULL ); |
---|
1272 | |
---|
1273 | tb = |
---|
1274 | gtk_check_button_new_with_mnemonic ( _( |
---|
1275 | "Limit _upload speed (KB/s):" ) ); |
---|
1276 | b = tr_torrentGetSpeedMode( tor, TR_UP ) == TR_SPEEDLIMIT_SINGLE; |
---|
1277 | gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON( tb ), b ); |
---|
1278 | g_signal_connect ( tb, "toggled", G_CALLBACK( |
---|
1279 | ul_speed_toggled_cb ), gtor ); |
---|
1280 | |
---|
1281 | i = tr_torrentGetSpeedLimit( tor, TR_UP ); |
---|
1282 | w = gtk_spin_button_new_with_range( 1, INT_MAX, 5 ); |
---|
1283 | gtk_spin_button_set_value( GTK_SPIN_BUTTON( w ), i ); |
---|
1284 | |
---|
1285 | g_signal_connect ( w, "value-changed", G_CALLBACK( |
---|
1286 | ul_speed_spun_cb ), gtor ); |
---|
1287 | g_signal_connect ( tb, "toggled", G_CALLBACK( |
---|
1288 | sensitize_from_check_cb ), w ); |
---|
1289 | sensitize_from_check_cb ( GTK_TOGGLE_BUTTON( tb ), w ); |
---|
1290 | hig_workarea_add_row_w ( t, &row, tb, w, NULL ); |
---|
1291 | |
---|
1292 | hig_workarea_add_section_divider ( t, &row ); |
---|
1293 | hig_workarea_add_section_title ( t, &row, _( "Peer Connections" ) ); |
---|
1294 | |
---|
1295 | maxConnectedPeers = tr_torrentGetPeerLimit( tor ); |
---|
1296 | w = gtk_spin_button_new_with_range( 1, 3000, 5 ); |
---|
1297 | gtk_spin_button_set_value( GTK_SPIN_BUTTON( w ), maxConnectedPeers ); |
---|
1298 | hig_workarea_add_row( t, &row, _( "_Maximum peers:" ), w, w ); |
---|
1299 | g_signal_connect( w, "value-changed", G_CALLBACK( |
---|
1300 | max_peers_spun_cb ), gtor ); |
---|
1301 | |
---|
1302 | hig_workarea_finish ( t, &row ); |
---|
1303 | return t; |
---|
1304 | } |
---|
1305 | |
---|
1306 | static void |
---|
1307 | refresh_options( GtkWidget * top UNUSED ) |
---|
1308 | {} |
---|
1309 | |
---|
1310 | /**** |
---|
1311 | ***** TRACKER |
---|
1312 | ****/ |
---|
1313 | |
---|
1314 | #define TRACKER_PAGE "tracker-page" |
---|
1315 | |
---|
1316 | struct tracker_page |
---|
1317 | { |
---|
1318 | TrTorrent * gtor; |
---|
1319 | |
---|
1320 | GtkTreeView * view; |
---|
1321 | GtkListStore * store; |
---|
1322 | GtkTreeSelection * sel; |
---|
1323 | |
---|
1324 | GtkWidget * add_button; |
---|
1325 | GtkWidget * remove_button; |
---|
1326 | GtkWidget * save_button; |
---|
1327 | GtkWidget * revert_button; |
---|
1328 | |
---|
1329 | GtkWidget * last_scrape_time_lb; |
---|
1330 | GtkWidget * last_scrape_response_lb; |
---|
1331 | GtkWidget * next_scrape_countdown_lb; |
---|
1332 | |
---|
1333 | GtkWidget * last_announce_time_lb; |
---|
1334 | GtkWidget * last_announce_response_lb; |
---|
1335 | GtkWidget * next_announce_countdown_lb; |
---|
1336 | GtkWidget * manual_announce_countdown_lb; |
---|
1337 | }; |
---|
1338 | |
---|
1339 | static GtkWidget* |
---|
1340 | tracker_page_new( TrTorrent * gtor ) |
---|
1341 | { |
---|
1342 | GtkWidget * t; |
---|
1343 | GtkWidget * l; |
---|
1344 | GtkWidget * w; |
---|
1345 | int row = 0; |
---|
1346 | const char * s; |
---|
1347 | struct tracker_page * page = g_new0( struct tracker_page, 1 ); |
---|
1348 | const tr_info * info = tr_torrent_info ( gtor ); |
---|
1349 | |
---|
1350 | page->gtor = gtor; |
---|
1351 | |
---|
1352 | t = hig_workarea_create( ); |
---|
1353 | hig_workarea_add_section_title( t, &row, _( "Trackers" ) ); |
---|
1354 | |
---|
1355 | w = tracker_list_new( gtor ); |
---|
1356 | hig_workarea_add_wide_control( t, &row, w ); |
---|
1357 | |
---|
1358 | hig_workarea_add_section_divider( t, &row ); |
---|
1359 | hig_workarea_add_section_title( t, &row, _( "Scrape" ) ); |
---|
1360 | |
---|
1361 | s = _( "Last scrape at:" ); |
---|
1362 | l = gtk_label_new( NULL ); |
---|
1363 | page->last_scrape_time_lb = l; |
---|
1364 | hig_workarea_add_row( t, &row, s, l, NULL ); |
---|
1365 | |
---|
1366 | s = _( "Tracker responded:" ); |
---|
1367 | l = gtk_label_new( NULL ); |
---|
1368 | page->last_scrape_response_lb = l; |
---|
1369 | hig_workarea_add_row( t, &row, s, l, NULL ); |
---|
1370 | |
---|
1371 | s = _( "Next scrape in:" ); |
---|
1372 | l = gtk_label_new( NULL ); |
---|
1373 | page->next_scrape_countdown_lb = l; |
---|
1374 | hig_workarea_add_row( t, &row, s, l, NULL ); |
---|
1375 | |
---|
1376 | hig_workarea_add_section_divider( t, &row ); |
---|
1377 | hig_workarea_add_section_title( t, &row, _( "Announce" ) ); |
---|
1378 | |
---|
1379 | l = gtk_label_new( info->trackers[0].announce ); |
---|
1380 | gtk_label_set_ellipsize( GTK_LABEL( l ), PANGO_ELLIPSIZE_END ); |
---|
1381 | hig_workarea_add_row ( t, &row, _( "Tracker:" ), l, NULL ); |
---|
1382 | |
---|
1383 | s = _( "Last announce at:" ); |
---|
1384 | l = gtk_label_new( NULL ); |
---|
1385 | page->last_announce_time_lb = l; |
---|
1386 | hig_workarea_add_row( t, &row, s, l, NULL ); |
---|
1387 | |
---|
1388 | s = _( "Tracker responded:" ); |
---|
1389 | l = gtk_label_new( NULL ); |
---|
1390 | page->last_announce_response_lb = l; |
---|
1391 | hig_workarea_add_row( t, &row, s, l, NULL ); |
---|
1392 | |
---|
1393 | s = _( "Next announce in:" ); |
---|
1394 | l = gtk_label_new( NULL ); |
---|
1395 | page->next_announce_countdown_lb = l; |
---|
1396 | hig_workarea_add_row( t, &row, s, l, NULL ); |
---|
1397 | |
---|
1398 | /* how long until the tracker will honor user |
---|
1399 | * pressing the "ask for more peers" button */ |
---|
1400 | s = _( "Manual announce allowed in:" ); |
---|
1401 | l = gtk_label_new( NULL ); |
---|
1402 | page->manual_announce_countdown_lb = l; |
---|
1403 | hig_workarea_add_row( t, &row, s, l, NULL ); |
---|
1404 | |
---|
1405 | hig_workarea_finish( t, &row ); |
---|
1406 | g_object_set_data_full( G_OBJECT( t ), TRACKER_PAGE, page, g_free ); |
---|
1407 | return t; |
---|
1408 | } |
---|
1409 | |
---|
1410 | static void |
---|
1411 | refresh_countdown_lb( GtkWidget * w, |
---|
1412 | time_t t, |
---|
1413 | const char * countdown_done ) |
---|
1414 | { |
---|
1415 | const time_t now = time( NULL ); |
---|
1416 | GtkLabel * l = GTK_LABEL( w ); |
---|
1417 | |
---|
1418 | if( t == 1 ) |
---|
1419 | gtk_label_set_text( l, _( "In progress" ) ); |
---|
1420 | else if( t < now ) |
---|
1421 | gtk_label_set_text( l, countdown_done ); |
---|
1422 | else |
---|
1423 | { |
---|
1424 | char buf[1024]; |
---|
1425 | const int seconds = t - now; |
---|
1426 | tr_strltime( buf, seconds, sizeof( buf ) ); |
---|
1427 | gtk_label_set_text( l, buf ); |
---|
1428 | } |
---|
1429 | } |
---|
1430 | |
---|
1431 | static void |
---|
1432 | refresh_tracker( GtkWidget * w ) |
---|
1433 | { |
---|
1434 | GtkWidget * l; |
---|
1435 | time_t t; |
---|
1436 | struct tracker_page * page = g_object_get_data( G_OBJECT( |
---|
1437 | w ), TRACKER_PAGE ); |
---|
1438 | const tr_stat * torStat = tr_torrent_stat( page->gtor ); |
---|
1439 | |
---|
1440 | l = page->last_scrape_time_lb; |
---|
1441 | t = torStat->lastScrapeTime; |
---|
1442 | refresh_time_lb( l, t ); |
---|
1443 | |
---|
1444 | l = page->last_scrape_response_lb; |
---|
1445 | gtk_label_set_text( GTK_LABEL( l ), torStat->scrapeResponse ); |
---|
1446 | |
---|
1447 | l = page->next_scrape_countdown_lb; |
---|
1448 | t = torStat->nextScrapeTime; |
---|
1449 | refresh_countdown_lb( l, t, _( "Never" ) ); |
---|
1450 | |
---|
1451 | l = page->last_announce_time_lb; |
---|
1452 | t = torStat->lastAnnounceTime; |
---|
1453 | refresh_time_lb( l, t ); |
---|
1454 | |
---|
1455 | l = page->last_announce_response_lb; |
---|
1456 | gtk_label_set_text( GTK_LABEL( l ), torStat->announceResponse ); |
---|
1457 | |
---|
1458 | l = page->next_announce_countdown_lb; |
---|
1459 | t = torStat->nextAnnounceTime; |
---|
1460 | refresh_countdown_lb( l, t, _( "Never" ) ); |
---|
1461 | |
---|
1462 | l = page->manual_announce_countdown_lb; |
---|
1463 | t = torStat->manualAnnounceTime; |
---|
1464 | refresh_countdown_lb( l, t, _( "Now" ) ); |
---|
1465 | } |
---|
1466 | |
---|
1467 | /**** |
---|
1468 | ***** DIALOG |
---|
1469 | ****/ |
---|
1470 | |
---|
1471 | static void |
---|
1472 | torrent_destroyed( gpointer dialog, |
---|
1473 | GObject * dead_torrent UNUSED ) |
---|
1474 | { |
---|
1475 | gtk_widget_destroy ( GTK_WIDGET( dialog ) ); |
---|
1476 | } |
---|
1477 | |
---|
1478 | static void |
---|
1479 | remove_tag( gpointer tag ) |
---|
1480 | { |
---|
1481 | g_source_remove ( GPOINTER_TO_UINT( tag ) ); /* stop the periodic refresh */ |
---|
1482 | } |
---|
1483 | |
---|
1484 | static void |
---|
1485 | response_cb( GtkDialog * dialog, |
---|
1486 | int response UNUSED, |
---|
1487 | gpointer gtor ) |
---|
1488 | { |
---|
1489 | g_object_weak_unref ( G_OBJECT( gtor ), torrent_destroyed, dialog ); |
---|
1490 | gtk_widget_destroy ( GTK_WIDGET( dialog ) ); |
---|
1491 | } |
---|
1492 | |
---|
1493 | static gboolean |
---|
1494 | periodic_refresh( gpointer data ) |
---|
1495 | { |
---|
1496 | refresh_tracker ( g_object_get_data ( G_OBJECT( data ), "tracker-top" ) ); |
---|
1497 | refresh_peers ( g_object_get_data ( G_OBJECT( data ), "peers-top" ) ); |
---|
1498 | refresh_activity ( g_object_get_data ( G_OBJECT( data ), |
---|
1499 | "activity-top" ) ); |
---|
1500 | refresh_options ( g_object_get_data ( G_OBJECT( data ), "options-top" ) ); |
---|
1501 | return TRUE; |
---|
1502 | } |
---|
1503 | |
---|
1504 | GtkWidget* |
---|
1505 | torrent_inspector_new( GtkWindow * parent, |
---|
1506 | TrTorrent * gtor ) |
---|
1507 | { |
---|
1508 | guint tag; |
---|
1509 | GtkWidget * d, *n, *w; |
---|
1510 | tr_torrent * tor = tr_torrent_handle ( gtor ); |
---|
1511 | char sizeStr[64]; |
---|
1512 | char title[512]; |
---|
1513 | const tr_info * info = tr_torrent_info ( gtor ); |
---|
1514 | |
---|
1515 | /* create the dialog */ |
---|
1516 | tr_strlsize( sizeStr, info->totalSize, sizeof( sizeStr ) ); |
---|
1517 | /* %1$s is torrent name |
---|
1518 | %2$s its file size */ |
---|
1519 | g_snprintf( title, sizeof( title ), _( |
---|
1520 | "Details for %1$s (%2$s)" ), info->name, sizeStr ); |
---|
1521 | d = gtk_dialog_new_with_buttons ( title, parent, 0, |
---|
1522 | GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, |
---|
1523 | NULL ); |
---|
1524 | gtk_window_set_role ( GTK_WINDOW( d ), "tr-info" ); |
---|
1525 | g_signal_connect ( d, "response", G_CALLBACK ( response_cb ), gtor ); |
---|
1526 | gtk_dialog_set_has_separator( GTK_DIALOG( d ), FALSE ); |
---|
1527 | gtk_container_set_border_width( GTK_CONTAINER( d ), GUI_PAD ); |
---|
1528 | g_object_weak_ref ( G_OBJECT( gtor ), torrent_destroyed, d ); |
---|
1529 | |
---|
1530 | |
---|
1531 | /* add the notebook */ |
---|
1532 | n = gtk_notebook_new ( ); |
---|
1533 | gtk_container_set_border_width ( GTK_CONTAINER ( n ), GUI_PAD ); |
---|
1534 | |
---|
1535 | w = activity_page_new ( gtor ); |
---|
1536 | g_object_set_data ( G_OBJECT( d ), "activity-top", w ); |
---|
1537 | gtk_notebook_append_page ( GTK_NOTEBOOK( n ), w, |
---|
1538 | gtk_label_new ( _( "Activity" ) ) ); |
---|
1539 | |
---|
1540 | w = peer_page_new ( gtor ); |
---|
1541 | g_object_set_data ( G_OBJECT( d ), "peers-top", w ); |
---|
1542 | gtk_notebook_append_page ( GTK_NOTEBOOK( n ), w, |
---|
1543 | gtk_label_new ( _( "Peers" ) ) ); |
---|
1544 | |
---|
1545 | w = tracker_page_new( gtor ); |
---|
1546 | g_object_set_data( G_OBJECT( d ), "tracker-top", w ); |
---|
1547 | gtk_notebook_append_page( GTK_NOTEBOOK( n ), w, |
---|
1548 | gtk_label_new( _( "Tracker" ) ) ); |
---|
1549 | |
---|
1550 | gtk_notebook_append_page ( GTK_NOTEBOOK( n ), |
---|
1551 | info_page_new ( tor ), |
---|
1552 | gtk_label_new ( _( "Information" ) ) ); |
---|
1553 | |
---|
1554 | w = file_list_new( gtor ); |
---|
1555 | gtk_container_set_border_width( GTK_CONTAINER( w ), GUI_PAD_BIG ); |
---|
1556 | g_object_set_data ( G_OBJECT( d ), "files-top", w ); |
---|
1557 | gtk_notebook_append_page ( GTK_NOTEBOOK( n ), w, |
---|
1558 | gtk_label_new ( _( "Files" ) ) ); |
---|
1559 | |
---|
1560 | w = options_page_new ( gtor ); |
---|
1561 | g_object_set_data ( G_OBJECT( d ), "options-top", w ); |
---|
1562 | gtk_notebook_append_page ( GTK_NOTEBOOK( n ), w, |
---|
1563 | gtk_label_new ( _( "Options" ) ) ); |
---|
1564 | |
---|
1565 | gtk_box_pack_start( GTK_BOX( GTK_DIALOG( d )->vbox ), n, TRUE, TRUE, 0 ); |
---|
1566 | |
---|
1567 | tag = g_timeout_add ( UPDATE_INTERVAL_MSEC, periodic_refresh, d ); |
---|
1568 | g_object_set_data_full ( G_OBJECT( d ), "tag", |
---|
1569 | GUINT_TO_POINTER( tag ), remove_tag ); |
---|
1570 | |
---|
1571 | /* return the results */ |
---|
1572 | periodic_refresh ( d ); |
---|
1573 | gtk_widget_show_all ( GTK_DIALOG( d )->vbox ); |
---|
1574 | return d; |
---|
1575 | } |
---|
1576 | |
---|