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

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

queuing: fix false "Error" message reported by rb07

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