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

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

(trunk gtk) tinker with compact mode a little bit

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