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

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

(trunk gtk) #3585 -- in torrent_cell_renderer_get_size(), set x_offset and y_offset even if cell_area is NULL

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