source: trunk/gtk/torrent-cell-renderer.c @ 11638

Last change on this file since 11638 was 11638, checked in by jordan, 11 years ago

(trunk gtk) #3519 "webseeds don't work" -- fix webseed peer counts and download speeds in the GTK+ client's main window

  • Property svn:keywords set to Date Rev Author Id
File size: 34.3 KB
Line 
1/*
2 * This file Copyright (C) 2007-2010 Mnemosyne LLC
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: torrent-cell-renderer.c 11638 2011-01-06 02:40:10Z jordan $
11 */
12
13#include <string.h> /* strcmp() */
14#include <gtk/gtk.h>
15#include <glib/gi18n.h>
16#include <libtransmission/transmission.h>
17#include <libtransmission/utils.h> /* tr_truncd() */
18#include "hig.h"
19#include "icons.h"
20#include "torrent-cell-renderer.h"
21#include "tr-torrent.h"
22#include "util.h"
23
24/* #define TEST_RTL */
25
26enum
27{
28    P_TORRENT = 1,
29    P_UPLOAD_SPEED,
30    P_DOWNLOAD_SPEED,
31    P_BAR_HEIGHT,
32    P_COMPACT
33};
34
35#define DEFAULT_BAR_HEIGHT 12
36#define SMALL_SCALE 0.9
37#define COMPACT_ICON_SIZE GTK_ICON_SIZE_MENU
38#define FULL_ICON_SIZE GTK_ICON_SIZE_DND
39
40/***
41****
42***/
43
44static char*
45getProgressString( const tr_torrent * tor,
46                   const tr_info    * info,
47                   const tr_stat    * st )
48{
49    const int      isDone = st->leftUntilDone == 0;
50    const uint64_t haveTotal = st->haveUnchecked + st->haveValid;
51    const int      isSeed = st->haveValid >= info->totalSize;
52    char           buf1[32], buf2[32], buf3[32], buf4[32], buf5[32], buf6[32];
53    char *         str;
54    double         seedRatio;
55    const gboolean hasSeedRatio = tr_torrentGetSeedRatio( tor, &seedRatio );
56
57    if( !isDone ) /* downloading */
58    {
59        str = g_strdup_printf(
60            /* %1$s is how much we've got,
61               %2$s is how much we'll have when done,
62               %3$s%% is a percentage of the two */
63            _( "%1$s of %2$s (%3$s%%)" ),
64            tr_strlsize( buf1, haveTotal, sizeof( buf1 ) ),
65            tr_strlsize( buf2, st->sizeWhenDone, sizeof( buf2 ) ),
66            tr_strlpercent( buf3, st->percentDone * 100.0, sizeof( buf3 ) ) );
67    }
68    else if( !isSeed ) /* partial seeds */
69    {
70        if( hasSeedRatio )
71        {
72            str = g_strdup_printf(
73                /* %1$s is how much we've got,
74                   %2$s is the torrent's total size,
75                   %3$s%% is a percentage of the two,
76                   %4$s is how much we've uploaded,
77                   %5$s is our upload-to-download ratio,
78                   %6$s is the ratio we want to reach before we stop uploading */
79                _( "%1$s of %2$s (%3$s%%), uploaded %4$s (Ratio: %5$s Goal: %6$s)" ),
80                tr_strlsize( buf1, haveTotal, sizeof( buf1 ) ),
81                tr_strlsize( buf2, info->totalSize, sizeof( buf2 ) ),
82                tr_strlpercent( buf3, st->percentComplete * 100.0, sizeof( buf3 ) ),
83                tr_strlsize( buf4, st->uploadedEver, sizeof( buf4 ) ),
84                tr_strlratio( buf5, st->ratio, sizeof( buf5 ) ),
85                tr_strlratio( buf6, seedRatio, sizeof( buf6 ) ) );
86        }
87        else
88        {
89            str = g_strdup_printf(
90                /* %1$s is how much we've got,
91                   %2$s is the torrent's total size,
92                   %3$s%% is a percentage of the two,
93                   %4$s is how much we've uploaded,
94                   %5$s is our upload-to-download ratio */
95                _( "%1$s of %2$s (%3$s%%), uploaded %4$s (Ratio: %5$s)" ),
96                tr_strlsize( buf1, haveTotal, sizeof( buf1 ) ),
97                tr_strlsize( buf2, info->totalSize, sizeof( buf2 ) ),
98                tr_strlpercent( buf3, st->percentComplete * 100.0, sizeof( buf3 ) ),
99                tr_strlsize( buf4, st->uploadedEver, sizeof( buf4 ) ),
100                tr_strlratio( buf5, st->ratio, sizeof( buf5 ) ) );
101        }
102    }
103    else /* seeding */
104    {
105        if( hasSeedRatio )
106        {
107            str = g_strdup_printf(
108                /* %1$s is the torrent's total size,
109                   %2$s is how much we've uploaded,
110                   %3$s is our upload-to-download ratio,
111                   %4$s is the ratio we want to reach before we stop uploading */
112                _( "%1$s, uploaded %2$s (Ratio: %3$s Goal: %4$s)" ),
113                tr_strlsize( buf1, info->totalSize, sizeof( buf1 ) ),
114                tr_strlsize( buf2, st->uploadedEver, sizeof( buf2 ) ),
115                tr_strlratio( buf3, st->ratio, sizeof( buf3 ) ),
116                tr_strlratio( buf4, seedRatio, sizeof( buf4 ) ) );
117        }
118        else /* seeding w/o a ratio */
119        {
120            str = g_strdup_printf(
121                /* %1$s is the torrent's total size,
122                   %2$s is how much we've uploaded,
123                   %3$s is our upload-to-download ratio */
124                _( "%1$s, uploaded %2$s (Ratio: %3$s)" ),
125                tr_strlsize( buf1, info->totalSize, sizeof( buf1 ) ),
126                tr_strlsize( buf2, st->uploadedEver, sizeof( buf2 ) ),
127                tr_strlratio( buf3, st->ratio, sizeof( buf3 ) ) );
128        }
129    }
130
131    /* add time when downloading */
132    if( ( st->activity == TR_STATUS_DOWNLOAD )
133        || ( hasSeedRatio && ( st->activity == TR_STATUS_SEED ) ) )
134    {
135        const int eta = st->eta;
136        GString * gstr = g_string_new( str );
137        g_string_append( gstr, " - " );
138        if( eta < 0 )
139            g_string_append( gstr, _( "Remaining time unknown" ) );
140        else
141        {
142            char timestr[128];
143            tr_strltime( timestr, eta, sizeof( timestr ) );
144            /* time remaining */
145            g_string_append_printf( gstr, _( "%s remaining" ), timestr );
146        }
147        g_free( str );
148        str = g_string_free( gstr, FALSE );
149    }
150
151    return str;
152}
153
154static char*
155getShortTransferString( const tr_torrent  * tor,
156                        const tr_stat     * st,
157                        double              uploadSpeed_KBps,
158                        double              downloadSpeed_KBps,
159                        char              * buf,
160                        size_t              buflen )
161{
162    char downStr[32], upStr[32];
163    const int haveMeta = tr_torrentHasMetadata( tor );
164    const int haveUp = haveMeta && st->peersGettingFromUs > 0;
165    const int haveDown = haveMeta && ( ( st->peersSendingToUs > 0 ) || ( st->webseedsSendingToUs > 0 ) );
166
167    if( haveDown )
168        tr_formatter_speed_KBps( downStr, downloadSpeed_KBps, sizeof( downStr ) );
169    if( haveUp )
170        tr_formatter_speed_KBps( upStr, uploadSpeed_KBps, sizeof( upStr ) );
171
172    if( haveDown && haveUp )
173        /* 1==down arrow, 2==down speed, 3==up arrow, 4==down speed */
174        g_snprintf( buf, buflen, _( "%1$s %2$s, %3$s %4$s" ),
175                    gtr_get_unicode_string( GTR_UNICODE_DOWN ), downStr,
176                    gtr_get_unicode_string( GTR_UNICODE_UP ), upStr );
177    else if( haveDown )
178        /* bandwidth speed + unicode arrow */
179        g_snprintf( buf, buflen, _( "%1$s %2$s" ),
180                    gtr_get_unicode_string( GTR_UNICODE_DOWN ), downStr );
181    else if( haveUp )
182        /* bandwidth speed + unicode arrow */
183        g_snprintf( buf, buflen, _( "%1$s %2$s" ),
184                    gtr_get_unicode_string( GTR_UNICODE_UP ), upStr );
185    else if( haveMeta )
186        /* the torrent isn't uploading or downloading */
187        g_strlcpy( buf, _( "Idle" ), buflen );
188    else
189        *buf = '\0';
190
191    return buf;
192}
193
194static char*
195getShortStatusString( const tr_torrent  * tor,
196                      const tr_stat     * st,
197                      double              uploadSpeed_KBps,
198                      double              downloadSpeed_KBps )
199{
200    GString * gstr = g_string_new( NULL );
201
202    switch( st->activity )
203    {
204        case TR_STATUS_STOPPED:
205            if( st->finished )
206                g_string_assign( gstr, _( "Finished" ) );
207            else
208                g_string_assign( gstr, _( "Paused" ) );
209            break;
210
211        case TR_STATUS_CHECK_WAIT:
212            g_string_assign( gstr, _( "Waiting to verify local data" ) );
213            break;
214
215        case TR_STATUS_CHECK:
216            g_string_append_printf( gstr,
217                                    _( "Verifying local data (%.1f%% tested)" ),
218                                    tr_truncd( st->recheckProgress * 100.0, 1 ) );
219            break;
220
221        case TR_STATUS_DOWNLOAD:
222        case TR_STATUS_SEED:
223        {
224            char buf[512];
225            if( st->activity != TR_STATUS_DOWNLOAD )
226            {
227                tr_strlratio( buf, st->ratio, sizeof( buf ) );
228                g_string_append_printf( gstr, _( "Ratio %s" ), buf );
229                g_string_append( gstr, ", " );
230            }
231            getShortTransferString( tor, st, uploadSpeed_KBps, downloadSpeed_KBps, buf, sizeof( buf ) );
232            g_string_append( gstr, buf );
233            break;
234        }
235
236        default:
237            break;
238    }
239
240    return g_string_free( gstr, FALSE );
241}
242
243static char*
244getStatusString( const tr_torrent  * tor,
245                 const tr_stat     * st,
246                 const double        uploadSpeed_KBps,
247                 const double        downloadSpeed_KBps )
248{
249    const int isActive = st->activity != TR_STATUS_STOPPED;
250    const int isChecking = st->activity == TR_STATUS_CHECK
251                        || st->activity == TR_STATUS_CHECK_WAIT;
252
253    GString * gstr = g_string_new( NULL );
254
255    if( st->error )
256    {
257        const char * fmt[] = { NULL, N_( "Tracker gave a warning: \"%s\"" ),
258                                     N_( "Tracker gave an error: \"%s\"" ),
259                                     N_( "Error: %s" ) };
260        g_string_append_printf( gstr, _( fmt[st->error] ), st->errorString );
261    }
262    else switch( st->activity )
263    {
264        case TR_STATUS_STOPPED:
265        case TR_STATUS_CHECK_WAIT:
266        case TR_STATUS_CHECK:
267        {
268            char * pch = getShortStatusString( tor, st, uploadSpeed_KBps, downloadSpeed_KBps );
269            g_string_assign( gstr, pch );
270            g_free( pch );
271            break;
272        }
273
274        case TR_STATUS_DOWNLOAD:
275        {
276            if( tr_torrentHasMetadata( tor ) )
277            {
278                g_string_append_printf( gstr,
279                    gtr_ngettext( "Downloading from %1$'d of %2$'d connected peer",
280                                  "Downloading from %1$'d of %2$'d connected peers",
281                                  st->webseedsSendingToUs + st->peersSendingToUs ),
282                    st->webseedsSendingToUs + st->peersSendingToUs,
283                    st->webseedsSendingToUs + st->peersConnected );
284            }
285            else
286            {
287                g_string_append_printf( gstr,
288                    gtr_ngettext( "Downloading metadata from %1$'d peer (%2$d%% done)",
289                                  "Downloading metadata from %1$'d peers (%2$d%% done)",
290                                  st->peersConnected + st->peersConnected ),
291                    st->peersConnected + st->webseedsSendingToUs,
292                    (int)(100.0*st->metadataPercentComplete) );
293            }
294            break;
295        }
296
297        case TR_STATUS_SEED:
298            g_string_append_printf( gstr,
299                gtr_ngettext( "Seeding to %1$'d of %2$'d connected peer",
300                              "Seeding to %1$'d of %2$'d connected peers",
301                              st->peersConnected ),
302                st->peersGettingFromUs,
303                st->peersConnected );
304                break;
305    }
306
307    if( isActive && !isChecking )
308    {
309        char buf[256];
310        getShortTransferString( tor, st, uploadSpeed_KBps, downloadSpeed_KBps, buf, sizeof( buf ) );
311        if( *buf )
312            g_string_append_printf( gstr, " - %s", buf );
313    }
314
315    return g_string_free( gstr, FALSE );
316}
317
318/***
319****
320***/
321
322static GtkCellRendererClass * parent_class = NULL;
323
324struct TorrentCellRendererPrivate
325{
326    tr_torrent       * tor;
327    GtkCellRenderer  * text_renderer;
328    GtkCellRenderer  * text_renderer_err;
329    GtkCellRenderer  * progress_renderer;
330    GtkCellRenderer  * icon_renderer;
331    int bar_height;
332
333    /* Use this instead of tr_stat.pieceUploadSpeed so that the model can
334       control when the speed displays get updated. This is done to keep
335       the individual torrents' speeds and the status bar's overall speed
336       in sync even if they refresh at slightly different times */
337    double upload_speed_KBps;
338
339    /* @see upload_speed_Bps */
340    double download_speed_KBps;
341
342    gboolean compact;
343};
344
345/***
346****
347***/
348
349static GdkPixbuf*
350get_icon( const tr_torrent * tor, GtkIconSize icon_size, GtkWidget * for_widget )
351{
352    const char * mime_type;
353    const tr_info * info = tr_torrentInfo( tor );
354
355    if( info->fileCount == 0  )
356        mime_type = UNKNOWN_MIME_TYPE;
357    else if( info->fileCount > 1 )
358        mime_type = DIRECTORY_MIME_TYPE;
359    else if( strchr( info->files[0].name, '/' ) != NULL )
360        mime_type = DIRECTORY_MIME_TYPE;
361    else
362        mime_type = gtr_get_mime_type_from_filename( info->files[0].name );
363
364    return gtr_get_mime_type_icon( mime_type, icon_size, for_widget );
365}
366
367static GtkCellRenderer*
368get_text_renderer( const tr_stat * st, TorrentCellRenderer * r )
369{
370    return st->error ? r->priv->text_renderer_err : r->priv->text_renderer;
371}
372
373/***
374****
375***/
376
377static void
378get_size_compact( TorrentCellRenderer * cell,
379                  GtkWidget           * widget,
380                  gint                * width,
381                  gint                * height )
382{
383    int w, h;
384    int xpad, ypad;
385    GdkRectangle icon_area;
386    GdkRectangle name_area;
387    GdkRectangle stat_area;
388    const char * name;
389    char * status;
390    GdkPixbuf * icon;
391    GtkCellRenderer * text_renderer;
392
393    struct TorrentCellRendererPrivate * p = cell->priv;
394    const tr_torrent * tor = p->tor;
395    const tr_stat * st = tr_torrentStatCached( (tr_torrent*)tor );
396
397    icon = get_icon( tor, COMPACT_ICON_SIZE, widget );
398    name = tr_torrentInfo( tor )->name;
399    status = getShortStatusString( tor, st, p->upload_speed_KBps, p->download_speed_KBps );
400    gtr_cell_renderer_get_padding( GTK_CELL_RENDERER( cell ), &xpad, &ypad );
401
402    /* get the idealized cell dimensions */
403    g_object_set( p->icon_renderer, "pixbuf", icon, NULL );
404    gtk_cell_renderer_get_size( p->icon_renderer, widget, NULL, NULL, NULL, &w, &h );
405    icon_area.width = w;
406    icon_area.height = h;
407    text_renderer = get_text_renderer( st, cell );
408    g_object_set( text_renderer, "text", name, "ellipsize", PANGO_ELLIPSIZE_NONE,  "scale", 1.0, NULL );
409    gtk_cell_renderer_get_size( text_renderer, widget, NULL, NULL, NULL, &w, &h );
410    name_area.width = w;
411    name_area.height = h;
412    g_object_set( text_renderer, "text", status, "scale", SMALL_SCALE, NULL );
413    gtk_cell_renderer_get_size( text_renderer, widget, NULL, NULL, NULL, &w, &h );
414    stat_area.width = w;
415    stat_area.height = h;
416
417    /**
418    *** LAYOUT
419    **/
420
421#define BAR_WIDTH 50
422    if( width != NULL )
423        *width = xpad * 2 + icon_area.width + GUI_PAD + name_area.width + GUI_PAD + BAR_WIDTH + GUI_PAD + stat_area.width;
424    if( height != NULL )
425        *height = ypad * 2 + MAX( name_area.height, p->bar_height );
426
427    /* cleanup */
428    g_free( status );
429    g_object_unref( icon );
430}
431
432#define MAX3(a,b,c) MAX(a,MAX(b,c))
433
434static void
435get_size_full( TorrentCellRenderer * cell,
436               GtkWidget           * widget,
437               gint                * width,
438               gint                * height )
439{
440    int w, h;
441    int xpad, ypad;
442    GdkRectangle icon_area;
443    GdkRectangle name_area;
444    GdkRectangle stat_area;
445    GdkRectangle prog_area;
446    const char * name;
447    char * status;
448    char * progress;
449    GdkPixbuf * icon;
450    GtkCellRenderer * text_renderer;
451
452    struct TorrentCellRendererPrivate * p = cell->priv;
453    const tr_torrent * tor = p->tor;
454    const tr_stat * st = tr_torrentStatCached( (tr_torrent*)tor );
455    const tr_info * inf = tr_torrentInfo( tor );
456
457    icon = get_icon( tor, FULL_ICON_SIZE, widget );
458    name = inf->name;
459    status = getStatusString( tor, st, p->upload_speed_KBps, p->download_speed_KBps );
460    progress = getProgressString( tor, inf, st );
461    gtr_cell_renderer_get_padding( GTK_CELL_RENDERER( cell ), &xpad, &ypad );
462
463    /* get the idealized cell dimensions */
464    g_object_set( p->icon_renderer, "pixbuf", icon, NULL );
465    gtk_cell_renderer_get_size( p->icon_renderer, widget, NULL, NULL, NULL, &w, &h );
466    icon_area.width = w;
467    icon_area.height = h;
468    text_renderer = get_text_renderer( st, cell );
469    g_object_set( text_renderer, "text", name, "weight", PANGO_WEIGHT_BOLD, "scale", 1.0, "ellipsize", PANGO_ELLIPSIZE_NONE, NULL );
470    gtk_cell_renderer_get_size( text_renderer, widget, NULL, NULL, NULL, &w, &h );
471    name_area.width = w;
472    name_area.height = h;
473    g_object_set( text_renderer, "text", progress, "weight", PANGO_WEIGHT_NORMAL, "scale", SMALL_SCALE, NULL );
474    gtk_cell_renderer_get_size( text_renderer, widget, NULL, NULL, NULL, &w, &h );
475    prog_area.width = w;
476    prog_area.height = h;
477    g_object_set( text_renderer, "text", status, NULL );
478    gtk_cell_renderer_get_size( text_renderer, widget, NULL, NULL, NULL, &w, &h );
479    stat_area.width = w;
480    stat_area.height = h;
481
482    /**
483    *** LAYOUT
484    **/
485
486    if( width != NULL )
487        *width = xpad * 2 + icon_area.width + GUI_PAD + MAX3( name_area.width, prog_area.width, stat_area.width );
488    if( height != NULL )
489        *height = ypad * 2 + name_area.height + prog_area.height + GUI_PAD_SMALL + p->bar_height + GUI_PAD_SMALL + stat_area.height;
490
491    /* cleanup */
492    g_free( status );
493    g_free( progress );
494    g_object_unref( icon );
495}
496
497
498static void
499torrent_cell_renderer_get_size( GtkCellRenderer  * cell,
500                                GtkWidget        * widget,
501                                GdkRectangle     * cell_area,
502                                gint             * x_offset,
503                                gint             * y_offset,
504                                gint             * width,
505                                gint             * height )
506{
507    TorrentCellRenderer * self = TORRENT_CELL_RENDERER( cell );
508
509    if( self && self->priv->tor )
510    {
511        int w, h;
512        struct TorrentCellRendererPrivate * p = self->priv;
513
514        if( p->compact )
515            get_size_compact( TORRENT_CELL_RENDERER( cell ), widget, &w, &h );
516        else
517            get_size_full( TORRENT_CELL_RENDERER( cell ), widget, &w, &h );
518
519        if( width )
520            *width = w;
521
522        if( height )
523            *height = h;
524
525        if( x_offset )
526            *x_offset = cell_area ? cell_area->x : 0;
527
528        if( y_offset ) {
529            int xpad, ypad;
530            gtr_cell_renderer_get_padding( cell, &xpad, &ypad );
531            *y_offset = cell_area ? (int)((cell_area->height - (ypad*2 +h)) / 2.0) : 0;
532        }
533    }
534}
535
536static void
537render_compact( TorrentCellRenderer   * cell,
538                GdkDrawable           * window,
539                GtkWidget             * widget,
540                GdkRectangle          * background_area,
541                GdkRectangle          * cell_area UNUSED,
542                GdkRectangle          * expose_area UNUSED,
543                GtkCellRendererState    flags )
544{
545    int xpad, ypad;
546    GdkRectangle icon_area;
547    GdkRectangle name_area;
548    GdkRectangle stat_area;
549    GdkRectangle prog_area;
550    GdkRectangle fill_area;
551    const char * name;
552    char * status;
553    GdkPixbuf * icon;
554    GtkCellRenderer * text_renderer;
555
556    struct TorrentCellRendererPrivate * p = cell->priv;
557    const tr_torrent * tor = p->tor;
558    const tr_stat * st = tr_torrentStatCached( (tr_torrent*)tor );
559    const gboolean active = st->activity != TR_STATUS_STOPPED;
560    const double percentDone = MAX( 0.0, st->percentDone );
561    const gboolean sensitive = active || st->error;
562
563    icon = get_icon( tor, COMPACT_ICON_SIZE, widget );
564    name = tr_torrentInfo( tor )->name;
565    status = getShortStatusString( tor, st, p->upload_speed_KBps, p->download_speed_KBps );
566    gtr_cell_renderer_get_padding( GTK_CELL_RENDERER( cell ), &xpad, &ypad );
567
568    fill_area = *background_area;
569    fill_area.x += xpad;
570    fill_area.y += ypad;
571    fill_area.width -= xpad * 2;
572    fill_area.height -= ypad * 2;
573    icon_area = name_area = stat_area = prog_area = fill_area;
574
575    g_object_set( p->icon_renderer, "pixbuf", icon, NULL );
576    gtk_cell_renderer_get_size( p->icon_renderer, widget, NULL, NULL, NULL, &icon_area.width, NULL );
577    text_renderer = get_text_renderer( st, cell );
578    g_object_set( text_renderer, "text", name, "ellipsize", PANGO_ELLIPSIZE_NONE, "scale", 1.0, NULL );
579    gtk_cell_renderer_get_size( text_renderer, widget, NULL, NULL, NULL, &name_area.width, NULL );
580    g_object_set( text_renderer, "text", status, "scale", SMALL_SCALE, NULL );
581    gtk_cell_renderer_get_size( text_renderer, widget, NULL, NULL, NULL, &stat_area.width, NULL );
582
583    icon_area.x = fill_area.x;
584    prog_area.x = fill_area.x + fill_area.width - BAR_WIDTH;
585    prog_area.width = BAR_WIDTH;
586    stat_area.x = prog_area.x - GUI_PAD - stat_area.width;
587    name_area.x = icon_area.x + icon_area.width + GUI_PAD;
588    name_area.y = fill_area.y;
589    name_area.width = stat_area.x - GUI_PAD - name_area.x;
590
591    /**
592    *** RENDER
593    **/
594
595    g_object_set( p->icon_renderer, "pixbuf", icon, "sensitive", sensitive, NULL );
596    gtk_cell_renderer_render( p->icon_renderer, window, widget, &icon_area, &icon_area, &icon_area, flags );
597    g_object_set( p->progress_renderer, "value", (int)(percentDone*100.0), "text", NULL, "sensitive", sensitive, NULL );
598    gtk_cell_renderer_render( p->progress_renderer, window, widget, &prog_area, &prog_area, &prog_area, flags );
599    g_object_set( text_renderer, "text", status, "scale", SMALL_SCALE, "sensitive", sensitive, "ellipsize", PANGO_ELLIPSIZE_END, NULL );
600    gtk_cell_renderer_render( text_renderer, window, widget, &stat_area, &stat_area, &stat_area, flags );
601    g_object_set( text_renderer, "text", name, "scale", 1.0, NULL );
602    gtk_cell_renderer_render( text_renderer, window, widget, &name_area, &name_area, &name_area, flags );
603
604    /* cleanup */
605    g_free( status );
606    g_object_unref( icon );
607}
608
609static void
610render_full( TorrentCellRenderer   * cell,
611             GdkDrawable           * window,
612             GtkWidget             * widget,
613             GdkRectangle          * background_area,
614             GdkRectangle          * cell_area UNUSED,
615             GdkRectangle          * expose_area UNUSED,
616             GtkCellRendererState    flags )
617{
618    int w, h;
619    int xpad, ypad;
620    GdkRectangle fill_area;
621    GdkRectangle icon_area;
622    GdkRectangle name_area;
623    GdkRectangle stat_area;
624    GdkRectangle prog_area;
625    GdkRectangle prct_area;
626    const char * name;
627    char * status;
628    char * progress;
629    GdkPixbuf * icon;
630    GtkCellRenderer * text_renderer;
631
632    struct TorrentCellRendererPrivate * p = cell->priv;
633    const tr_torrent * tor = p->tor;
634    const tr_stat * st = tr_torrentStatCached( (tr_torrent*)tor );
635    const tr_info * inf = tr_torrentInfo( tor );
636    const gboolean active = st->activity != TR_STATUS_STOPPED;
637    const double percentDone = MAX( 0.0, st->percentDone );
638    const gboolean sensitive = active || st->error;
639
640    icon = get_icon( tor, FULL_ICON_SIZE, widget );
641    name = inf->name;
642    status = getStatusString( tor, st, p->upload_speed_KBps, p->download_speed_KBps );
643    progress = getProgressString( tor, inf, st );
644    gtr_cell_renderer_get_padding( GTK_CELL_RENDERER( cell ), &xpad, &ypad );
645
646    /* get the idealized cell dimensions */
647    g_object_set( p->icon_renderer, "pixbuf", icon, NULL );
648    gtk_cell_renderer_get_size( p->icon_renderer, widget, NULL, NULL, NULL, &w, &h );
649    icon_area.width = w;
650    icon_area.height = h;
651    text_renderer = get_text_renderer( st, cell );
652    g_object_set( text_renderer, "text", name, "weight", PANGO_WEIGHT_BOLD, "ellipsize", PANGO_ELLIPSIZE_NONE, "scale", 1.0, NULL );
653    gtk_cell_renderer_get_size( text_renderer, widget, NULL, NULL, NULL, &w, &h );
654    name_area.width = w;
655    name_area.height = h;
656    g_object_set( text_renderer, "text", progress, "weight", PANGO_WEIGHT_NORMAL, "scale", SMALL_SCALE, NULL );
657    gtk_cell_renderer_get_size( text_renderer, widget, NULL, NULL, NULL, &w, &h );
658    prog_area.width = w;
659    prog_area.height = h;
660    g_object_set( text_renderer, "text", status, NULL );
661    gtk_cell_renderer_get_size( text_renderer, widget, NULL, NULL, NULL, &w, &h );
662    stat_area.width = w;
663    stat_area.height = h;
664
665    /**
666    *** LAYOUT
667    **/
668
669    fill_area = *background_area;
670    fill_area.x += xpad;
671    fill_area.y += ypad;
672    fill_area.width -= xpad * 2;
673    fill_area.height -= ypad * 2;
674
675    /* icon */
676    icon_area.x = fill_area.x;
677    icon_area.y = fill_area.y + ( fill_area.height - icon_area.height ) / 2;
678
679    /* name */
680    name_area.x = icon_area.x + icon_area.width + GUI_PAD;
681    name_area.y = fill_area.y;
682    name_area.width = fill_area.width - GUI_PAD - icon_area.width - GUI_PAD_SMALL;
683
684    /* prog */
685    prog_area.x = name_area.x;
686    prog_area.y = name_area.y + name_area.height;
687    prog_area.width = name_area.width;
688
689    /* progressbar */
690    prct_area.x = prog_area.x;
691    prct_area.y = prog_area.y + prog_area.height + GUI_PAD_SMALL;
692    prct_area.width = prog_area.width;
693    prct_area.height = p->bar_height;
694
695    /* status */
696    stat_area.x = prct_area.x;
697    stat_area.y = prct_area.y + prct_area.height + GUI_PAD_SMALL;
698    stat_area.width = prct_area.width;
699
700    /**
701    *** RENDER
702    **/
703
704    g_object_set( p->icon_renderer, "pixbuf", icon, "sensitive", sensitive, NULL );
705    gtk_cell_renderer_render( p->icon_renderer, window, widget, &icon_area, &icon_area, &icon_area, flags );
706    g_object_set( text_renderer, "text", name, "scale", 1.0, "sensitive", sensitive, "ellipsize", PANGO_ELLIPSIZE_END, "weight", PANGO_WEIGHT_BOLD, NULL );
707    gtk_cell_renderer_render( text_renderer, window, widget, &name_area, &name_area, &name_area, flags );
708    g_object_set( text_renderer, "text", progress, "scale", SMALL_SCALE, "weight", PANGO_WEIGHT_NORMAL, NULL );
709    gtk_cell_renderer_render( text_renderer, window, widget, &prog_area, &prog_area, &prog_area, flags );
710    g_object_set( p->progress_renderer, "value", (int)(percentDone*100.0), "text", "", "sensitive", sensitive, NULL );
711    gtk_cell_renderer_render( p->progress_renderer, window, widget, &prct_area, &prct_area, &prct_area, flags );
712    g_object_set( text_renderer, "text", status, NULL );
713    gtk_cell_renderer_render( text_renderer, window, widget, &stat_area, &stat_area, &stat_area, flags );
714
715    /* cleanup */
716    g_free( status );
717    g_free( progress );
718    g_object_unref( icon );
719}
720
721static void
722torrent_cell_renderer_render( GtkCellRenderer       * cell,
723                              GdkDrawable           * window,
724                              GtkWidget             * widget,
725                              GdkRectangle          * background_area,
726                              GdkRectangle          * cell_area,
727                              GdkRectangle          * expose_area,
728                              GtkCellRendererState    flags )
729{
730    TorrentCellRenderer * self = TORRENT_CELL_RENDERER( cell );
731
732#ifdef TEST_RTL
733    GtkTextDirection      real_dir = gtk_widget_get_direction( widget );
734    gtk_widget_set_direction( widget, GTK_TEXT_DIR_RTL );
735#endif
736
737    if( self && self->priv->tor )
738    {
739        struct TorrentCellRendererPrivate * p = self->priv;
740        if( p->compact )
741            render_compact( self, window, widget, background_area, cell_area, expose_area, flags );
742        else
743            render_full( self, window, widget, background_area, cell_area, expose_area, flags );
744    }
745
746#ifdef TEST_RTL
747    gtk_widget_set_direction( widget, real_dir );
748#endif
749}
750
751static void
752torrent_cell_renderer_set_property( GObject      * object,
753                                    guint          property_id,
754                                    const GValue * v,
755                                    GParamSpec   * pspec )
756{
757    TorrentCellRenderer * self = TORRENT_CELL_RENDERER( object );
758    struct TorrentCellRendererPrivate * p = self->priv;
759
760    switch( property_id )
761    {
762        case P_TORRENT:        p->tor                 = g_value_get_pointer( v ); break;
763        case P_UPLOAD_SPEED:   p->upload_speed_KBps   = g_value_get_double( v ); break;
764        case P_DOWNLOAD_SPEED: p->download_speed_KBps = g_value_get_double( v ); break;
765        case P_BAR_HEIGHT:     p->bar_height          = g_value_get_int( v ); break;
766        case P_COMPACT:        p->compact             = g_value_get_boolean( v ); break;
767        default: G_OBJECT_WARN_INVALID_PROPERTY_ID( object, property_id, pspec ); break;
768    }
769}
770
771static void
772torrent_cell_renderer_get_property( GObject     * object,
773                                    guint         property_id,
774                                    GValue      * v,
775                                    GParamSpec  * pspec )
776{
777    const TorrentCellRenderer * self = TORRENT_CELL_RENDERER( object );
778    struct TorrentCellRendererPrivate * p = self->priv;
779
780    switch( property_id )
781    {
782        case P_TORRENT:        g_value_set_pointer( v, p->tor ); break;
783        case P_UPLOAD_SPEED:   g_value_set_double( v, p->upload_speed_KBps ); break;
784        case P_DOWNLOAD_SPEED: g_value_set_double( v, p->download_speed_KBps ); break;
785        case P_BAR_HEIGHT:     g_value_set_int( v, p->bar_height ); break;
786        case P_COMPACT:        g_value_set_boolean( v, p->compact ); break;
787        default: G_OBJECT_WARN_INVALID_PROPERTY_ID( object, property_id, pspec ); break;
788    }
789}
790
791static void
792torrent_cell_renderer_dispose( GObject * o )
793{
794    TorrentCellRenderer * r = TORRENT_CELL_RENDERER( o );
795    GObjectClass *        parent;
796
797    if( r && r->priv )
798    {
799        g_object_unref( G_OBJECT( r->priv->text_renderer ) );
800        g_object_unref( G_OBJECT( r->priv->text_renderer_err ) );
801        g_object_unref( G_OBJECT( r->priv->progress_renderer ) );
802        g_object_unref( G_OBJECT( r->priv->icon_renderer ) );
803        r->priv = NULL;
804    }
805
806    parent = g_type_class_peek( g_type_parent( TORRENT_CELL_RENDERER_TYPE ) );
807    parent->dispose( o );
808}
809
810static void
811torrent_cell_renderer_class_init( TorrentCellRendererClass * klass )
812{
813    GObjectClass *         gobject_class = G_OBJECT_CLASS( klass );
814    GtkCellRendererClass * cell_class = GTK_CELL_RENDERER_CLASS( klass );
815
816    g_type_class_add_private( klass,
817                             sizeof( struct TorrentCellRendererPrivate ) );
818
819    parent_class = (GtkCellRendererClass*) g_type_class_peek_parent( klass );
820
821    cell_class->render = torrent_cell_renderer_render;
822    cell_class->get_size = torrent_cell_renderer_get_size;
823    gobject_class->set_property = torrent_cell_renderer_set_property;
824    gobject_class->get_property = torrent_cell_renderer_get_property;
825    gobject_class->dispose = torrent_cell_renderer_dispose;
826
827    g_object_class_install_property( gobject_class, P_TORRENT,
828                                    g_param_spec_pointer( "torrent", NULL,
829                                                          "tr_torrent*",
830                                                          G_PARAM_READWRITE ) );
831
832    g_object_class_install_property( gobject_class, P_UPLOAD_SPEED,
833                                    g_param_spec_double( "piece-upload-speed", NULL,
834                                                         "tr_stat.pieceUploadSpeed_KBps",
835                                                         0, INT_MAX, 0,
836                                                         G_PARAM_READWRITE ) );
837
838    g_object_class_install_property( gobject_class, P_DOWNLOAD_SPEED,
839                                    g_param_spec_double( "piece-download-speed", NULL,
840                                                         "tr_stat.pieceDownloadSpeed_KBps",
841                                                         0, INT_MAX, 0,
842                                                         G_PARAM_READWRITE ) );
843
844    g_object_class_install_property( gobject_class, P_BAR_HEIGHT,
845                                    g_param_spec_int( "bar-height", NULL,
846                                                      "Bar Height",
847                                                      1, INT_MAX,
848                                                      DEFAULT_BAR_HEIGHT,
849                                                      G_PARAM_READWRITE ) );
850
851    g_object_class_install_property( gobject_class, P_COMPACT,
852                                    g_param_spec_boolean( "compact", NULL,
853                                                          "Compact Mode",
854                                                          FALSE,
855                                                          G_PARAM_READWRITE ) );
856}
857
858static void
859torrent_cell_renderer_init( GTypeInstance *  instance,
860                            gpointer g_class UNUSED )
861{
862    TorrentCellRenderer *               self = TORRENT_CELL_RENDERER(
863        instance );
864    struct TorrentCellRendererPrivate * p;
865
866    p = self->priv = G_TYPE_INSTANCE_GET_PRIVATE(
867            self,
868            TORRENT_CELL_RENDERER_TYPE,
869            struct
870            TorrentCellRendererPrivate );
871
872    p->tor = NULL;
873    p->text_renderer = gtk_cell_renderer_text_new( );
874    g_object_set( p->text_renderer, "xpad", 0, "ypad", 0, NULL );
875    p->text_renderer_err = gtk_cell_renderer_text_new(  );
876    g_object_set( p->text_renderer_err, "xpad", 0, "ypad", 0, NULL );
877    p->progress_renderer = gtk_cell_renderer_progress_new(  );
878    p->icon_renderer = gtk_cell_renderer_pixbuf_new(  );
879    g_object_set( p->text_renderer_err, "foreground", "red", NULL );
880    gtr_object_ref_sink( p->text_renderer );
881    gtr_object_ref_sink( p->text_renderer_err );
882    gtr_object_ref_sink( p->progress_renderer );
883    gtr_object_ref_sink( p->icon_renderer );
884
885    p->bar_height = DEFAULT_BAR_HEIGHT;
886}
887
888GType
889torrent_cell_renderer_get_type( void )
890{
891    static GType type = 0;
892
893    if( !type )
894    {
895        static const GTypeInfo info =
896        {
897            sizeof( TorrentCellRendererClass ),
898            NULL,                                            /* base_init */
899            NULL,                                            /* base_finalize */
900            (GClassInitFunc)torrent_cell_renderer_class_init,
901            NULL,                                            /* class_finalize
902                                                               */
903            NULL,                                            /* class_data */
904            sizeof( TorrentCellRenderer ),
905            0,                                               /* n_preallocs */
906            (GInstanceInitFunc)torrent_cell_renderer_init,
907            NULL
908        };
909
910        type = g_type_register_static( GTK_TYPE_CELL_RENDERER,
911                                       "TorrentCellRenderer",
912                                       &info, (GTypeFlags)0 );
913    }
914
915    return type;
916}
917
918GtkCellRenderer *
919torrent_cell_renderer_new( void )
920{
921    return (GtkCellRenderer *) g_object_new( TORRENT_CELL_RENDERER_TYPE,
922                                             NULL );
923}
924
Note: See TracBrowser for help on using the repository browser.