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

Last change on this file since 13817 was 13817, checked in by jordan, 8 years ago

(gtk) #5235 'Plural wrong in torrent-cell-renderer' -- fix with patch by RamsesDeNorre?

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