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

Last change on this file since 12591 was 12591, checked in by jordan, 10 years ago

(trunk gtk) #4320 "Port GTK client to GTK 3." -- transmission-gtk now compiles cleanly & runs on GTK 3.

  • Property svn:keywords set to Date Rev Author Id
File size: 36.9 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 12591 2011-07-26 01:02:32Z 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
374gtr_cell_renderer_get_preferred_size( GtkCellRenderer  * renderer,
375                                      GtkWidget        * widget,
376                                      GtkRequisition   * minimum_size,
377                                      GtkRequisition   * natural_size )
378{
379#if GTK_CHECK_VERSION( 3,0,0 )
380
381    gtk_cell_renderer_get_preferred_size( renderer, widget, minimum_size, natural_size );
382
383#else
384
385    GtkRequisition r;
386    gtk_cell_renderer_get_size( renderer, widget, NULL, NULL, NULL, &r.width, &r.height );
387    if( minimum_size ) *minimum_size = r;
388    if( natural_size ) *natural_size = r;
389
390#endif
391}
392
393static void
394get_size_compact( TorrentCellRenderer * cell,
395                  GtkWidget           * widget,
396                  gint                * width,
397                  gint                * height )
398{
399    int xpad, ypad;
400    GtkRequisition icon_size;
401    GtkRequisition name_size;
402    GtkRequisition stat_size;
403    const char * name;
404    GdkPixbuf * icon;
405
406    struct TorrentCellRendererPrivate * p = cell->priv;
407    const tr_torrent * tor = p->tor;
408    const tr_stat * st = tr_torrentStatCached( (tr_torrent*)tor );
409    GString * gstr_stat = p->gstr1;
410
411    icon = get_icon( tor, COMPACT_ICON_SIZE, widget );
412    name = tr_torrentName( tor );
413    g_string_truncate( gstr_stat, 0 );
414    getShortStatusString( gstr_stat, tor, st, p->upload_speed_KBps, p->download_speed_KBps );
415    gtr_cell_renderer_get_padding( GTK_CELL_RENDERER( cell ), &xpad, &ypad );
416
417    /* get the idealized cell dimensions */
418    g_object_set( p->icon_renderer, "pixbuf", icon, NULL );
419    gtr_cell_renderer_get_preferred_size( p->icon_renderer, widget, NULL, &icon_size );
420    g_object_set( p->text_renderer, "text", name, "ellipsize", PANGO_ELLIPSIZE_NONE,  "scale", 1.0, NULL );
421    gtr_cell_renderer_get_preferred_size( p->text_renderer, widget, NULL, &name_size );
422    g_object_set( p->text_renderer, "text", gstr_stat->str, "scale", SMALL_SCALE, NULL );
423    gtr_cell_renderer_get_preferred_size( p->text_renderer, widget, NULL, &stat_size );
424
425    /**
426    *** LAYOUT
427    **/
428
429#define BAR_WIDTH 50
430    if( width != NULL )
431        *width = xpad * 2 + icon_size.width + GUI_PAD + name_size.width + GUI_PAD + BAR_WIDTH + GUI_PAD + stat_size.width;
432    if( height != NULL )
433        *height = ypad * 2 + MAX( name_size.height, p->bar_height );
434
435    /* cleanup */
436    g_object_unref( icon );
437}
438
439#define MAX3(a,b,c) MAX(a,MAX(b,c))
440
441static void
442get_size_full( TorrentCellRenderer * cell,
443               GtkWidget           * widget,
444               gint                * width,
445               gint                * height )
446{
447    int xpad, ypad;
448    GtkRequisition icon_size;
449    GtkRequisition name_size;
450    GtkRequisition stat_size;
451    GtkRequisition prog_size;
452    const char * name;
453    GdkPixbuf * icon;
454
455    struct TorrentCellRendererPrivate * p = cell->priv;
456    const tr_torrent * tor = p->tor;
457    const tr_stat * st = tr_torrentStatCached( (tr_torrent*)tor );
458    const tr_info * inf = tr_torrentInfo( tor );
459    GString * gstr_prog = p->gstr1;
460    GString * gstr_stat = p->gstr2;
461
462    icon = get_icon( tor, FULL_ICON_SIZE, widget );
463    name = tr_torrentName( tor );
464    g_string_truncate( gstr_stat, 0 );
465    getStatusString( gstr_stat, tor, st, p->upload_speed_KBps, p->download_speed_KBps );
466    g_string_truncate( gstr_prog, 0 );
467    getProgressString( gstr_prog, tor, inf, st );
468    gtr_cell_renderer_get_padding( GTK_CELL_RENDERER( cell ), &xpad, &ypad );
469
470    /* get the idealized cell dimensions */
471    g_object_set( p->icon_renderer, "pixbuf", icon, NULL );
472    gtr_cell_renderer_get_preferred_size( p->icon_renderer, widget, NULL, &icon_size );
473    g_object_set( p->text_renderer, "text", name, "weight", PANGO_WEIGHT_BOLD, "scale", 1.0, "ellipsize", PANGO_ELLIPSIZE_NONE, NULL );
474    gtr_cell_renderer_get_preferred_size( p->text_renderer, widget, NULL, &name_size );
475    g_object_set( p->text_renderer, "text", gstr_prog->str, "weight", PANGO_WEIGHT_NORMAL, "scale", SMALL_SCALE, NULL );
476    gtr_cell_renderer_get_preferred_size( p->text_renderer, widget, NULL, &prog_size );
477    g_object_set( p->text_renderer, "text", gstr_stat->str, NULL );
478    gtr_cell_renderer_get_preferred_size( p->text_renderer, widget, NULL, &stat_size );
479
480    /**
481    *** LAYOUT
482    **/
483
484    if( width != NULL )
485        *width = xpad * 2 + icon_size.width + GUI_PAD + MAX3( name_size.width, prog_size.width, stat_size.width );
486    if( height != NULL )
487        *height = ypad * 2 + name_size.height + prog_size.height + GUI_PAD_SMALL + p->bar_height + GUI_PAD_SMALL + stat_size.height;
488
489    /* cleanup */
490    g_object_unref( icon );
491}
492
493
494static void
495torrent_cell_renderer_get_size( GtkCellRenderer     * cell,
496                                GtkWidget           * widget,
497#if GTK_CHECK_VERSION( 3,0,0 )
498                                const GdkRectangle  * cell_area,
499#else
500                                GdkRectangle        * cell_area,
501#endif
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
536#if GTK_CHECK_VERSION( 3,0,0 )
537    typedef GdkRGBA GtrColor;
538    #define FOREGROUND_COLOR_KEY "foreground-rgba"
539#else
540    typedef GdkColor GtrColor;
541    #define FOREGROUND_COLOR_KEY "foreground-gdk"
542#endif
543
544static void
545get_text_color( GtkWidget * w, const tr_stat * st, GtrColor * setme )
546{
547#if GTK_CHECK_VERSION( 3,0,0 )
548
549    static const GdkRGBA red = { 1.0, 0, 0, 0 };
550    if( st->error )
551        *setme = red;
552    else if( st->activity == TR_STATUS_STOPPED )
553        gtk_style_context_get_color( gtk_widget_get_style_context( w ), GTK_STATE_FLAG_INSENSITIVE, setme );
554    else
555        gtk_style_context_get_color( gtk_widget_get_style_context( w ), GTK_STATE_FLAG_NORMAL, setme );
556
557#else
558
559    static const GdkColor red = { 0, 65535, 0, 0 };
560    if( st->error )
561        *setme = red;
562    else if( st->activity == TR_STATUS_STOPPED )
563        *setme = gtk_widget_get_style(w)->text[GTK_STATE_INSENSITIVE];
564    else
565        *setme = gtk_widget_get_style(w)->text[GTK_STATE_NORMAL];
566
567#endif
568}
569
570
571static double
572get_percent_done( const tr_torrent * tor, const tr_stat * st, bool * seed )
573{
574    double d;
575
576    if( ( st->activity == TR_STATUS_SEED ) && tr_torrentGetSeedRatio( tor, &d ) )
577    {
578        *seed = true;
579        d = MAX( 0.0, st->seedRatioPercentDone );
580    }
581    else
582    {
583        *seed = false;
584        d = MAX( 0.0, st->percentDone );
585    }
586
587    return d;
588}
589
590#if GTK_CHECK_VERSION( 3,0,0 )
591    typedef cairo_t GtrDrawable;
592#else
593    typedef GdkDrawable GtrDrawable;
594#endif
595
596static void
597gtr_cell_renderer_render( GtkCellRenderer       * renderer,
598                          GtrDrawable           * drawable,
599                          GtkWidget             * widget,
600                          const GdkRectangle    * area,
601                          GtkCellRendererState    flags)
602{
603#if GTK_CHECK_VERSION( 3,0,0 )
604    gtk_cell_renderer_render( renderer, drawable, widget, area, area, flags );
605#else
606    gtk_cell_renderer_render( renderer, drawable, widget, area, area, area, flags );
607#endif
608}
609
610static void
611render_compact( TorrentCellRenderer   * cell,
612                GtrDrawable           * window,
613                GtkWidget             * widget,
614                const GdkRectangle    * background_area,
615                const GdkRectangle    * cell_area UNUSED,
616                GtkCellRendererState    flags )
617{
618    int xpad, ypad;
619    GtkRequisition size;
620    GdkRectangle icon_area;
621    GdkRectangle name_area;
622    GdkRectangle stat_area;
623    GdkRectangle prog_area;
624    GdkRectangle fill_area;
625    const char * name;
626    GdkPixbuf * icon;
627    GtrColor text_color;
628    bool seed;
629
630    struct TorrentCellRendererPrivate * p = cell->priv;
631    const tr_torrent * tor = p->tor;
632    const tr_stat * st = tr_torrentStatCached( (tr_torrent*)tor );
633    const gboolean active = st->activity != TR_STATUS_STOPPED;
634    const double percentDone = get_percent_done( tor, st, &seed );
635    const gboolean sensitive = active || st->error;
636    GString * gstr_stat = p->gstr1;
637
638    icon = get_icon( tor, COMPACT_ICON_SIZE, widget );
639    name = tr_torrentName( tor );
640    g_string_truncate( gstr_stat, 0 );
641    getShortStatusString( gstr_stat, tor, st, p->upload_speed_KBps, p->download_speed_KBps );
642    gtr_cell_renderer_get_padding( GTK_CELL_RENDERER( cell ), &xpad, &ypad );
643    get_text_color( widget, st, &text_color );
644
645    fill_area = *background_area;
646    fill_area.x += xpad;
647    fill_area.y += ypad;
648    fill_area.width -= xpad * 2;
649    fill_area.height -= ypad * 2;
650    icon_area = name_area = stat_area = prog_area = fill_area;
651
652    g_object_set( p->icon_renderer, "pixbuf", icon, NULL );
653    gtr_cell_renderer_get_preferred_size( p->icon_renderer, widget, NULL, &size );
654    icon_area.width = size.width;
655    g_object_set( p->text_renderer, "text", name, "ellipsize", PANGO_ELLIPSIZE_NONE, "scale", 1.0, NULL );
656    gtr_cell_renderer_get_preferred_size( p->text_renderer, widget, NULL, &size );
657    name_area.width = size.width;
658    g_object_set( p->text_renderer, "text", gstr_stat->str, "scale", SMALL_SCALE, NULL );
659    gtr_cell_renderer_get_preferred_size( p->text_renderer, widget, NULL, &size );
660    stat_area.width = size.width;
661
662    icon_area.x = fill_area.x;
663    prog_area.x = fill_area.x + fill_area.width - BAR_WIDTH;
664    prog_area.width = BAR_WIDTH;
665    stat_area.x = prog_area.x - GUI_PAD - stat_area.width;
666    name_area.x = icon_area.x + icon_area.width + GUI_PAD;
667    name_area.y = fill_area.y;
668    name_area.width = stat_area.x - GUI_PAD - name_area.x;
669
670    /**
671    *** RENDER
672    **/
673
674    g_object_set( p->icon_renderer, "pixbuf", icon, "sensitive", sensitive, NULL );
675    gtr_cell_renderer_render( p->icon_renderer, window, widget, &icon_area, flags );
676    g_object_set( p->progress_renderer, "value", (int)(percentDone*100.0), "text", NULL, "sensitive", sensitive,
677#if GTK_CHECK_VERSION( 3,0,0 )
678        "inverted", seed,
679#elif GTK_CHECK_VERSION( 2,6,0 )
680        "orientation", (seed ? GTK_PROGRESS_RIGHT_TO_LEFT : GTK_PROGRESS_LEFT_TO_RIGHT),
681#endif
682        NULL );
683    gtr_cell_renderer_render( p->progress_renderer, window, widget, &prog_area, flags );
684    g_object_set( p->text_renderer, "text", gstr_stat->str, "scale", SMALL_SCALE, "ellipsize", PANGO_ELLIPSIZE_END, FOREGROUND_COLOR_KEY, &text_color, NULL );
685    gtr_cell_renderer_render( p->text_renderer, window, widget, &stat_area, flags );
686    g_object_set( p->text_renderer, "text", name, "scale", 1.0, NULL );
687    gtr_cell_renderer_render( p->text_renderer, window, widget, &name_area, flags );
688
689    /* cleanup */
690    g_object_unref( icon );
691}
692
693static void
694render_full( TorrentCellRenderer   * cell,
695             GtrDrawable           * window,
696             GtkWidget             * widget,
697             const GdkRectangle    * background_area,
698             const GdkRectangle    * cell_area UNUSED,
699             GtkCellRendererState    flags )
700{
701    int xpad, ypad;
702    GtkRequisition size;
703    GdkRectangle fill_area;
704    GdkRectangle icon_area;
705    GdkRectangle name_area;
706    GdkRectangle stat_area;
707    GdkRectangle prog_area;
708    GdkRectangle prct_area;
709    const char * name;
710    GdkPixbuf * icon;
711    GtrColor text_color;
712    bool seed;
713
714    struct TorrentCellRendererPrivate * p = cell->priv;
715    const tr_torrent * tor = p->tor;
716    const tr_stat * st = tr_torrentStatCached( (tr_torrent*)tor );
717    const tr_info * inf = tr_torrentInfo( tor );
718    const gboolean active = st->activity != TR_STATUS_STOPPED;
719    const double percentDone = get_percent_done( tor, st, &seed );
720    const gboolean sensitive = active || st->error;
721    GString * gstr_prog = p->gstr1;
722    GString * gstr_stat = p->gstr2;
723
724    icon = get_icon( tor, FULL_ICON_SIZE, widget );
725    name = tr_torrentName( tor );
726    g_string_truncate( gstr_prog, 0 );
727    getProgressString( gstr_prog, tor, inf, st );
728    g_string_truncate( gstr_stat, 0 );
729    getStatusString( gstr_stat, tor, st, p->upload_speed_KBps, p->download_speed_KBps );
730    gtr_cell_renderer_get_padding( GTK_CELL_RENDERER( cell ), &xpad, &ypad );
731    get_text_color( widget, st, &text_color );
732
733    /* get the idealized cell dimensions */
734    g_object_set( p->icon_renderer, "pixbuf", icon, NULL );
735    gtr_cell_renderer_get_preferred_size( p->icon_renderer, widget, NULL, &size );
736    icon_area.width = size.width;
737    icon_area.height = size.height;
738    g_object_set( p->text_renderer, "text", name, "weight", PANGO_WEIGHT_BOLD, "ellipsize", PANGO_ELLIPSIZE_NONE, "scale", 1.0, NULL );
739    gtr_cell_renderer_get_preferred_size( p->text_renderer, widget, NULL, &size );
740    name_area.width = size.width;
741    name_area.height = size.height;
742    g_object_set( p->text_renderer, "text", gstr_prog->str, "weight", PANGO_WEIGHT_NORMAL, "scale", SMALL_SCALE, NULL );
743    gtr_cell_renderer_get_preferred_size( p->text_renderer, widget, NULL, &size );
744    prog_area.width = size.width;
745    prog_area.height = size.height;
746    g_object_set( p->text_renderer, "text", gstr_stat->str, NULL );
747    gtr_cell_renderer_get_preferred_size( p->text_renderer, widget, NULL, &size );
748    stat_area.width = size.width;
749    stat_area.height = size.height;
750
751    /**
752    *** LAYOUT
753    **/
754
755    fill_area = *background_area;
756    fill_area.x += xpad;
757    fill_area.y += ypad;
758    fill_area.width -= xpad * 2;
759    fill_area.height -= ypad * 2;
760
761    /* icon */
762    icon_area.x = fill_area.x;
763    icon_area.y = fill_area.y + ( fill_area.height - icon_area.height ) / 2;
764
765    /* name */
766    name_area.x = icon_area.x + icon_area.width + GUI_PAD;
767    name_area.y = fill_area.y;
768    name_area.width = fill_area.width - GUI_PAD - icon_area.width - GUI_PAD_SMALL;
769
770    /* prog */
771    prog_area.x = name_area.x;
772    prog_area.y = name_area.y + name_area.height;
773    prog_area.width = name_area.width;
774
775    /* progressbar */
776    prct_area.x = prog_area.x;
777    prct_area.y = prog_area.y + prog_area.height + GUI_PAD_SMALL;
778    prct_area.width = prog_area.width;
779    prct_area.height = p->bar_height;
780
781    /* status */
782    stat_area.x = prct_area.x;
783    stat_area.y = prct_area.y + prct_area.height + GUI_PAD_SMALL;
784    stat_area.width = prct_area.width;
785
786    /**
787    *** RENDER
788    **/
789
790    g_object_set( p->icon_renderer, "pixbuf", icon, "sensitive", sensitive, NULL );
791    gtr_cell_renderer_render( p->icon_renderer, window, widget, &icon_area, flags );
792    g_object_set( p->text_renderer, "text", name, "scale", 1.0, FOREGROUND_COLOR_KEY, &text_color, "ellipsize", PANGO_ELLIPSIZE_END, "weight", PANGO_WEIGHT_BOLD, NULL );
793    gtr_cell_renderer_render( p->text_renderer, window, widget, &name_area, flags );
794    g_object_set( p->text_renderer, "text", gstr_prog->str, "scale", SMALL_SCALE, "weight", PANGO_WEIGHT_NORMAL, NULL );
795    gtr_cell_renderer_render( p->text_renderer, window, widget, &prog_area, flags );
796    g_object_set( p->progress_renderer, "value", (int)(percentDone*100.0), "text", "", "sensitive", sensitive,
797#if GTK_CHECK_VERSION( 3,0,0 )
798        "inverted", seed,
799#elif GTK_CHECK_VERSION( 2,6,0 )
800        "orientation", (seed ? GTK_PROGRESS_RIGHT_TO_LEFT : GTK_PROGRESS_LEFT_TO_RIGHT),
801#endif
802        NULL );
803    gtr_cell_renderer_render( p->progress_renderer, window, widget, &prct_area, flags );
804    g_object_set( p->text_renderer, "text", gstr_stat->str, NULL );
805    gtr_cell_renderer_render( p->text_renderer, window, widget, &stat_area, flags );
806
807    /* cleanup */
808    g_object_unref( icon );
809}
810
811static void
812torrent_cell_renderer_render( GtkCellRenderer       * cell,
813                              GtrDrawable           * window,
814                              GtkWidget             * widget,
815#if GTK_CHECK_VERSION( 3,0,0 )
816                              const GdkRectangle    * background_area,
817                              const GdkRectangle    * cell_area,
818#else
819                              GdkRectangle          * background_area,
820                              GdkRectangle          * cell_area,
821                              GdkRectangle          * expose_area UNUSED,
822#endif
823                              GtkCellRendererState    flags )
824{
825    TorrentCellRenderer * self = TORRENT_CELL_RENDERER( cell );
826
827#ifdef TEST_RTL
828    GtkTextDirection      real_dir = gtk_widget_get_direction( widget );
829    gtk_widget_set_direction( widget, GTK_TEXT_DIR_RTL );
830#endif
831
832    if( self && self->priv->tor )
833    {
834        struct TorrentCellRendererPrivate * p = self->priv;
835        if( p->compact )
836            render_compact( self, window, widget, background_area, cell_area, flags );
837        else
838            render_full( self, window, widget, background_area, cell_area, flags );
839    }
840
841#ifdef TEST_RTL
842    gtk_widget_set_direction( widget, real_dir );
843#endif
844}
845
846static void
847torrent_cell_renderer_set_property( GObject      * object,
848                                    guint          property_id,
849                                    const GValue * v,
850                                    GParamSpec   * pspec )
851{
852    TorrentCellRenderer * self = TORRENT_CELL_RENDERER( object );
853    struct TorrentCellRendererPrivate * p = self->priv;
854
855    switch( property_id )
856    {
857        case P_TORRENT:        p->tor                 = g_value_get_pointer( v ); break;
858        case P_UPLOAD_SPEED:   p->upload_speed_KBps   = g_value_get_double( v ); break;
859        case P_DOWNLOAD_SPEED: p->download_speed_KBps = g_value_get_double( v ); break;
860        case P_BAR_HEIGHT:     p->bar_height          = g_value_get_int( v ); break;
861        case P_COMPACT:        p->compact             = g_value_get_boolean( v ); break;
862        default: G_OBJECT_WARN_INVALID_PROPERTY_ID( object, property_id, pspec ); break;
863    }
864}
865
866static void
867torrent_cell_renderer_get_property( GObject     * object,
868                                    guint         property_id,
869                                    GValue      * v,
870                                    GParamSpec  * pspec )
871{
872    const TorrentCellRenderer * self = TORRENT_CELL_RENDERER( object );
873    struct TorrentCellRendererPrivate * p = self->priv;
874
875    switch( property_id )
876    {
877        case P_TORRENT:        g_value_set_pointer( v, p->tor ); break;
878        case P_UPLOAD_SPEED:   g_value_set_double( v, p->upload_speed_KBps ); break;
879        case P_DOWNLOAD_SPEED: g_value_set_double( v, p->download_speed_KBps ); break;
880        case P_BAR_HEIGHT:     g_value_set_int( v, p->bar_height ); break;
881        case P_COMPACT:        g_value_set_boolean( v, p->compact ); break;
882        default: G_OBJECT_WARN_INVALID_PROPERTY_ID( object, property_id, pspec ); break;
883    }
884}
885
886static void
887torrent_cell_renderer_dispose( GObject * o )
888{
889    TorrentCellRenderer * r = TORRENT_CELL_RENDERER( o );
890    GObjectClass *        parent;
891
892    if( r && r->priv )
893    {
894        g_string_free( r->priv->gstr1, TRUE );
895        g_string_free( r->priv->gstr2, TRUE );
896        g_object_unref( G_OBJECT( r->priv->text_renderer ) );
897        g_object_unref( G_OBJECT( r->priv->progress_renderer ) );
898        g_object_unref( G_OBJECT( r->priv->icon_renderer ) );
899        r->priv = NULL;
900    }
901
902    parent = g_type_class_peek( g_type_parent( TORRENT_CELL_RENDERER_TYPE ) );
903    parent->dispose( o );
904}
905
906static void
907torrent_cell_renderer_class_init( TorrentCellRendererClass * klass )
908{
909    GObjectClass *         gobject_class = G_OBJECT_CLASS( klass );
910    GtkCellRendererClass * cell_class = GTK_CELL_RENDERER_CLASS( klass );
911
912    g_type_class_add_private( klass,
913                             sizeof( struct TorrentCellRendererPrivate ) );
914
915    parent_class = (GtkCellRendererClass*) g_type_class_peek_parent( klass );
916
917    cell_class->render = torrent_cell_renderer_render;
918    cell_class->get_size = torrent_cell_renderer_get_size;
919    gobject_class->set_property = torrent_cell_renderer_set_property;
920    gobject_class->get_property = torrent_cell_renderer_get_property;
921    gobject_class->dispose = torrent_cell_renderer_dispose;
922
923    g_object_class_install_property( gobject_class, P_TORRENT,
924                                    g_param_spec_pointer( "torrent", NULL,
925                                                          "tr_torrent*",
926                                                          G_PARAM_READWRITE ) );
927
928    g_object_class_install_property( gobject_class, P_UPLOAD_SPEED,
929                                    g_param_spec_double( "piece-upload-speed", NULL,
930                                                         "tr_stat.pieceUploadSpeed_KBps",
931                                                         0, INT_MAX, 0,
932                                                         G_PARAM_READWRITE ) );
933
934    g_object_class_install_property( gobject_class, P_DOWNLOAD_SPEED,
935                                    g_param_spec_double( "piece-download-speed", NULL,
936                                                         "tr_stat.pieceDownloadSpeed_KBps",
937                                                         0, INT_MAX, 0,
938                                                         G_PARAM_READWRITE ) );
939
940    g_object_class_install_property( gobject_class, P_BAR_HEIGHT,
941                                    g_param_spec_int( "bar-height", NULL,
942                                                      "Bar Height",
943                                                      1, INT_MAX,
944                                                      DEFAULT_BAR_HEIGHT,
945                                                      G_PARAM_READWRITE ) );
946
947    g_object_class_install_property( gobject_class, P_COMPACT,
948                                    g_param_spec_boolean( "compact", NULL,
949                                                          "Compact Mode",
950                                                          FALSE,
951                                                          G_PARAM_READWRITE ) );
952}
953
954static void
955torrent_cell_renderer_init( GTypeInstance * instance, gpointer g_class UNUSED )
956{
957    struct TorrentCellRendererPrivate * p;
958    TorrentCellRenderer * self = TORRENT_CELL_RENDERER( instance );
959
960    p = self->priv = G_TYPE_INSTANCE_GET_PRIVATE(
961            self,
962            TORRENT_CELL_RENDERER_TYPE,
963            struct
964            TorrentCellRendererPrivate );
965
966    p->tor = NULL;
967    p->gstr1 = g_string_new( NULL );
968    p->gstr2 = g_string_new( NULL );
969    p->text_renderer = gtk_cell_renderer_text_new( );
970    g_object_set( p->text_renderer, "xpad", 0, "ypad", 0, NULL );
971    p->progress_renderer = gtk_cell_renderer_progress_new(  );
972    p->icon_renderer = gtk_cell_renderer_pixbuf_new(  );
973    gtr_object_ref_sink( p->text_renderer );
974    gtr_object_ref_sink( p->progress_renderer );
975    gtr_object_ref_sink( p->icon_renderer );
976
977    p->bar_height = DEFAULT_BAR_HEIGHT;
978}
979
980GType
981torrent_cell_renderer_get_type( void )
982{
983    static GType type = 0;
984
985    if( !type )
986    {
987        static const GTypeInfo info =
988        {
989            sizeof( TorrentCellRendererClass ),
990            NULL,                                            /* base_init */
991            NULL,                                            /* base_finalize */
992            (GClassInitFunc)torrent_cell_renderer_class_init,
993            NULL,                                            /* class_finalize
994                                                               */
995            NULL,                                            /* class_data */
996            sizeof( TorrentCellRenderer ),
997            0,                                               /* n_preallocs */
998            (GInstanceInitFunc)torrent_cell_renderer_init,
999            NULL
1000        };
1001
1002        type = g_type_register_static( GTK_TYPE_CELL_RENDERER,
1003                                       "TorrentCellRenderer",
1004                                       &info, (GTypeFlags)0 );
1005    }
1006
1007    return type;
1008}
1009
1010GtkCellRenderer *
1011torrent_cell_renderer_new( void )
1012{
1013    return (GtkCellRenderer *) g_object_new( TORRENT_CELL_RENDERER_TYPE,
1014                                             NULL );
1015}
1016
Note: See TracBrowser for help on using the repository browser.