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

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

(trunk gtk) small renderer cleanup from the #3488 patch

  • Property svn:keywords set to Date Rev Author Id
File size: 34.2 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 11135 2010-08-06 15:17:27Z 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 = 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_KBps, p->download_speed_KBps );
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_KBps, p->download_speed_KBps );
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    GdkRectangle icon_area;
543    GdkRectangle name_area;
544    GdkRectangle stat_area;
545    GdkRectangle prog_area;
546    GdkRectangle fill_area;
547    const char * name;
548    char * status;
549    GdkPixbuf * icon;
550    GtkCellRenderer * text_renderer;
551
552    struct TorrentCellRendererPrivate * p = cell->priv;
553    const tr_torrent * tor = p->tor;
554    const tr_stat * st = tr_torrentStatCached( (tr_torrent*)tor );
555    const gboolean active = st->activity != TR_STATUS_STOPPED;
556    const double percentDone = MAX( 0.0, st->percentDone );
557    const gboolean sensitive = active || st->error;
558
559    icon = get_icon( tor, COMPACT_ICON_SIZE, widget );
560    name = tr_torrentInfo( tor )->name;
561    status = getShortStatusString( tor, st, p->upload_speed_KBps, p->download_speed_KBps );
562
563    fill_area = *background_area;
564    fill_area.x += cell->parent.xpad;
565    fill_area.y += cell->parent.ypad;
566    fill_area.width -= cell->parent.xpad * 2;
567    fill_area.height -= cell->parent.ypad * 2;
568    icon_area = name_area = stat_area = prog_area = fill_area;
569
570    g_object_set( p->icon_renderer, "pixbuf", icon, NULL );
571    gtk_cell_renderer_get_size( p->icon_renderer, widget, NULL, NULL, NULL, &icon_area.width, NULL );
572    text_renderer = get_text_renderer( st, cell );
573    g_object_set( text_renderer, "text", name, "ellipsize", PANGO_ELLIPSIZE_NONE, "scale", 1.0, NULL );
574    gtk_cell_renderer_get_size( text_renderer, widget, NULL, NULL, NULL, &name_area.width, NULL );
575    g_object_set( text_renderer, "text", status, "scale", SMALL_SCALE, NULL );
576    gtk_cell_renderer_get_size( text_renderer, widget, NULL, NULL, NULL, &stat_area.width, NULL );
577
578    icon_area.x = fill_area.x;
579    prog_area.x = fill_area.x + fill_area.width - BAR_WIDTH;
580    prog_area.width = BAR_WIDTH;
581    stat_area.x = prog_area.x - GUI_PAD - stat_area.width;
582    name_area.x = icon_area.x + icon_area.width + GUI_PAD;
583    name_area.y = fill_area.y;
584    name_area.width = stat_area.x - GUI_PAD - name_area.x;
585
586    /**
587    *** RENDER
588    **/
589
590    g_object_set( p->icon_renderer, "pixbuf", icon, "sensitive", sensitive, NULL );
591    gtk_cell_renderer_render( p->icon_renderer, window, widget, &icon_area, &icon_area, &icon_area, flags );
592    g_object_set( p->progress_renderer, "value", (int)(percentDone*100.0), "text", NULL, "sensitive", sensitive, NULL );
593    gtk_cell_renderer_render( p->progress_renderer, window, widget, &prog_area, &prog_area, &prog_area, flags );
594    g_object_set( text_renderer, "text", status, "scale", SMALL_SCALE, "sensitive", sensitive, "ellipsize", PANGO_ELLIPSIZE_END, NULL );
595    gtk_cell_renderer_render( text_renderer, window, widget, &stat_area, &stat_area, &stat_area, flags );
596    g_object_set( text_renderer, "text", name, "scale", 1.0, NULL );
597    gtk_cell_renderer_render( text_renderer, window, widget, &name_area, &name_area, &name_area, flags );
598
599    /* cleanup */
600    g_free( status );
601    g_object_unref( icon );
602}
603
604static void
605render_full( TorrentCellRenderer   * cell,
606             GdkDrawable           * window,
607             GtkWidget             * widget,
608             GdkRectangle          * background_area,
609             GdkRectangle          * cell_area UNUSED,
610             GdkRectangle          * expose_area UNUSED,
611             GtkCellRendererState    flags )
612{
613    int w, h;
614    GdkRectangle fill_area;
615    GdkRectangle icon_area;
616    GdkRectangle name_area;
617    GdkRectangle stat_area;
618    GdkRectangle prog_area;
619    GdkRectangle prct_area;
620    const char * name;
621    char * status;
622    char * progress;
623    GdkPixbuf * icon;
624    GtkCellRenderer * text_renderer;
625
626    struct TorrentCellRendererPrivate * p = cell->priv;
627    const tr_torrent * tor = p->tor;
628    const tr_stat * st = tr_torrentStatCached( (tr_torrent*)tor );
629    const tr_info * inf = tr_torrentInfo( tor );
630    const gboolean active = st->activity != TR_STATUS_STOPPED;
631    const double percentDone = MAX( 0.0, st->percentDone );
632    const gboolean sensitive = active || st->error;
633
634    icon = get_icon( tor, FULL_ICON_SIZE, widget );
635    name = inf->name;
636    status = getStatusString( tor, st, p->upload_speed_KBps, p->download_speed_KBps );
637    progress = getProgressString( tor, inf, st );
638
639    /* get the idealized cell dimensions */
640    g_object_set( p->icon_renderer, "pixbuf", icon, NULL );
641    gtk_cell_renderer_get_size( p->icon_renderer, widget, NULL, NULL, NULL, &w, &h );
642    icon_area.width = w;
643    icon_area.height = h;
644    text_renderer = get_text_renderer( st, cell );
645    g_object_set( text_renderer, "text", name, "weight", PANGO_WEIGHT_BOLD, "ellipsize", PANGO_ELLIPSIZE_NONE, "scale", 1.0, NULL );
646    gtk_cell_renderer_get_size( text_renderer, widget, NULL, NULL, NULL, &w, &h );
647    name_area.width = w;
648    name_area.height = h;
649    g_object_set( text_renderer, "text", progress, "weight", PANGO_WEIGHT_NORMAL, "scale", SMALL_SCALE, NULL );
650    gtk_cell_renderer_get_size( text_renderer, widget, NULL, NULL, NULL, &w, &h );
651    prog_area.width = w;
652    prog_area.height = h;
653    g_object_set( text_renderer, "text", status, NULL );
654    gtk_cell_renderer_get_size( text_renderer, widget, NULL, NULL, NULL, &w, &h );
655    stat_area.width = w;
656    stat_area.height = h;
657
658    /**
659    *** LAYOUT
660    **/
661
662    fill_area = *background_area;
663    fill_area.x += cell->parent.xpad;
664    fill_area.y += cell->parent.ypad;
665    fill_area.width -= cell->parent.xpad * 2;
666    fill_area.height -= cell->parent.ypad * 2;
667
668    /* icon */
669    icon_area.x = fill_area.x;
670    icon_area.y = fill_area.y + ( fill_area.height - icon_area.height ) / 2;
671
672    /* name */
673    name_area.x = icon_area.x + icon_area.width + GUI_PAD;
674    name_area.y = fill_area.y;
675    name_area.width = fill_area.width - GUI_PAD - icon_area.width - GUI_PAD_SMALL;
676
677    /* prog */
678    prog_area.x = name_area.x;
679    prog_area.y = name_area.y + name_area.height;
680    prog_area.width = name_area.width;
681
682    /* progressbar */
683    prct_area.x = prog_area.x;
684    prct_area.y = prog_area.y + prog_area.height + GUI_PAD_SMALL;
685    prct_area.width = prog_area.width;
686    prct_area.height = p->bar_height;
687
688    /* status */
689    stat_area.x = prct_area.x;
690    stat_area.y = prct_area.y + prct_area.height + GUI_PAD_SMALL;
691    stat_area.width = prct_area.width;
692
693    /**
694    *** RENDER
695    **/
696
697    g_object_set( p->icon_renderer, "pixbuf", icon, "sensitive", sensitive, NULL );
698    gtk_cell_renderer_render( p->icon_renderer, window, widget, &icon_area, &icon_area, &icon_area, flags );
699    g_object_set( text_renderer, "text", name, "scale", 1.0, "sensitive", sensitive, "ellipsize", PANGO_ELLIPSIZE_END, "weight", PANGO_WEIGHT_BOLD, NULL );
700    gtk_cell_renderer_render( text_renderer, window, widget, &name_area, &name_area, &name_area, flags );
701    g_object_set( text_renderer, "text", progress, "scale", SMALL_SCALE, "weight", PANGO_WEIGHT_NORMAL, NULL );
702    gtk_cell_renderer_render( text_renderer, window, widget, &prog_area, &prog_area, &prog_area, flags );
703    g_object_set( p->progress_renderer, "value", (int)(percentDone*100.0), "text", "", "sensitive", sensitive, NULL );
704    gtk_cell_renderer_render( p->progress_renderer, window, widget, &prct_area, &prct_area, &prct_area, flags );
705    g_object_set( text_renderer, "text", status, NULL );
706    gtk_cell_renderer_render( text_renderer, window, widget, &stat_area, &stat_area, &stat_area, flags );
707
708    /* cleanup */
709    g_free( status );
710    g_free( progress );
711    g_object_unref( icon );
712}
713
714static void
715torrent_cell_renderer_render( GtkCellRenderer       * cell,
716                              GdkDrawable           * window,
717                              GtkWidget             * widget,
718                              GdkRectangle          * background_area,
719                              GdkRectangle          * cell_area,
720                              GdkRectangle          * expose_area,
721                              GtkCellRendererState    flags )
722{
723    TorrentCellRenderer * self = TORRENT_CELL_RENDERER( cell );
724
725#ifdef TEST_RTL
726    GtkTextDirection      real_dir = gtk_widget_get_direction( widget );
727    gtk_widget_set_direction( widget, GTK_TEXT_DIR_RTL );
728#endif
729
730    if( self && self->priv->tor )
731    {
732        struct TorrentCellRendererPrivate * p = self->priv;
733        if( p->compact )
734            render_compact( self, window, widget, background_area, cell_area, expose_area, flags );
735        else
736            render_full( self, window, widget, background_area, cell_area, expose_area, flags );
737    }
738
739#ifdef TEST_RTL
740    gtk_widget_set_direction( widget, real_dir );
741#endif
742}
743
744static void
745torrent_cell_renderer_set_property( GObject      * object,
746                                    guint          property_id,
747                                    const GValue * v,
748                                    GParamSpec   * pspec )
749{
750    TorrentCellRenderer * self = TORRENT_CELL_RENDERER( object );
751    struct TorrentCellRendererPrivate * p = self->priv;
752
753    switch( property_id )
754    {
755        case P_TORRENT:        p->tor                 = g_value_get_pointer( v ); break;
756        case P_UPLOAD_SPEED:   p->upload_speed_KBps   = g_value_get_double( v ); break;
757        case P_DOWNLOAD_SPEED: p->download_speed_KBps = g_value_get_double( v ); break;
758        case P_BAR_HEIGHT:     p->bar_height          = g_value_get_int( v ); break;
759        case P_COMPACT:        p->compact             = g_value_get_boolean( v ); break;
760        default: G_OBJECT_WARN_INVALID_PROPERTY_ID( object, property_id, pspec ); break;
761    }
762}
763
764static void
765torrent_cell_renderer_get_property( GObject     * object,
766                                    guint         property_id,
767                                    GValue      * v,
768                                    GParamSpec  * pspec )
769{
770    const TorrentCellRenderer * self = TORRENT_CELL_RENDERER( object );
771    struct TorrentCellRendererPrivate * p = self->priv;
772
773    switch( property_id )
774    {
775        case P_TORRENT:        g_value_set_pointer( v, p->tor ); break;
776        case P_UPLOAD_SPEED:   g_value_set_double( v, p->upload_speed_KBps ); break;
777        case P_DOWNLOAD_SPEED: g_value_set_double( v, p->download_speed_KBps ); break;
778        case P_BAR_HEIGHT:     g_value_set_int( v, p->bar_height ); break;
779        case P_COMPACT:        g_value_set_boolean( v, p->compact ); break;
780        default: G_OBJECT_WARN_INVALID_PROPERTY_ID( object, property_id, pspec ); break;
781    }
782}
783
784static void
785torrent_cell_renderer_dispose( GObject * o )
786{
787    TorrentCellRenderer * r = TORRENT_CELL_RENDERER( o );
788    GObjectClass *        parent;
789
790    if( r && r->priv )
791    {
792        g_object_unref( G_OBJECT( r->priv->text_renderer ) );
793        g_object_unref( G_OBJECT( r->priv->text_renderer_err ) );
794        g_object_unref( G_OBJECT( r->priv->progress_renderer ) );
795        g_object_unref( G_OBJECT( r->priv->icon_renderer ) );
796        r->priv = NULL;
797    }
798
799    parent = g_type_class_peek( g_type_parent( TORRENT_CELL_RENDERER_TYPE ) );
800    parent->dispose( o );
801}
802
803static void
804torrent_cell_renderer_class_init( TorrentCellRendererClass * klass )
805{
806    GObjectClass *         gobject_class = G_OBJECT_CLASS( klass );
807    GtkCellRendererClass * cell_class = GTK_CELL_RENDERER_CLASS( klass );
808
809    g_type_class_add_private( klass,
810                             sizeof( struct TorrentCellRendererPrivate ) );
811
812    parent_class = (GtkCellRendererClass*) g_type_class_peek_parent( klass );
813
814    cell_class->render = torrent_cell_renderer_render;
815    cell_class->get_size = torrent_cell_renderer_get_size;
816    gobject_class->set_property = torrent_cell_renderer_set_property;
817    gobject_class->get_property = torrent_cell_renderer_get_property;
818    gobject_class->dispose = torrent_cell_renderer_dispose;
819
820    g_object_class_install_property( gobject_class, P_TORRENT,
821                                    g_param_spec_pointer( "torrent", NULL,
822                                                          "tr_torrent*",
823                                                          G_PARAM_READWRITE ) );
824
825    g_object_class_install_property( gobject_class, P_UPLOAD_SPEED,
826                                    g_param_spec_double( "piece-upload-speed", NULL,
827                                                         "tr_stat.pieceUploadSpeed_KBps",
828                                                         0, INT_MAX, 0,
829                                                         G_PARAM_READWRITE ) );
830
831    g_object_class_install_property( gobject_class, P_DOWNLOAD_SPEED,
832                                    g_param_spec_double( "piece-download-speed", NULL,
833                                                         "tr_stat.pieceDownloadSpeed_KBps",
834                                                         0, INT_MAX, 0,
835                                                         G_PARAM_READWRITE ) );
836
837    g_object_class_install_property( gobject_class, P_BAR_HEIGHT,
838                                    g_param_spec_int( "bar-height", NULL,
839                                                      "Bar Height",
840                                                      1, INT_MAX,
841                                                      DEFAULT_BAR_HEIGHT,
842                                                      G_PARAM_READWRITE ) );
843
844    g_object_class_install_property( gobject_class, P_COMPACT,
845                                    g_param_spec_boolean( "compact", NULL,
846                                                          "Compact Mode",
847                                                          FALSE,
848                                                          G_PARAM_READWRITE ) );
849}
850
851static void
852torrent_cell_renderer_init( GTypeInstance *  instance,
853                            gpointer g_class UNUSED )
854{
855    TorrentCellRenderer *               self = TORRENT_CELL_RENDERER(
856        instance );
857    struct TorrentCellRendererPrivate * p;
858
859    p = self->priv = G_TYPE_INSTANCE_GET_PRIVATE(
860            self,
861            TORRENT_CELL_RENDERER_TYPE,
862            struct
863            TorrentCellRendererPrivate );
864
865    p->tor = NULL;
866    p->text_renderer = gtk_cell_renderer_text_new( );
867    g_object_set( p->text_renderer, "xpad", 0, "ypad", 0, NULL );
868    p->text_renderer_err = gtk_cell_renderer_text_new(  );
869    g_object_set( p->text_renderer_err, "xpad", 0, "ypad", 0, NULL );
870    p->progress_renderer = gtk_cell_renderer_progress_new(  );
871    p->icon_renderer = gtk_cell_renderer_pixbuf_new(  );
872    g_object_set( p->text_renderer_err, "foreground", "red", NULL );
873    gtr_object_ref_sink( p->text_renderer );
874    gtr_object_ref_sink( p->text_renderer_err );
875    gtr_object_ref_sink( p->progress_renderer );
876    gtr_object_ref_sink( p->icon_renderer );
877
878    p->bar_height = DEFAULT_BAR_HEIGHT;
879}
880
881GType
882torrent_cell_renderer_get_type( void )
883{
884    static GType type = 0;
885
886    if( !type )
887    {
888        static const GTypeInfo info =
889        {
890            sizeof( TorrentCellRendererClass ),
891            NULL,                                            /* base_init */
892            NULL,                                            /* base_finalize */
893            (GClassInitFunc)torrent_cell_renderer_class_init,
894            NULL,                                            /* class_finalize
895                                                               */
896            NULL,                                            /* class_data */
897            sizeof( TorrentCellRenderer ),
898            0,                                               /* n_preallocs */
899            (GInstanceInitFunc)torrent_cell_renderer_init,
900            NULL
901        };
902
903        type = g_type_register_static( GTK_TYPE_CELL_RENDERER,
904                                       "TorrentCellRenderer",
905                                       &info, (GTypeFlags)0 );
906    }
907
908    return type;
909}
910
911GtkCellRenderer *
912torrent_cell_renderer_new( void )
913{
914    return (GtkCellRenderer *) g_object_new( TORRENT_CELL_RENDERER_TYPE,
915                                             NULL );
916}
917
Note: See TracBrowser for help on using the repository browser.