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

Last change on this file since 10931 was 10931, checked in by charles, 12 years ago

(trunk) #3045 "make libtransmission's API byte-oriented instead of KiB-oriented." -- implemented. This is a largish commit and will break the mac build for a little while.

  • Property svn:keywords set to Date Rev Author Id
File size: 34.7 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 10931 2010-07-03 00:25:22Z 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                        int                 uploadSpeed_Bps,
158                        int                 downloadSpeed_Bps,
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_strlspeed( downStr, downloadSpeed_Bps, sizeof( downStr ) );
169    if( haveUp )
170        tr_strlspeed( upStr, uploadSpeed_Bps, sizeof( upStr ) );
171
172    if( haveDown && haveUp )
173        /* 1==down speed, 2==down arrow, 3==up speed, 4==down arrow */
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( tr_torrentHasMetadata( tor ) )
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                      int                 uploadSpeed_Bps,
198                      int                 downloadSpeed_Bps )
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_Bps, downloadSpeed_Bps, 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 int           uploadSpeed_Bps,
247                 const int           downloadSpeed_Bps )
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_Bps, downloadSpeed_Bps );
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                    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                    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                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_Bps, downloadSpeed_Bps, 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    int upload_speed_Bps;
340
341    /* @see upload_speed_Bps */
342    int download_speed_Bps;
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 = get_mime_type_from_filename( info->files[0].name );
365
366    return 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    GdkRectangle icon_area;
387    GdkRectangle name_area;
388    GdkRectangle stat_area;
389    const char * name;
390    char * status;
391    GdkPixbuf * icon;
392    GtkCellRenderer * text_renderer;
393
394    struct TorrentCellRendererPrivate * p = cell->priv;
395    const tr_torrent * tor = p->tor;
396    const tr_stat * st = tr_torrentStatCached( (tr_torrent*)tor );
397
398    icon = get_icon( tor, COMPACT_ICON_SIZE, widget );
399    name = tr_torrentInfo( tor )->name;
400    status = getShortStatusString( tor, st, p->upload_speed_Bps, p->download_speed_Bps );
401
402    /* get the idealized cell dimensions */
403    g_object_set( p->icon_renderer, "pixbuf", icon, NULL );
404    gtk_cell_renderer_get_size( p->icon_renderer, widget, NULL, NULL, NULL, &w, &h );
405    icon_area.width = w;
406    icon_area.height = h;
407    text_renderer = get_text_renderer( st, cell );
408    g_object_set( text_renderer, "text", name, "ellipsize", PANGO_ELLIPSIZE_NONE,  "scale", 1.0, NULL );
409    gtk_cell_renderer_get_size( text_renderer, widget, NULL, NULL, NULL, &w, &h );
410    name_area.width = w;
411    name_area.height = h;
412    g_object_set( text_renderer, "text", status, "scale", SMALL_SCALE, NULL );
413    gtk_cell_renderer_get_size( text_renderer, widget, NULL, NULL, NULL, &w, &h );
414    stat_area.width = w;
415    stat_area.height = h;
416
417    /**
418    *** LAYOUT
419    **/
420
421#define BAR_WIDTH 50
422    if( width != NULL )
423        *width = cell->parent.xpad * 2 + icon_area.width + GUI_PAD + name_area.width + GUI_PAD + BAR_WIDTH + GUI_PAD + stat_area.width;
424    if( height != NULL )
425        *height = cell->parent.ypad * 2 + MAX( name_area.height, p->bar_height );
426
427    /* cleanup */
428    g_free( status );
429    g_object_unref( icon );
430}
431
432#define MAX3(a,b,c) MAX(a,MAX(b,c))
433
434static void
435get_size_full( TorrentCellRenderer * cell,
436               GtkWidget           * widget,
437               gint                * width,
438               gint                * height )
439{
440    int w, h;
441    GdkRectangle icon_area;
442    GdkRectangle name_area;
443    GdkRectangle stat_area;
444    GdkRectangle prog_area;
445    const char * name;
446    char * status;
447    char * progress;
448    GdkPixbuf * icon;
449    GtkCellRenderer * text_renderer;
450
451    struct TorrentCellRendererPrivate * p = cell->priv;
452    const tr_torrent * tor = p->tor;
453    const tr_stat * st = tr_torrentStatCached( (tr_torrent*)tor );
454    const tr_info * inf = tr_torrentInfo( tor );
455
456    icon = get_icon( tor, FULL_ICON_SIZE, widget );
457    name = inf->name;
458    status = getStatusString( tor, st, p->upload_speed_Bps, p->download_speed_Bps );
459    progress = getProgressString( tor, inf, st );
460
461    /* get the idealized cell dimensions */
462    g_object_set( p->icon_renderer, "pixbuf", icon, NULL );
463    gtk_cell_renderer_get_size( p->icon_renderer, widget, NULL, NULL, NULL, &w, &h );
464    icon_area.width = w;
465    icon_area.height = h;
466    text_renderer = get_text_renderer( st, cell );
467    g_object_set( text_renderer, "text", name, "weight", PANGO_WEIGHT_BOLD, "scale", 1.0, "ellipsize", PANGO_ELLIPSIZE_NONE, NULL );
468    gtk_cell_renderer_get_size( text_renderer, widget, NULL, NULL, NULL, &w, &h );
469    name_area.width = w;
470    name_area.height = h;
471    g_object_set( text_renderer, "text", progress, "weight", PANGO_WEIGHT_NORMAL, "scale", SMALL_SCALE, NULL );
472    gtk_cell_renderer_get_size( text_renderer, widget, NULL, NULL, NULL, &w, &h );
473    prog_area.width = w;
474    prog_area.height = h;
475    g_object_set( text_renderer, "text", status, NULL );
476    gtk_cell_renderer_get_size( text_renderer, widget, NULL, NULL, NULL, &w, &h );
477    stat_area.width = w;
478    stat_area.height = h;
479
480    /**
481    *** LAYOUT
482    **/
483
484    if( width != NULL )
485        *width = cell->parent.xpad * 2 + icon_area.width + GUI_PAD + MAX3( name_area.width, prog_area.width, stat_area.width );
486    if( height != NULL )
487        *height = cell->parent.ypad * 2 + name_area.height + prog_area.height + GUI_PAD_SMALL + p->bar_height + GUI_PAD_SMALL + stat_area.height;
488
489    /* cleanup */
490    g_free( status );
491    g_free( progress );
492    g_object_unref( icon );
493}
494
495
496static void
497torrent_cell_renderer_get_size( GtkCellRenderer  * cell,
498                                GtkWidget        * widget,
499                                GdkRectangle     * cell_area,
500                                gint             * x_offset,
501                                gint             * y_offset,
502                                gint             * width,
503                                gint             * height )
504{
505    TorrentCellRenderer * self = TORRENT_CELL_RENDERER( cell );
506
507    if( self && self->priv->tor )
508    {
509        struct TorrentCellRendererPrivate * p = self->priv;
510        int w, h;
511
512        if( p->compact )
513            get_size_compact( TORRENT_CELL_RENDERER( cell ), widget, &w, &h );
514        else
515            get_size_full( TORRENT_CELL_RENDERER( cell ), widget, &w, &h );
516
517        if( width )
518            *width = w;
519
520        if( height )
521            *height = h;
522
523        if( cell_area ) {
524            if( x_offset ) *x_offset = 0;
525            if( y_offset ) {
526                *y_offset = 0.5 * ( cell_area->height - ( h + ( 2 * cell->ypad ) ) );
527                *y_offset = MAX( *y_offset, 0 );
528            }
529        }
530    }
531}
532
533static void
534render_compact( TorrentCellRenderer   * cell,
535                GdkDrawable           * window,
536                GtkWidget             * widget,
537                GdkRectangle          * background_area,
538                GdkRectangle          * cell_area UNUSED,
539                GdkRectangle          * expose_area UNUSED,
540                GtkCellRendererState    flags )
541{
542    int w, h;
543    GdkRectangle icon_area;
544    GdkRectangle name_area;
545    GdkRectangle stat_area;
546    GdkRectangle prog_area;
547    GdkRectangle fill_area;
548    const char * name;
549    char * status;
550    GdkPixbuf * icon;
551    GtkCellRenderer * text_renderer;
552
553    struct TorrentCellRendererPrivate * p = cell->priv;
554    const tr_torrent * tor = p->tor;
555    const tr_stat * st = tr_torrentStatCached( (tr_torrent*)tor );
556    const gboolean active = st->activity != TR_STATUS_STOPPED;
557    const double percentDone = MAX( 0.0, st->percentDone );
558    const gboolean sensitive = active || st->error;
559
560    icon = get_icon( tor, COMPACT_ICON_SIZE, widget );
561    name = tr_torrentInfo( tor )->name;
562    status = getShortStatusString( tor, st, p->upload_speed_Bps, p->download_speed_Bps );
563
564    /* get the cell dimensions */
565    g_object_set( p->icon_renderer, "pixbuf", icon, NULL );
566    gtk_cell_renderer_get_size( p->icon_renderer, widget, NULL, NULL, NULL, &w, &h );
567    icon_area.width = w;
568    icon_area.height = h;
569    text_renderer = get_text_renderer( st, cell );
570    g_object_set( text_renderer, "text", name, "ellipsize", PANGO_ELLIPSIZE_NONE, "scale", 1.0, NULL );
571    gtk_cell_renderer_get_size( text_renderer, widget, NULL, NULL, NULL, &w, &h );
572    name_area.width = w;
573    name_area.height = h;
574    g_object_set( text_renderer, "text", status, "scale", SMALL_SCALE, NULL );
575    gtk_cell_renderer_get_size( text_renderer, widget, NULL, NULL, NULL, &w, &h );
576    stat_area.width = w;
577    stat_area.height = h;
578    prog_area.height = p->bar_height;
579
580    h = 1;
581    h = MAX( h, stat_area.height );
582    h = MAX( h, name_area.height );
583    h = MAX( h, icon_area.height );
584    h = MAX( h, prog_area.height );
585
586    /**
587    *** LAYOUT
588    **/
589
590    fill_area = *background_area;
591    fill_area.x += cell->parent.xpad;
592    fill_area.y += cell->parent.ypad;
593    fill_area.width -= cell->parent.xpad * 2;
594    fill_area.height -= cell->parent.ypad * 2;
595
596    /* icon */
597    icon_area.x = fill_area.x;
598    icon_area.y = fill_area.y;
599    icon_area.height = h;
600
601    /* progressbar */
602    prog_area.x = fill_area.x + fill_area.width - BAR_WIDTH;
603    prog_area.y = fill_area.y;
604    prog_area.width = BAR_WIDTH;
605    prog_area.height = h;
606
607    /* short status (right justified) */
608    stat_area.x = prog_area.x - GUI_PAD - stat_area.width;
609    stat_area.y = fill_area.y + ( h - stat_area.height ) / 2;
610
611    /* name */
612    name_area.x = icon_area.x + icon_area.width + GUI_PAD;
613    name_area.y = fill_area.y + ( h - name_area.height ) / 2;
614    name_area.width = stat_area.x - GUI_PAD - name_area.x;
615
616    /**
617    *** RENDER
618    **/
619
620    g_object_set( p->icon_renderer, "pixbuf", icon, "sensitive", sensitive, NULL );
621    gtk_cell_renderer_render( p->icon_renderer, window, widget, &icon_area, &icon_area, &icon_area, flags );
622    g_object_set( p->progress_renderer, "value", (int)(percentDone*100.0), "text", NULL, "sensitive", sensitive, NULL );
623    gtk_cell_renderer_render( p->progress_renderer, window, widget, &prog_area, &prog_area, &prog_area, flags );
624    g_object_set( text_renderer, "text", status, "scale", SMALL_SCALE, "sensitive", sensitive, "ellipsize", PANGO_ELLIPSIZE_END, NULL );
625    gtk_cell_renderer_render( text_renderer, window, widget, &stat_area, &stat_area, &stat_area, flags );
626    g_object_set( text_renderer, "text", name, "scale", 1.0, NULL );
627    gtk_cell_renderer_render( text_renderer, window, widget, &name_area, &name_area, &name_area, flags );
628
629    /* cleanup */
630    g_free( status );
631    g_object_unref( icon );
632}
633
634static void
635render_full( TorrentCellRenderer   * cell,
636             GdkDrawable           * window,
637             GtkWidget             * widget,
638             GdkRectangle          * background_area,
639             GdkRectangle          * cell_area UNUSED,
640             GdkRectangle          * expose_area UNUSED,
641             GtkCellRendererState    flags )
642{
643    int w, h;
644    GdkRectangle fill_area;
645    GdkRectangle icon_area;
646    GdkRectangle name_area;
647    GdkRectangle stat_area;
648    GdkRectangle prog_area;
649    GdkRectangle prct_area;
650    const char * name;
651    char * status;
652    char * progress;
653    GdkPixbuf * icon;
654    GtkCellRenderer * text_renderer;
655
656    struct TorrentCellRendererPrivate * p = cell->priv;
657    const tr_torrent * tor = p->tor;
658    const tr_stat * st = tr_torrentStatCached( (tr_torrent*)tor );
659    const tr_info * inf = tr_torrentInfo( tor );
660    const gboolean active = st->activity != TR_STATUS_STOPPED;
661    const double percentDone = MAX( 0.0, st->percentDone );
662    const gboolean sensitive = active || st->error;
663
664    icon = get_icon( tor, FULL_ICON_SIZE, widget );
665    name = inf->name;
666    status = getStatusString( tor, st, p->upload_speed_Bps, p->download_speed_Bps );
667    progress = getProgressString( tor, inf, st );
668
669    /* get the idealized cell dimensions */
670    g_object_set( p->icon_renderer, "pixbuf", icon, NULL );
671    gtk_cell_renderer_get_size( p->icon_renderer, widget, NULL, NULL, NULL, &w, &h );
672    icon_area.width = w;
673    icon_area.height = h;
674    text_renderer = get_text_renderer( st, cell );
675    g_object_set( text_renderer, "text", name, "weight", PANGO_WEIGHT_BOLD, "ellipsize", PANGO_ELLIPSIZE_NONE, "scale", 1.0, NULL );
676    gtk_cell_renderer_get_size( text_renderer, widget, NULL, NULL, NULL, &w, &h );
677    name_area.width = w;
678    name_area.height = h;
679    g_object_set( text_renderer, "text", progress, "weight", PANGO_WEIGHT_NORMAL, "scale", SMALL_SCALE, NULL );
680    gtk_cell_renderer_get_size( text_renderer, widget, NULL, NULL, NULL, &w, &h );
681    prog_area.width = w;
682    prog_area.height = h;
683    g_object_set( text_renderer, "text", status, NULL );
684    gtk_cell_renderer_get_size( text_renderer, widget, NULL, NULL, NULL, &w, &h );
685    stat_area.width = w;
686    stat_area.height = h;
687
688    /**
689    *** LAYOUT
690    **/
691
692    fill_area = *background_area;
693    fill_area.x += cell->parent.xpad;
694    fill_area.y += cell->parent.ypad;
695    fill_area.width -= cell->parent.xpad * 2;
696    fill_area.height -= cell->parent.ypad * 2;
697
698    /* icon */
699    icon_area.x = fill_area.x;
700    icon_area.y = fill_area.y + ( fill_area.height - icon_area.height ) / 2;
701
702    /* name */
703    name_area.x = icon_area.x + icon_area.width + GUI_PAD;
704    name_area.y = fill_area.y;
705    name_area.width = fill_area.width - GUI_PAD - icon_area.width - GUI_PAD_SMALL;
706
707    /* prog */
708    prog_area.x = name_area.x;
709    prog_area.y = name_area.y + name_area.height;
710    prog_area.width = name_area.width;
711
712    /* progressbar */
713    prct_area.x = prog_area.x;
714    prct_area.y = prog_area.y + prog_area.height + GUI_PAD_SMALL;
715    prct_area.width = prog_area.width;
716    prct_area.height = p->bar_height;
717
718    /* status */
719    stat_area.x = prct_area.x;
720    stat_area.y = prct_area.y + prct_area.height + GUI_PAD_SMALL;
721    stat_area.width = prct_area.width;
722
723    /**
724    *** RENDER
725    **/
726
727    g_object_set( p->icon_renderer, "pixbuf", icon, "sensitive", sensitive, NULL );
728    gtk_cell_renderer_render( p->icon_renderer, window, widget, &icon_area, &icon_area, &icon_area, flags );
729    g_object_set( text_renderer, "text", name, "scale", 1.0, "sensitive", sensitive, "ellipsize", PANGO_ELLIPSIZE_END, "weight", PANGO_WEIGHT_BOLD, NULL );
730    gtk_cell_renderer_render( text_renderer, window, widget, &name_area, &name_area, &name_area, flags );
731    g_object_set( text_renderer, "text", progress, "scale", SMALL_SCALE, "weight", PANGO_WEIGHT_NORMAL, NULL );
732    gtk_cell_renderer_render( text_renderer, window, widget, &prog_area, &prog_area, &prog_area, flags );
733    g_object_set( p->progress_renderer, "value", (int)(percentDone*100.0), "text", "", "sensitive", sensitive, NULL );
734    gtk_cell_renderer_render( p->progress_renderer, window, widget, &prct_area, &prct_area, &prct_area, flags );
735    g_object_set( text_renderer, "text", status, NULL );
736    gtk_cell_renderer_render( text_renderer, window, widget, &stat_area, &stat_area, &stat_area, flags );
737
738    /* cleanup */
739    g_free( status );
740    g_free( progress );
741    g_object_unref( icon );
742}
743
744static void
745torrent_cell_renderer_render( GtkCellRenderer       * cell,
746                              GdkDrawable           * window,
747                              GtkWidget             * widget,
748                              GdkRectangle          * background_area,
749                              GdkRectangle          * cell_area,
750                              GdkRectangle          * expose_area,
751                              GtkCellRendererState    flags )
752{
753    TorrentCellRenderer * self = TORRENT_CELL_RENDERER( cell );
754
755#ifdef TEST_RTL
756    GtkTextDirection      real_dir = gtk_widget_get_direction( widget );
757    gtk_widget_set_direction( widget, GTK_TEXT_DIR_RTL );
758#endif
759
760    if( self && self->priv->tor )
761    {
762        struct TorrentCellRendererPrivate * p = self->priv;
763        if( p->compact )
764            render_compact( self, window, widget, background_area, cell_area, expose_area, flags );
765        else
766            render_full( self, window, widget, background_area, cell_area, expose_area, flags );
767    }
768
769#ifdef TEST_RTL
770    gtk_widget_set_direction( widget, real_dir );
771#endif
772}
773
774static void
775torrent_cell_renderer_set_property( GObject      * object,
776                                    guint          property_id,
777                                    const GValue * v,
778                                    GParamSpec   * pspec )
779{
780    TorrentCellRenderer * self = TORRENT_CELL_RENDERER( object );
781    struct TorrentCellRendererPrivate * p = self->priv;
782
783    switch( property_id )
784    {
785        case P_TORRENT:        p->tor                = g_value_get_pointer( v ); break;
786        case P_UPLOAD_SPEED:   p->upload_speed_Bps   = g_value_get_int( v ); break;
787        case P_DOWNLOAD_SPEED: p->download_speed_Bps = g_value_get_int( v ); break;
788        case P_BAR_HEIGHT:     p->bar_height         = g_value_get_int( v ); break;
789        case P_COMPACT:        p->compact            = g_value_get_boolean( v ); break;
790        default: G_OBJECT_WARN_INVALID_PROPERTY_ID( object, property_id, pspec ); break;
791    }
792}
793
794static void
795torrent_cell_renderer_get_property( GObject     * object,
796                                    guint         property_id,
797                                    GValue      * v,
798                                    GParamSpec  * pspec )
799{
800    const TorrentCellRenderer * self = TORRENT_CELL_RENDERER( object );
801    struct TorrentCellRendererPrivate * p = self->priv;
802
803    switch( property_id )
804    {
805        case P_TORRENT:        g_value_set_pointer( v, p->tor ); break;
806        case P_UPLOAD_SPEED:   g_value_set_int( v, p->upload_speed_Bps ); break;
807        case P_DOWNLOAD_SPEED: g_value_set_int( v, p->download_speed_Bps ); break;
808        case P_BAR_HEIGHT:     g_value_set_int( v, p->bar_height ); break;
809        case P_COMPACT:        g_value_set_boolean( v, p->compact ); break;
810        default: G_OBJECT_WARN_INVALID_PROPERTY_ID( object, property_id, pspec ); break;
811    }
812}
813
814static void
815torrent_cell_renderer_dispose( GObject * o )
816{
817    TorrentCellRenderer * r = TORRENT_CELL_RENDERER( o );
818    GObjectClass *        parent;
819
820    if( r && r->priv )
821    {
822        g_object_unref( G_OBJECT( r->priv->text_renderer ) );
823        g_object_unref( G_OBJECT( r->priv->text_renderer_err ) );
824        g_object_unref( G_OBJECT( r->priv->progress_renderer ) );
825        g_object_unref( G_OBJECT( r->priv->icon_renderer ) );
826        r->priv = NULL;
827    }
828
829    parent = g_type_class_peek( g_type_parent( TORRENT_CELL_RENDERER_TYPE ) );
830    parent->dispose( o );
831}
832
833static void
834torrent_cell_renderer_class_init( TorrentCellRendererClass * klass )
835{
836    GObjectClass *         gobject_class = G_OBJECT_CLASS( klass );
837    GtkCellRendererClass * cell_class = GTK_CELL_RENDERER_CLASS( klass );
838
839    g_type_class_add_private( klass,
840                             sizeof( struct TorrentCellRendererPrivate ) );
841
842    parent_class = (GtkCellRendererClass*) g_type_class_peek_parent( klass );
843
844    cell_class->render = torrent_cell_renderer_render;
845    cell_class->get_size = torrent_cell_renderer_get_size;
846    gobject_class->set_property = torrent_cell_renderer_set_property;
847    gobject_class->get_property = torrent_cell_renderer_get_property;
848    gobject_class->dispose = torrent_cell_renderer_dispose;
849
850    g_object_class_install_property( gobject_class, P_TORRENT,
851                                    g_param_spec_pointer( "torrent", NULL,
852                                                          "tr_torrent*",
853                                                          G_PARAM_READWRITE ) );
854
855    g_object_class_install_property( gobject_class, P_UPLOAD_SPEED,
856                                    g_param_spec_int( "piece-upload-speed", NULL,
857                                                      "tr_stat.pieceUploadSpeed_Bps",
858                                                      0, INT_MAX, 0,
859                                                      G_PARAM_READWRITE ) );
860
861    g_object_class_install_property( gobject_class, P_DOWNLOAD_SPEED,
862                                    g_param_spec_int( "piece-download-speed", NULL,
863                                                      "tr_stat.pieceDownloadSpeed_Bps",
864                                                      0, INT_MAX, 0,
865                                                      G_PARAM_READWRITE ) );
866
867    g_object_class_install_property( gobject_class, P_BAR_HEIGHT,
868                                    g_param_spec_int( "bar-height", NULL,
869                                                      "Bar Height",
870                                                      1, INT_MAX,
871                                                      DEFAULT_BAR_HEIGHT,
872                                                      G_PARAM_READWRITE ) );
873
874    g_object_class_install_property( gobject_class, P_COMPACT,
875                                    g_param_spec_boolean( "compact", NULL,
876                                                          "Compact Mode",
877                                                          FALSE,
878                                                          G_PARAM_READWRITE ) );
879}
880
881static void
882torrent_cell_renderer_init( GTypeInstance *  instance,
883                            gpointer g_class UNUSED )
884{
885    TorrentCellRenderer *               self = TORRENT_CELL_RENDERER(
886        instance );
887    struct TorrentCellRendererPrivate * p;
888
889    p = self->priv = G_TYPE_INSTANCE_GET_PRIVATE(
890            self,
891            TORRENT_CELL_RENDERER_TYPE,
892            struct
893            TorrentCellRendererPrivate );
894
895    p->tor = NULL;
896    p->text_renderer = gtk_cell_renderer_text_new( );
897    g_object_set( p->text_renderer, "xpad", 0, "ypad", 0, NULL );
898    p->text_renderer_err = gtk_cell_renderer_text_new(  );
899    g_object_set( p->text_renderer_err, "xpad", 0, "ypad", 0, NULL );
900    p->progress_renderer = gtk_cell_renderer_progress_new(  );
901    p->icon_renderer = gtk_cell_renderer_pixbuf_new(  );
902    g_object_set( p->text_renderer_err, "foreground", "red", NULL );
903    gtr_object_ref_sink( p->text_renderer );
904    gtr_object_ref_sink( p->text_renderer_err );
905    gtr_object_ref_sink( p->progress_renderer );
906    gtr_object_ref_sink( p->icon_renderer );
907
908    p->bar_height = DEFAULT_BAR_HEIGHT;
909}
910
911GType
912torrent_cell_renderer_get_type( void )
913{
914    static GType type = 0;
915
916    if( !type )
917    {
918        static const GTypeInfo info =
919        {
920            sizeof( TorrentCellRendererClass ),
921            NULL,                                            /* base_init */
922            NULL,                                            /* base_finalize */
923            (GClassInitFunc)torrent_cell_renderer_class_init,
924            NULL,                                            /* class_finalize
925                                                               */
926            NULL,                                            /* class_data */
927            sizeof( TorrentCellRenderer ),
928            0,                                               /* n_preallocs */
929            (GInstanceInitFunc)torrent_cell_renderer_init,
930            NULL
931        };
932
933        type = g_type_register_static( GTK_TYPE_CELL_RENDERER,
934                                       "TorrentCellRenderer",
935                                       &info, (GTypeFlags)0 );
936    }
937
938    return type;
939}
940
941GtkCellRenderer *
942torrent_cell_renderer_new( void )
943{
944    return (GtkCellRenderer *) g_object_new( TORRENT_CELL_RENDERER_TYPE,
945                                             NULL );
946}
947
Note: See TracBrowser for help on using the repository browser.