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

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

(trunk gtk) use ngettext() instead of gtr_dngettext() to address https://bugs.launchpad.net/ubuntu/+source/transmission/+bug/760761

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