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

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

(trunk, GTK+/Qt) #4281 "Visualize seed progress like the web client does" -- done, with a modified patch & suggestion from mag

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