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

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

(trunk gtk) in torrent-cell-renderer and tr-core, use the _parent_class field generated by G_DEFINE_TYPE.

  • Property svn:keywords set to Date Rev Author Id
File size: 35.8 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 12659 2011-08-09 06:31:33Z 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
311struct TorrentCellRendererPrivate
312{
313    tr_torrent       * tor;
314    GtkCellRenderer  * text_renderer;
315    GtkCellRenderer  * progress_renderer;
316    GtkCellRenderer  * icon_renderer;
317    GString          * gstr1;
318    GString          * gstr2;
319    int bar_height;
320
321    /* Use this instead of tr_stat.pieceUploadSpeed so that the model can
322       control when the speed displays get updated. This is done to keep
323       the individual torrents' speeds and the status bar's overall speed
324       in sync even if they refresh at slightly different times */
325    double upload_speed_KBps;
326
327    /* @see upload_speed_Bps */
328    double download_speed_KBps;
329
330    gboolean compact;
331};
332
333/***
334****
335***/
336
337static GdkPixbuf*
338get_icon( const tr_torrent * tor, GtkIconSize icon_size, GtkWidget * for_widget )
339{
340    const char * mime_type;
341    const tr_info * info = tr_torrentInfo( tor );
342
343    if( info->fileCount == 0  )
344        mime_type = UNKNOWN_MIME_TYPE;
345    else if( info->fileCount > 1 )
346        mime_type = DIRECTORY_MIME_TYPE;
347    else if( strchr( info->files[0].name, '/' ) != NULL )
348        mime_type = DIRECTORY_MIME_TYPE;
349    else
350        mime_type = gtr_get_mime_type_from_filename( info->files[0].name );
351
352    return gtr_get_mime_type_icon( mime_type, icon_size, for_widget );
353}
354
355/***
356****
357***/
358
359static void
360gtr_cell_renderer_get_preferred_size( GtkCellRenderer  * renderer,
361                                      GtkWidget        * widget,
362                                      GtkRequisition   * minimum_size,
363                                      GtkRequisition   * natural_size )
364{
365#if GTK_CHECK_VERSION( 3,0,0 )
366
367    gtk_cell_renderer_get_preferred_size( renderer, widget, minimum_size, natural_size );
368
369#else
370
371    GtkRequisition r;
372    gtk_cell_renderer_get_size( renderer, widget, NULL, NULL, NULL, &r.width, &r.height );
373    if( minimum_size ) *minimum_size = r;
374    if( natural_size ) *natural_size = r;
375
376#endif
377}
378
379static void
380get_size_compact( TorrentCellRenderer * cell,
381                  GtkWidget           * widget,
382                  gint                * width,
383                  gint                * height )
384{
385    int xpad, ypad;
386    GtkRequisition icon_size;
387    GtkRequisition name_size;
388    GtkRequisition stat_size;
389    const char * name;
390    GdkPixbuf * icon;
391
392    struct TorrentCellRendererPrivate * p = cell->priv;
393    const tr_torrent * tor = p->tor;
394    const tr_stat * st = tr_torrentStatCached( (tr_torrent*)tor );
395    GString * gstr_stat = p->gstr1;
396
397    icon = get_icon( tor, COMPACT_ICON_SIZE, widget );
398    name = tr_torrentName( tor );
399    g_string_truncate( gstr_stat, 0 );
400    getShortStatusString( gstr_stat, tor, st, p->upload_speed_KBps, p->download_speed_KBps );
401    gtk_cell_renderer_get_padding( GTK_CELL_RENDERER( cell ), &xpad, &ypad );
402
403    /* get the idealized cell dimensions */
404    g_object_set( p->icon_renderer, "pixbuf", icon, NULL );
405    gtr_cell_renderer_get_preferred_size( p->icon_renderer, widget, NULL, &icon_size );
406    g_object_set( p->text_renderer, "text", name, "ellipsize", PANGO_ELLIPSIZE_NONE,  "scale", 1.0, NULL );
407    gtr_cell_renderer_get_preferred_size( p->text_renderer, widget, NULL, &name_size );
408    g_object_set( p->text_renderer, "text", gstr_stat->str, "scale", SMALL_SCALE, NULL );
409    gtr_cell_renderer_get_preferred_size( p->text_renderer, widget, NULL, &stat_size );
410
411    /**
412    *** LAYOUT
413    **/
414
415#define BAR_WIDTH 50
416    if( width != NULL )
417        *width = xpad * 2 + icon_size.width + GUI_PAD + name_size.width + GUI_PAD + BAR_WIDTH + GUI_PAD + stat_size.width;
418    if( height != NULL )
419        *height = ypad * 2 + MAX( name_size.height, p->bar_height );
420
421    /* cleanup */
422    g_object_unref( icon );
423}
424
425#define MAX3(a,b,c) MAX(a,MAX(b,c))
426
427static void
428get_size_full( TorrentCellRenderer * cell,
429               GtkWidget           * widget,
430               gint                * width,
431               gint                * height )
432{
433    int xpad, ypad;
434    GtkRequisition icon_size;
435    GtkRequisition name_size;
436    GtkRequisition stat_size;
437    GtkRequisition prog_size;
438    const char * name;
439    GdkPixbuf * icon;
440
441    struct TorrentCellRendererPrivate * p = cell->priv;
442    const tr_torrent * tor = p->tor;
443    const tr_stat * st = tr_torrentStatCached( (tr_torrent*)tor );
444    const tr_info * inf = tr_torrentInfo( tor );
445    GString * gstr_prog = p->gstr1;
446    GString * gstr_stat = p->gstr2;
447
448    icon = get_icon( tor, FULL_ICON_SIZE, widget );
449    name = tr_torrentName( tor );
450    g_string_truncate( gstr_stat, 0 );
451    getStatusString( gstr_stat, tor, st, p->upload_speed_KBps, p->download_speed_KBps );
452    g_string_truncate( gstr_prog, 0 );
453    getProgressString( gstr_prog, tor, inf, st );
454    gtk_cell_renderer_get_padding( GTK_CELL_RENDERER( cell ), &xpad, &ypad );
455
456    /* get the idealized cell dimensions */
457    g_object_set( p->icon_renderer, "pixbuf", icon, NULL );
458    gtr_cell_renderer_get_preferred_size( p->icon_renderer, widget, NULL, &icon_size );
459    g_object_set( p->text_renderer, "text", name, "weight", PANGO_WEIGHT_BOLD, "scale", 1.0, "ellipsize", PANGO_ELLIPSIZE_NONE, NULL );
460    gtr_cell_renderer_get_preferred_size( p->text_renderer, widget, NULL, &name_size );
461    g_object_set( p->text_renderer, "text", gstr_prog->str, "weight", PANGO_WEIGHT_NORMAL, "scale", SMALL_SCALE, NULL );
462    gtr_cell_renderer_get_preferred_size( p->text_renderer, widget, NULL, &prog_size );
463    g_object_set( p->text_renderer, "text", gstr_stat->str, NULL );
464    gtr_cell_renderer_get_preferred_size( p->text_renderer, widget, NULL, &stat_size );
465
466    /**
467    *** LAYOUT
468    **/
469
470    if( width != NULL )
471        *width = xpad * 2 + icon_size.width + GUI_PAD + MAX3( name_size.width, prog_size.width, stat_size.width );
472    if( height != NULL )
473        *height = ypad * 2 + name_size.height + prog_size.height + GUI_PAD_SMALL + p->bar_height + GUI_PAD_SMALL + stat_size.height;
474
475    /* cleanup */
476    g_object_unref( icon );
477}
478
479
480static void
481torrent_cell_renderer_get_size( GtkCellRenderer     * cell,
482                                GtkWidget           * widget,
483#if GTK_CHECK_VERSION( 3,0,0 )
484                                const GdkRectangle  * cell_area,
485#else
486                                GdkRectangle        * cell_area,
487#endif
488                                gint                * x_offset,
489                                gint                * y_offset,
490                                gint                * width,
491                                gint                * height )
492{
493    TorrentCellRenderer * self = TORRENT_CELL_RENDERER( cell );
494
495    if( self && self->priv->tor )
496    {
497        int w, h;
498        struct TorrentCellRendererPrivate * p = self->priv;
499
500        if( p->compact )
501            get_size_compact( TORRENT_CELL_RENDERER( cell ), widget, &w, &h );
502        else
503            get_size_full( TORRENT_CELL_RENDERER( cell ), widget, &w, &h );
504
505        if( width )
506            *width = w;
507
508        if( height )
509            *height = h;
510
511        if( x_offset )
512            *x_offset = cell_area ? cell_area->x : 0;
513
514        if( y_offset ) {
515            int xpad, ypad;
516            gtk_cell_renderer_get_padding( cell, &xpad, &ypad );
517            *y_offset = cell_area ? (int)((cell_area->height - (ypad*2 +h)) / 2.0) : 0;
518        }
519    }
520}
521
522#if GTK_CHECK_VERSION( 3,0,0 )
523    typedef GdkRGBA GtrColor;
524    #define FOREGROUND_COLOR_KEY "foreground-rgba"
525#else
526    typedef GdkColor GtrColor;
527    #define FOREGROUND_COLOR_KEY "foreground-gdk"
528#endif
529
530static void
531get_text_color( GtkWidget * w, const tr_stat * st, GtrColor * setme )
532{
533#if GTK_CHECK_VERSION( 3,0,0 )
534
535    static const GdkRGBA red = { 1.0, 0, 0, 0 };
536    if( st->error )
537        *setme = red;
538    else if( st->activity == TR_STATUS_STOPPED )
539        gtk_style_context_get_color( gtk_widget_get_style_context( w ), GTK_STATE_FLAG_INSENSITIVE, setme );
540    else
541        gtk_style_context_get_color( gtk_widget_get_style_context( w ), GTK_STATE_FLAG_NORMAL, setme );
542
543#else
544
545    static const GdkColor red = { 0, 65535, 0, 0 };
546    if( st->error )
547        *setme = red;
548    else if( st->activity == TR_STATUS_STOPPED )
549        *setme = gtk_widget_get_style(w)->text[GTK_STATE_INSENSITIVE];
550    else
551        *setme = gtk_widget_get_style(w)->text[GTK_STATE_NORMAL];
552
553#endif
554}
555
556
557static double
558get_percent_done( const tr_torrent * tor, const tr_stat * st, bool * seed )
559{
560    double d;
561
562    if( ( st->activity == TR_STATUS_SEED ) && tr_torrentGetSeedRatio( tor, &d ) )
563    {
564        *seed = true;
565        d = MAX( 0.0, st->seedRatioPercentDone );
566    }
567    else
568    {
569        *seed = false;
570        d = MAX( 0.0, st->percentDone );
571    }
572
573    return d;
574}
575
576#if GTK_CHECK_VERSION( 3,0,0 )
577    typedef cairo_t GtrDrawable;
578#else
579    typedef GdkDrawable GtrDrawable;
580#endif
581
582static void
583gtr_cell_renderer_render( GtkCellRenderer       * renderer,
584                          GtrDrawable           * drawable,
585                          GtkWidget             * widget,
586                          const GdkRectangle    * area,
587                          GtkCellRendererState    flags)
588{
589#if GTK_CHECK_VERSION( 3,0,0 )
590    gtk_cell_renderer_render( renderer, drawable, widget, area, area, flags );
591#else
592    gtk_cell_renderer_render( renderer, drawable, widget, area, area, area, flags );
593#endif
594}
595
596static void
597render_compact( TorrentCellRenderer   * cell,
598                GtrDrawable           * window,
599                GtkWidget             * widget,
600                const GdkRectangle    * background_area,
601                const GdkRectangle    * cell_area UNUSED,
602                GtkCellRendererState    flags )
603{
604    int xpad, ypad;
605    GtkRequisition size;
606    GdkRectangle icon_area;
607    GdkRectangle name_area;
608    GdkRectangle stat_area;
609    GdkRectangle prog_area;
610    GdkRectangle fill_area;
611    const char * name;
612    GdkPixbuf * icon;
613    GtrColor text_color;
614    bool seed;
615
616    struct TorrentCellRendererPrivate * p = cell->priv;
617    const tr_torrent * tor = p->tor;
618    const tr_stat * st = tr_torrentStatCached( (tr_torrent*)tor );
619    const gboolean active = ( st->activity != TR_STATUS_STOPPED ) && ( st->activity != TR_STATUS_DOWNLOAD_WAIT ) && ( st->activity != TR_STATUS_SEED_WAIT );
620    const double percentDone = get_percent_done( tor, st, &seed );
621    const gboolean sensitive = active || st->error;
622    GString * gstr_stat = p->gstr1;
623
624    icon = get_icon( tor, COMPACT_ICON_SIZE, widget );
625    name = tr_torrentName( tor );
626    g_string_truncate( gstr_stat, 0 );
627    getShortStatusString( gstr_stat, tor, st, p->upload_speed_KBps, p->download_speed_KBps );
628    gtk_cell_renderer_get_padding( GTK_CELL_RENDERER( cell ), &xpad, &ypad );
629    get_text_color( widget, st, &text_color );
630
631    fill_area = *background_area;
632    fill_area.x += xpad;
633    fill_area.y += ypad;
634    fill_area.width -= xpad * 2;
635    fill_area.height -= ypad * 2;
636    icon_area = name_area = stat_area = prog_area = fill_area;
637
638    g_object_set( p->icon_renderer, "pixbuf", icon, NULL );
639    gtr_cell_renderer_get_preferred_size( p->icon_renderer, widget, NULL, &size );
640    icon_area.width = size.width;
641    g_object_set( p->text_renderer, "text", name, "ellipsize", PANGO_ELLIPSIZE_NONE, "scale", 1.0, NULL );
642    gtr_cell_renderer_get_preferred_size( p->text_renderer, widget, NULL, &size );
643    name_area.width = size.width;
644    g_object_set( p->text_renderer, "text", gstr_stat->str, "scale", SMALL_SCALE, NULL );
645    gtr_cell_renderer_get_preferred_size( p->text_renderer, widget, NULL, &size );
646    stat_area.width = size.width;
647
648    icon_area.x = fill_area.x;
649    prog_area.x = fill_area.x + fill_area.width - BAR_WIDTH;
650    prog_area.width = BAR_WIDTH;
651    stat_area.x = prog_area.x - GUI_PAD - stat_area.width;
652    name_area.x = icon_area.x + icon_area.width + GUI_PAD;
653    name_area.y = fill_area.y;
654    name_area.width = stat_area.x - GUI_PAD - name_area.x;
655
656    /**
657    *** RENDER
658    **/
659
660    g_object_set( p->icon_renderer, "pixbuf", icon, "sensitive", sensitive, NULL );
661    gtr_cell_renderer_render( p->icon_renderer, window, widget, &icon_area, flags );
662    g_object_set( p->progress_renderer, "value", (int)(percentDone*100.0), "text", NULL,
663        "sensitive", sensitive,
664#if GTK_CHECK_VERSION( 3,0,0 )
665        "inverted", seed,
666#else
667        "orientation", (seed ? GTK_PROGRESS_RIGHT_TO_LEFT : GTK_PROGRESS_LEFT_TO_RIGHT),
668#endif
669        NULL );
670    gtr_cell_renderer_render( p->progress_renderer, window, widget, &prog_area, flags );
671    g_object_set( p->text_renderer, "text", gstr_stat->str, "scale", SMALL_SCALE, "ellipsize", PANGO_ELLIPSIZE_END, FOREGROUND_COLOR_KEY, &text_color, NULL );
672    gtr_cell_renderer_render( p->text_renderer, window, widget, &stat_area, flags );
673    g_object_set( p->text_renderer, "text", name, "scale", 1.0, FOREGROUND_COLOR_KEY, &text_color, NULL );
674    gtr_cell_renderer_render( p->text_renderer, window, widget, &name_area, flags );
675
676    /* cleanup */
677    g_object_unref( icon );
678}
679
680static void
681render_full( TorrentCellRenderer   * cell,
682             GtrDrawable           * window,
683             GtkWidget             * widget,
684             const GdkRectangle    * background_area,
685             const GdkRectangle    * cell_area UNUSED,
686             GtkCellRendererState    flags )
687{
688    int xpad, ypad;
689    GtkRequisition size;
690    GdkRectangle fill_area;
691    GdkRectangle icon_area;
692    GdkRectangle name_area;
693    GdkRectangle stat_area;
694    GdkRectangle prog_area;
695    GdkRectangle prct_area;
696    const char * name;
697    GdkPixbuf * icon;
698    GtrColor text_color;
699    bool seed;
700
701    struct TorrentCellRendererPrivate * p = cell->priv;
702    const tr_torrent * tor = p->tor;
703    const tr_stat * st = tr_torrentStatCached( (tr_torrent*)tor );
704    const tr_info * inf = tr_torrentInfo( tor );
705    const gboolean active = ( st->activity != TR_STATUS_STOPPED ) && ( st->activity != TR_STATUS_DOWNLOAD_WAIT ) && ( st->activity != TR_STATUS_SEED_WAIT );
706    const double percentDone = get_percent_done( tor, st, &seed );
707    const gboolean sensitive = active || st->error;
708    GString * gstr_prog = p->gstr1;
709    GString * gstr_stat = p->gstr2;
710
711    icon = get_icon( tor, FULL_ICON_SIZE, widget );
712    name = tr_torrentName( tor );
713    g_string_truncate( gstr_prog, 0 );
714    getProgressString( gstr_prog, tor, inf, st );
715    g_string_truncate( gstr_stat, 0 );
716    getStatusString( gstr_stat, tor, st, p->upload_speed_KBps, p->download_speed_KBps );
717    gtk_cell_renderer_get_padding( GTK_CELL_RENDERER( cell ), &xpad, &ypad );
718    get_text_color( widget, st, &text_color );
719
720    /* get the idealized cell dimensions */
721    g_object_set( p->icon_renderer, "pixbuf", icon, NULL );
722    gtr_cell_renderer_get_preferred_size( p->icon_renderer, widget, NULL, &size );
723    icon_area.width = size.width;
724    icon_area.height = size.height;
725    g_object_set( p->text_renderer, "text", name, "weight", PANGO_WEIGHT_BOLD, "ellipsize", PANGO_ELLIPSIZE_NONE, "scale", 1.0, NULL );
726    gtr_cell_renderer_get_preferred_size( p->text_renderer, widget, NULL, &size );
727    name_area.width = size.width;
728    name_area.height = size.height;
729    g_object_set( p->text_renderer, "text", gstr_prog->str, "weight", PANGO_WEIGHT_NORMAL, "scale", SMALL_SCALE, NULL );
730    gtr_cell_renderer_get_preferred_size( p->text_renderer, widget, NULL, &size );
731    prog_area.width = size.width;
732    prog_area.height = size.height;
733    g_object_set( p->text_renderer, "text", gstr_stat->str, NULL );
734    gtr_cell_renderer_get_preferred_size( p->text_renderer, widget, NULL, &size );
735    stat_area.width = size.width;
736    stat_area.height = size.height;
737
738    /**
739    *** LAYOUT
740    **/
741
742    fill_area = *background_area;
743    fill_area.x += xpad;
744    fill_area.y += ypad;
745    fill_area.width -= xpad * 2;
746    fill_area.height -= ypad * 2;
747
748    /* icon */
749    icon_area.x = fill_area.x;
750    icon_area.y = fill_area.y + ( fill_area.height - icon_area.height ) / 2;
751
752    /* name */
753    name_area.x = icon_area.x + icon_area.width + GUI_PAD;
754    name_area.y = fill_area.y;
755    name_area.width = fill_area.width - GUI_PAD - icon_area.width - GUI_PAD_SMALL;
756
757    /* prog */
758    prog_area.x = name_area.x;
759    prog_area.y = name_area.y + name_area.height;
760    prog_area.width = name_area.width;
761
762    /* progressbar */
763    prct_area.x = prog_area.x;
764    prct_area.y = prog_area.y + prog_area.height + GUI_PAD_SMALL;
765    prct_area.width = prog_area.width;
766    prct_area.height = p->bar_height;
767
768    /* status */
769    stat_area.x = prct_area.x;
770    stat_area.y = prct_area.y + prct_area.height + GUI_PAD_SMALL;
771    stat_area.width = prct_area.width;
772
773    /**
774    *** RENDER
775    **/
776
777    g_object_set( p->icon_renderer, "pixbuf", icon, "sensitive", sensitive, NULL );
778    gtr_cell_renderer_render( p->icon_renderer, window, widget, &icon_area, flags );
779    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 );
780    gtr_cell_renderer_render( p->text_renderer, window, widget, &name_area, flags );
781    g_object_set( p->text_renderer, "text", gstr_prog->str, "scale", SMALL_SCALE, "weight", PANGO_WEIGHT_NORMAL, NULL );
782    gtr_cell_renderer_render( p->text_renderer, window, widget, &prog_area, flags );
783    g_object_set( p->progress_renderer, "value", (int)(percentDone*100.0), "text", "", "sensitive", sensitive,
784#if GTK_CHECK_VERSION( 3,0,0 )
785        "inverted", seed,
786#else
787        "orientation", (seed ? GTK_PROGRESS_RIGHT_TO_LEFT : GTK_PROGRESS_LEFT_TO_RIGHT),
788#endif
789        NULL );
790    gtr_cell_renderer_render( p->progress_renderer, window, widget, &prct_area, flags );
791    g_object_set( p->text_renderer, "text", gstr_stat->str, FOREGROUND_COLOR_KEY, &text_color, NULL );
792    gtr_cell_renderer_render( p->text_renderer, window, widget, &stat_area, flags );
793
794    /* cleanup */
795    g_object_unref( icon );
796}
797
798static void
799torrent_cell_renderer_render( GtkCellRenderer       * cell,
800                              GtrDrawable           * window,
801                              GtkWidget             * widget,
802#if GTK_CHECK_VERSION( 3,0,0 )
803                              const GdkRectangle    * background_area,
804                              const GdkRectangle    * cell_area,
805#else
806                              GdkRectangle          * background_area,
807                              GdkRectangle          * cell_area,
808                              GdkRectangle          * expose_area UNUSED,
809#endif
810                              GtkCellRendererState    flags )
811{
812    TorrentCellRenderer * self = TORRENT_CELL_RENDERER( cell );
813
814#ifdef TEST_RTL
815    GtkTextDirection      real_dir = gtk_widget_get_direction( widget );
816    gtk_widget_set_direction( widget, GTK_TEXT_DIR_RTL );
817#endif
818
819    if( self && self->priv->tor )
820    {
821        struct TorrentCellRendererPrivate * p = self->priv;
822        if( p->compact )
823            render_compact( self, window, widget, background_area, cell_area, flags );
824        else
825            render_full( self, window, widget, background_area, cell_area, flags );
826    }
827
828#ifdef TEST_RTL
829    gtk_widget_set_direction( widget, real_dir );
830#endif
831}
832
833static void
834torrent_cell_renderer_set_property( GObject      * object,
835                                    guint          property_id,
836                                    const GValue * v,
837                                    GParamSpec   * pspec )
838{
839    TorrentCellRenderer * self = TORRENT_CELL_RENDERER( object );
840    struct TorrentCellRendererPrivate * p = self->priv;
841
842    switch( property_id )
843    {
844        case P_TORRENT:        p->tor                 = g_value_get_pointer( v ); break;
845        case P_UPLOAD_SPEED:   p->upload_speed_KBps   = g_value_get_double( v ); break;
846        case P_DOWNLOAD_SPEED: p->download_speed_KBps = g_value_get_double( v ); break;
847        case P_BAR_HEIGHT:     p->bar_height          = g_value_get_int( v ); break;
848        case P_COMPACT:        p->compact             = g_value_get_boolean( v ); break;
849        default: G_OBJECT_WARN_INVALID_PROPERTY_ID( object, property_id, pspec ); break;
850    }
851}
852
853static void
854torrent_cell_renderer_get_property( GObject     * object,
855                                    guint         property_id,
856                                    GValue      * v,
857                                    GParamSpec  * pspec )
858{
859    const TorrentCellRenderer * self = TORRENT_CELL_RENDERER( object );
860    struct TorrentCellRendererPrivate * p = self->priv;
861
862    switch( property_id )
863    {
864        case P_TORRENT:        g_value_set_pointer( v, p->tor ); break;
865        case P_UPLOAD_SPEED:   g_value_set_double( v, p->upload_speed_KBps ); break;
866        case P_DOWNLOAD_SPEED: g_value_set_double( v, p->download_speed_KBps ); break;
867        case P_BAR_HEIGHT:     g_value_set_int( v, p->bar_height ); break;
868        case P_COMPACT:        g_value_set_boolean( v, p->compact ); break;
869        default: G_OBJECT_WARN_INVALID_PROPERTY_ID( object, property_id, pspec ); break;
870    }
871}
872
873G_DEFINE_TYPE (TorrentCellRenderer, torrent_cell_renderer, GTK_TYPE_CELL_RENDERER)
874
875static void
876torrent_cell_renderer_dispose( GObject * o )
877{
878    TorrentCellRenderer * r = TORRENT_CELL_RENDERER( o );
879
880    if( r && r->priv )
881    {
882        g_string_free( r->priv->gstr1, TRUE );
883        g_string_free( r->priv->gstr2, TRUE );
884        g_object_unref( G_OBJECT( r->priv->text_renderer ) );
885        g_object_unref( G_OBJECT( r->priv->progress_renderer ) );
886        g_object_unref( G_OBJECT( r->priv->icon_renderer ) );
887        r->priv = NULL;
888    }
889
890    G_OBJECT_CLASS( torrent_cell_renderer_parent_class )->dispose( o );
891}
892
893static void
894torrent_cell_renderer_class_init( TorrentCellRendererClass * klass )
895{
896    GObjectClass *         gobject_class = G_OBJECT_CLASS( klass );
897    GtkCellRendererClass * cell_class = GTK_CELL_RENDERER_CLASS( klass );
898
899    g_type_class_add_private( klass,
900                             sizeof( struct TorrentCellRendererPrivate ) );
901
902    cell_class->render = torrent_cell_renderer_render;
903    cell_class->get_size = torrent_cell_renderer_get_size;
904    gobject_class->set_property = torrent_cell_renderer_set_property;
905    gobject_class->get_property = torrent_cell_renderer_get_property;
906    gobject_class->dispose = torrent_cell_renderer_dispose;
907
908    g_object_class_install_property( gobject_class, P_TORRENT,
909                                    g_param_spec_pointer( "torrent", NULL,
910                                                          "tr_torrent*",
911                                                          G_PARAM_READWRITE ) );
912
913    g_object_class_install_property( gobject_class, P_UPLOAD_SPEED,
914                                    g_param_spec_double( "piece-upload-speed", NULL,
915                                                         "tr_stat.pieceUploadSpeed_KBps",
916                                                         0, INT_MAX, 0,
917                                                         G_PARAM_READWRITE ) );
918
919    g_object_class_install_property( gobject_class, P_DOWNLOAD_SPEED,
920                                    g_param_spec_double( "piece-download-speed", NULL,
921                                                         "tr_stat.pieceDownloadSpeed_KBps",
922                                                         0, INT_MAX, 0,
923                                                         G_PARAM_READWRITE ) );
924
925    g_object_class_install_property( gobject_class, P_BAR_HEIGHT,
926                                    g_param_spec_int( "bar-height", NULL,
927                                                      "Bar Height",
928                                                      1, INT_MAX,
929                                                      DEFAULT_BAR_HEIGHT,
930                                                      G_PARAM_READWRITE ) );
931
932    g_object_class_install_property( gobject_class, P_COMPACT,
933                                    g_param_spec_boolean( "compact", NULL,
934                                                          "Compact Mode",
935                                                          FALSE,
936                                                          G_PARAM_READWRITE ) );
937}
938
939static void
940torrent_cell_renderer_init( TorrentCellRenderer * self )
941{
942    struct TorrentCellRendererPrivate * p;
943
944    p = self->priv = G_TYPE_INSTANCE_GET_PRIVATE(
945            self,
946            TORRENT_CELL_RENDERER_TYPE,
947            struct
948            TorrentCellRendererPrivate );
949
950    p->tor = NULL;
951    p->gstr1 = g_string_new( NULL );
952    p->gstr2 = g_string_new( NULL );
953    p->text_renderer = gtk_cell_renderer_text_new( );
954    g_object_set( p->text_renderer, "xpad", 0, "ypad", 0, NULL );
955    p->progress_renderer = gtk_cell_renderer_progress_new(  );
956    p->icon_renderer = gtk_cell_renderer_pixbuf_new(  );
957    g_object_ref_sink( p->text_renderer );
958    g_object_ref_sink( p->progress_renderer );
959    g_object_ref_sink( p->icon_renderer );
960
961    p->bar_height = DEFAULT_BAR_HEIGHT;
962}
963
964
965GtkCellRenderer *
966torrent_cell_renderer_new( void )
967{
968    return (GtkCellRenderer *) g_object_new( TORRENT_CELL_RENDERER_TYPE,
969                                             NULL );
970}
971
Note: See TracBrowser for help on using the repository browser.