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

Last change on this file since 5208 was 5208, checked in by charles, 14 years ago

more i18n strings work

  • Property svn:keywords set to Date Rev Author Id
File size: 21.7 KB
Line 
1/*
2 * This file Copyright (C) 2007-2008 Charles Kerr <charles@rebelbase.com>
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 5208 2008-03-06 21:59:00Z charles $
11 */
12
13#include "assert.h"
14#include <gtk/gtk.h>
15#include <gtk/gtkcellrenderertext.h>
16#include <gtk/gtkcellrendererprogress.h>
17#include <glib/gi18n.h>
18#include <libtransmission/transmission.h>
19#include "hig.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_BAR_HEIGHT,
30    P_MINIMAL
31};
32
33#define DEFAULT_BAR_HEIGHT 12
34
35/***
36****
37***/
38
39static char*
40getProgressString( const tr_info * info, const tr_stat * torStat )
41{
42    const int isDone = torStat->leftUntilDone == 0;
43    const uint64_t haveTotal = torStat->haveUnchecked + torStat->haveValid;
44    const int isSeed = torStat->haveValid >= info->totalSize;
45    char buf1[32], buf2[32], buf3[32], buf4[32];
46    char * str;
47
48    if( !isDone )
49        str = g_strdup_printf(
50                  _("%s of %s (%.2f%%)"),
51                  tr_strlsize( buf1, haveTotal, sizeof(buf1) ),
52                  tr_strlsize( buf2, torStat->desiredSize, sizeof(buf2) ),
53                  torStat->percentDone * 100.0 );
54    else if( !isSeed )
55        str = g_strdup_printf(
56                  _("%s of %s (%.2f%%), uploaded %s (Ratio: %s)"),
57                  tr_strlsize( buf1, haveTotal, sizeof(buf1) ),
58                  tr_strlsize( buf2, info->totalSize, sizeof(buf2) ),
59                  torStat->percentComplete * 100.0,
60                  tr_strlsize( buf3, torStat->uploadedEver, sizeof(buf3) ),
61                  tr_strlratio( buf4, torStat->ratio, sizeof( buf4 ) ) );
62    else
63        str = g_strdup_printf(
64                  _("%s, uploaded %s (Ratio: %s)"),
65                  tr_strlsize( buf1, info->totalSize, sizeof(buf1) ),
66                  tr_strlsize( buf2, torStat->uploadedEver, sizeof(buf2) ),
67                  tr_strlratio( buf3, torStat->ratio, sizeof( buf3 ) ) );
68
69    // add time when downloading
70    if( torStat->status == TR_STATUS_DOWNLOAD )
71    {
72        const int eta = torStat->eta;
73        GString * gstr = g_string_new( str );
74        g_string_append( gstr, " - " );
75        if( eta < 0 )
76            g_string_append( gstr, _( "Stalled" ) );
77        else {
78            char timestr[128];
79            tr_strltime( timestr, eta, sizeof( timestr ) );
80            g_string_append_printf( gstr, _( "%s remaining" ), timestr );
81        }
82        g_free( str );
83        str = g_string_free( gstr, FALSE );
84    }
85
86    return str;
87}
88
89static char*
90getShortTransferString( const tr_stat * torStat, char * buf, size_t buflen )
91{
92    char downStr[32], upStr[32];
93    const int haveDown = torStat->peersSendingToUs > 0;
94    const int haveUp = torStat->peersGettingFromUs > 0;
95
96    if( haveDown )
97        tr_strlspeed( downStr, torStat->rateDownload, sizeof(downStr) );
98    if( haveUp )
99        tr_strlspeed( upStr, torStat->rateUpload, sizeof(upStr) );
100
101    if( haveDown && haveUp )
102        g_snprintf( buf, buflen, _( "Down: %s, Up: %s"), downStr, upStr );
103    else if( haveDown )
104        g_snprintf( buf, buflen, _( "Down: %s" ), downStr );
105    else if( haveUp )
106        g_snprintf( buf, buflen, _( "Up: %s" ), upStr );
107    else
108        g_strlcpy( buf, _( "Idle" ), buflen );
109
110    return buf;
111}
112
113static char*
114getShortStatusString( const tr_stat * torStat )
115{
116    GString * gstr = g_string_new( NULL );
117
118    switch( torStat->status )
119    {
120        case TR_STATUS_STOPPED:
121            g_string_assign( gstr, _("Paused") );
122            break;
123
124        case TR_STATUS_CHECK_WAIT:
125            g_string_assign( gstr, _( "Waiting to verify local data" ) );
126            break;
127
128        case TR_STATUS_CHECK:
129            g_string_append_printf( gstr, _("Verifying local data (%.1f%% tested)"),
130                                    torStat->recheckProgress * 100.0 );
131            break;
132
133        case TR_STATUS_DOWNLOAD:
134        case TR_STATUS_SEED:
135        case TR_STATUS_DONE: {
136            char buf[128];
137            if( torStat->status != TR_STATUS_DOWNLOAD ) {
138                tr_strlratio( buf, torStat->ratio, sizeof( buf ) );
139                g_string_append_printf( gstr, _("Ratio: %s, " ), buf );
140            }
141            getShortTransferString( torStat, buf, sizeof( buf ) );
142            g_string_append( gstr, buf );
143            break;
144        }
145
146        default:
147            break;
148    }
149
150    return g_string_free( gstr, FALSE );
151}
152
153static char*
154getStatusString( const tr_stat * torStat )
155{
156    const int isActive = torStat->status != TR_STATUS_STOPPED;
157    const int isChecking = torStat->status == TR_STATUS_CHECK
158                        || torStat->status == TR_STATUS_CHECK_WAIT;
159
160    GString * gstr = g_string_new( NULL );
161
162    if( torStat->error )
163    {
164        g_string_assign( gstr, torStat->errorString );
165    }
166    else switch( torStat->status )
167    {
168        case TR_STATUS_STOPPED:
169        case TR_STATUS_CHECK_WAIT:
170        case TR_STATUS_CHECK: {
171            char * pch = getShortStatusString( torStat );
172            g_string_assign( gstr, pch );
173            g_free( pch );
174            break;
175        }
176
177        case TR_STATUS_DOWNLOAD:
178            g_string_append_printf( gstr,
179                ngettext( "Downloading from %d of %d connected peer",
180                          "Downloading from %d of %d connected peers",
181                          torStat->peersConnected ),
182                torStat->peersSendingToUs,
183                torStat->peersConnected );
184            break;
185
186        case TR_STATUS_DONE:
187        case TR_STATUS_SEED:
188            g_string_append_printf( gstr,
189                ngettext( "Seeding to %d of %d connected peer",
190                          "Seeding to %d of %d connected peers",
191                          torStat->peersConnected ),
192                torStat->peersGettingFromUs,
193                torStat->peersConnected );
194            break;
195    }
196
197    if( isActive && !isChecking )
198    {
199        char buf[256];
200        getShortTransferString( torStat, buf, sizeof(buf) );
201        g_string_append_printf( gstr, " - %s", buf );
202    }
203
204    return g_string_free( gstr, FALSE );
205}
206
207/***
208****
209***/
210
211static GtkCellRendererClass * parent_class = NULL;
212
213struct TorrentCellRendererPrivate
214{
215    tr_torrent * tor;
216    GtkCellRenderer * text_renderer;
217    GtkCellRenderer * text_renderer_err;
218    GtkCellRenderer * progress_renderer;
219    int bar_height;
220    gboolean minimal;
221};
222
223static void
224torrent_cell_renderer_get_size( GtkCellRenderer  * cell,
225                                GtkWidget        * widget,
226                                GdkRectangle     * cell_area,
227                                gint             * x_offset,
228                                gint             * y_offset,
229                                gint             * width,
230                                gint             * height)
231{
232    TorrentCellRenderer * self = TORRENT_CELL_RENDERER( cell );
233    int xpad, ypad;
234    g_object_get( self, "xpad", &xpad, "ypad", &ypad, NULL );
235
236    if( self && self->priv->tor )
237    {
238        const tr_torrent * tor = self->priv->tor;
239        const tr_info * info = tr_torrentInfo( tor );
240        const char * name = info->name;
241        const tr_stat * torStat = tr_torrentStatCached( (tr_torrent*)tor );
242        char * str;
243        int w=0, h=0;
244        struct TorrentCellRendererPrivate * p = self->priv;
245        GtkCellRenderer * text_renderer = torStat->error != 0
246            ? p->text_renderer_err
247            : p->text_renderer;
248
249        g_object_set( text_renderer, "ellipsize", PANGO_ELLIPSIZE_NONE, NULL );
250
251        /* above the progressbar */
252        if( p->minimal )
253        {
254            int w1, w2, h1, h2;
255            char * shortStatus = getShortStatusString( torStat );
256            g_object_set( text_renderer, "text", name, NULL );
257            gtk_cell_renderer_get_size( text_renderer,
258                                        widget, NULL, NULL, NULL, &w1, &h1 );
259            str = g_markup_printf_escaped( "<small>%s</small>", shortStatus );
260            g_object_set( text_renderer, "markup", str, NULL );
261            gtk_cell_renderer_get_size( text_renderer,
262                                        widget, NULL, NULL, NULL, &w2, &h2 );
263            h += MAX( h1, h2 );
264            w = MAX( w, w1+GUI_PAD_BIG+w2 );
265            g_free( str );
266            g_free( shortStatus );
267        }
268        else
269        {
270            int w1, h1;
271            char * progressString = getProgressString( info, torStat );
272            str = g_markup_printf_escaped( "<b>%s</b>\n<small>%s</small>",
273                                           name, progressString );
274            g_object_set( text_renderer, "markup", str, NULL );
275            gtk_cell_renderer_get_size( text_renderer,
276                                        widget, NULL, NULL, NULL, &w1, &h1 );
277            h += h1;
278            w = MAX( w, w1 );
279            g_free( str );
280            g_free( progressString );
281        }
282
283        /* below the progressbar */
284        if( !p->minimal )
285        {
286            int w1, h1;
287            char * statusString = getStatusString( torStat );
288            str = g_markup_printf_escaped( "<small>%s</small>", statusString );
289            g_object_set( text_renderer, "markup", str, NULL );
290            gtk_cell_renderer_get_size( text_renderer,
291                                        widget, NULL, NULL, NULL, &w1, &h1 );
292            h += h1;
293            w = MAX( w, w1 );
294            g_free( str );
295            g_free( statusString );
296        }
297
298        h += p->bar_height;
299
300        if( cell_area ) {
301            if( x_offset ) *x_offset = 0;
302            if( y_offset ) {
303                *y_offset = 0.5 * (cell_area->height - (h + (2 * ypad)));
304                *y_offset = MAX( *y_offset, 0 );
305            }
306        }
307
308        *width = w + xpad*2;
309        *height = h + ypad*2;
310    }
311}
312
313static void
314torrent_cell_renderer_render( GtkCellRenderer      * cell,
315                              GdkDrawable          * window,
316                              GtkWidget            * widget,
317                              GdkRectangle         * background_area,
318                              GdkRectangle         * cell_area UNUSED,
319                              GdkRectangle         * expose_area UNUSED,
320                              GtkCellRendererState   flags)
321{
322    TorrentCellRenderer * self = TORRENT_CELL_RENDERER( cell );
323
324#ifdef TEST_RTL
325    GtkTextDirection real_dir = gtk_widget_get_direction( widget );
326    gtk_widget_set_direction( widget, GTK_TEXT_DIR_RTL );
327#endif
328
329    if( self && self->priv->tor )
330    {
331        const tr_torrent * tor = self->priv->tor;
332        const tr_info * info = tr_torrentInfo( tor );
333        const char * name = info->name;
334        const tr_stat * torStat = tr_torrentStatCached( (tr_torrent*)tor );
335        GdkRectangle my_bg;
336        GdkRectangle my_cell;
337        GdkRectangle my_expose;
338        int xpad, ypad;
339        int w, h;
340        struct TorrentCellRendererPrivate * p = self->priv;
341        GtkCellRenderer * text_renderer = torStat->error != 0
342            ? p->text_renderer_err
343            : p->text_renderer;
344        const gboolean isActive = torStat->status != TR_STATUS_STOPPED;
345
346        g_object_get( self, "xpad", &xpad, "ypad", &ypad, NULL );
347
348        my_bg = *background_area; 
349        my_bg.x += xpad;
350        my_bg.y += ypad;
351        my_bg.width -= xpad*2;
352        my_cell = my_expose = my_bg;
353
354        g_object_set( text_renderer, "sensitive", isActive, NULL );
355        g_object_set( p->progress_renderer, "sensitive", isActive, NULL );
356
357        /* above the progressbar */
358        if( !p->minimal )
359        {
360            char * progressString = getProgressString( info, torStat );
361            char * str = g_markup_printf_escaped( "<b>%s</b>\n<small>%s</small>",
362                                                  name, progressString );
363            g_object_set( text_renderer, "markup", str,
364                                            "ellipsize", PANGO_ELLIPSIZE_NONE,
365                                            NULL );
366            gtk_cell_renderer_get_size( text_renderer,
367                                        widget, NULL, NULL, NULL, &w, &h );
368            my_bg.height     = 
369            my_cell.height   =
370            my_expose.height = h;
371            g_object_set( text_renderer, "ellipsize", PANGO_ELLIPSIZE_END,
372                                            NULL );
373            gtk_cell_renderer_render( text_renderer,
374                                      window, widget,
375                                      &my_bg, &my_cell, &my_expose, flags );
376            my_bg.y += h;
377            my_cell.y += h;
378            my_expose.y += h;
379
380            g_free( str );
381            g_free( progressString );
382        }
383        else
384        {
385            char * statusStr = getShortStatusString( torStat );
386            char * str = g_markup_printf_escaped( "<small>%s</small>", statusStr );
387            int w1, w2, h1, h2, tmp_h;
388            GdkRectangle tmp_bg, tmp_cell, tmp_expose;
389
390            /* get the dimensions for the name */
391            g_object_set( text_renderer, "text", name,
392                                         "ellipsize", PANGO_ELLIPSIZE_NONE,
393                                         NULL );
394            gtk_cell_renderer_get_size( text_renderer,
395                                        widget, NULL, NULL, NULL, &w1, &h1 );
396
397            /* get the dimensions for the short status string */
398            g_object_set( text_renderer, "markup", str,
399                                         "ellipsize", PANGO_ELLIPSIZE_NONE,
400                                         NULL );
401            gtk_cell_renderer_get_size( text_renderer,
402                                        widget, NULL, NULL, NULL, &w2, &h2 );
403
404            tmp_h = MAX( h1, h2 );
405
406            /* short status */
407            tmp_bg.x = my_bg.width - w2;
408            tmp_bg.y = my_bg.y + (h2-h1)/2;
409            tmp_bg.width = w2;
410            tmp_bg.height = tmp_h;
411            tmp_expose = tmp_cell = tmp_bg;
412            g_object_set( text_renderer, "markup", str,
413                                         "ellipsize", PANGO_ELLIPSIZE_END,
414                                         NULL );
415            gtk_cell_renderer_render( text_renderer,
416                                      window, widget,
417                                      &tmp_bg, &tmp_cell, &tmp_expose, flags );
418
419            /* name */
420            tmp_bg.x = my_bg.x;
421            tmp_bg.width = my_bg.width - w2 - GUI_PAD_BIG;
422            tmp_expose = tmp_cell = tmp_bg;
423            g_object_set( text_renderer, "text", name,
424                                         "ellipsize", PANGO_ELLIPSIZE_END,
425                                         NULL );
426            gtk_cell_renderer_render( text_renderer,
427                                      window, widget,
428                                      &tmp_bg, &tmp_cell, &tmp_expose, flags );
429
430            my_bg.y = tmp_bg.y + tmp_bg.height;
431            my_cell.y = tmp_cell.y + tmp_cell.height;
432            my_expose.y += tmp_expose.y + tmp_cell.height;
433
434            g_free( str );
435            g_free( statusStr );
436        }
437
438        /* the progressbar */
439        my_cell.height = p->bar_height;
440        if( 1 )
441        {
442            const double havePercent = ( torStat->haveValid + torStat->haveUnchecked )
443                                                              / (double)info->totalSize;
444            g_object_set( p->progress_renderer, "value", (int)(havePercent*100.0), 
445                                                "text", "",
446                                                NULL );
447            gtk_cell_renderer_render( p->progress_renderer,
448                                      window, widget,
449                                      &my_cell, &my_cell, &my_cell, flags );
450 
451        }
452        my_bg.y     += my_cell.height;
453        my_cell.y   += my_cell.height;
454        my_expose.y += my_cell.height;
455
456        /* below progressbar */
457        if( !p->minimal )
458        {
459            char * statusString = getStatusString( torStat );
460            char * str = g_markup_printf_escaped( "<small>%s</small>",
461                                                  statusString );
462            g_object_set( text_renderer, "markup", str,
463                                         "ellipsize", PANGO_ELLIPSIZE_END,
464                                         NULL );
465            gtk_cell_renderer_get_size( text_renderer,
466                                        widget, NULL, NULL, NULL, &w, &h );
467            my_bg.height      =
468            my_cell.height    =
469            my_expose.height  = h;
470            gtk_cell_renderer_render( text_renderer,
471                                      window, widget,
472                                      &my_bg, &my_cell, &my_expose, flags );
473
474            g_free( str );
475            g_free( statusString );
476        }
477    }
478
479#ifdef TEST_RTL
480    gtk_widget_set_direction( widget, real_dir );
481#endif
482}
483
484static void
485torrent_cell_renderer_set_property( GObject      * object,
486                                    guint          property_id,
487                                    const GValue * v,
488                                    GParamSpec   * pspec)
489{
490    TorrentCellRenderer * self = TORRENT_CELL_RENDERER( object );
491    struct TorrentCellRendererPrivate * p = self->priv;
492
493    switch( property_id )
494    {
495        case P_TORRENT:     p->tor = g_value_get_pointer( v ); break;
496        case P_BAR_HEIGHT:  p->bar_height = g_value_get_int( v ); break;
497        case P_MINIMAL:     p->minimal  = g_value_get_boolean( v ); break;
498        default:
499            G_OBJECT_WARN_INVALID_PROPERTY_ID( object, property_id, pspec );
500            break;
501    }
502}
503
504static void
505torrent_cell_renderer_get_property( GObject      * object,
506                                    guint          property_id,
507                                    GValue       * v,
508                                    GParamSpec   * pspec)
509{
510    const TorrentCellRenderer * self = TORRENT_CELL_RENDERER( object );
511    struct TorrentCellRendererPrivate * p = self->priv;
512
513    switch( property_id )
514    {
515        case P_TORRENT:     g_value_set_pointer( v, p->tor ); break;
516        case P_BAR_HEIGHT:  g_value_set_int( v, p->bar_height ); break;
517        case P_MINIMAL:     g_value_set_boolean( v, p->minimal ); break;
518        default:
519            G_OBJECT_WARN_INVALID_PROPERTY_ID( object, property_id, pspec );
520            break;
521    }
522}
523
524static void
525torrent_cell_renderer_dispose( GObject * o )
526{
527    TorrentCellRenderer * r = TORRENT_CELL_RENDERER( o );
528    GObjectClass * parent;
529
530    if( r && r->priv )
531    {
532        g_object_unref( G_OBJECT( r->priv->text_renderer ) );
533        g_object_unref( G_OBJECT( r->priv->text_renderer_err ) );
534        g_object_unref( G_OBJECT( r->priv->progress_renderer ) );
535        r->priv = NULL;
536    }
537
538    parent = g_type_class_peek( g_type_parent( TORRENT_CELL_RENDERER_TYPE ) );
539    parent->dispose( o );
540}
541
542static void
543torrent_cell_renderer_class_init( TorrentCellRendererClass * klass )
544{
545    GObjectClass * gobject_class = G_OBJECT_CLASS( klass );
546    GtkCellRendererClass * cell_class = GTK_CELL_RENDERER_CLASS( klass );
547
548    g_type_class_add_private( klass,
549                              sizeof(struct TorrentCellRendererPrivate) );
550
551    parent_class = (GtkCellRendererClass*) g_type_class_peek_parent( klass );
552
553    cell_class->render = torrent_cell_renderer_render;
554    cell_class->get_size = torrent_cell_renderer_get_size;
555    gobject_class->set_property = torrent_cell_renderer_set_property;
556    gobject_class->get_property = torrent_cell_renderer_get_property;
557    gobject_class->dispose = torrent_cell_renderer_dispose;
558
559    g_object_class_install_property( gobject_class, P_TORRENT,
560        g_param_spec_pointer( "torrent", NULL, "tr_torrent*",
561                              G_PARAM_READWRITE ) );
562
563    g_object_class_install_property( gobject_class, P_BAR_HEIGHT,
564        g_param_spec_int( "bar-height", NULL, "Bar Height",
565                          1, INT_MAX, DEFAULT_BAR_HEIGHT, G_PARAM_READWRITE ) );
566
567    g_object_class_install_property( gobject_class, P_MINIMAL,
568        g_param_spec_boolean( "minimal", NULL, "Minimal Mode",
569                              FALSE, G_PARAM_READWRITE ) );
570}
571
572static void
573torrent_cell_renderer_init( GTypeInstance * instance, gpointer g_class UNUSED )
574{
575    TorrentCellRenderer * self = TORRENT_CELL_RENDERER( instance );
576    struct TorrentCellRendererPrivate * p;
577   
578    p = self->priv = G_TYPE_INSTANCE_GET_PRIVATE( self,
579                         TORRENT_CELL_RENDERER_TYPE,
580                         struct TorrentCellRendererPrivate );
581
582    p->tor = NULL;
583    p->text_renderer = gtk_cell_renderer_text_new( );
584    p->text_renderer_err = gtk_cell_renderer_text_new(  );
585    p->progress_renderer = gtk_cell_renderer_progress_new(  );
586    g_object_set( p->text_renderer_err, "foreground", "red", NULL );
587    tr_object_ref_sink( p->text_renderer );
588    tr_object_ref_sink( p->text_renderer_err );
589    tr_object_ref_sink( p->progress_renderer );
590
591    p->bar_height = DEFAULT_BAR_HEIGHT;
592}
593
594GType
595torrent_cell_renderer_get_type( void )
596{
597    static GType type = 0;
598
599    if( !type )
600    {
601        static const GTypeInfo info =
602        {
603            sizeof( TorrentCellRendererClass ),
604            NULL, /* base_init */
605            NULL, /* base_finalize */
606            (GClassInitFunc)torrent_cell_renderer_class_init,
607            NULL, /* class_finalize */
608            NULL, /* class_data */
609            sizeof( TorrentCellRenderer ),
610            0, /* n_preallocs */
611            (GInstanceInitFunc)torrent_cell_renderer_init,
612            NULL
613        };
614
615        type = g_type_register_static( GTK_TYPE_CELL_RENDERER,
616                                       "TorrentCellRenderer",
617                                       &info, (GTypeFlags)0 );
618    }
619
620    return type;
621}
622
623GtkCellRenderer *
624torrent_cell_renderer_new( void )
625{
626    return (GtkCellRenderer *) g_object_new( TORRENT_CELL_RENDERER_TYPE, NULL );
627}
Note: See TracBrowser for help on using the repository browser.