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

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

synchronize the Qt and GTK+ client's statusbars, including the new freespace indicator. Make the up/down speed ordering consistent between statusbar and torrent list.

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