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

Last change on this file since 11589 was 11589, checked in by charles, 11 years ago

(trunk gtk) #3847 "GTK+ 3 transition: Use accessor functions instead direct access" -- don't directly access GtkCellRenderer?.xpad, GtkCellRenderer?.ypad, or GtkWidget?.window.

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