From 834a3f2151dd8738a1f878489f6207664c4af5aa Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Sun, 14 Jul 2013 17:14:53 -0700 Subject: Imported Upstream version 1.1.1 --- src/torrent-cell-renderer.c | 1207 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1207 insertions(+) create mode 100644 src/torrent-cell-renderer.c (limited to 'src/torrent-cell-renderer.c') diff --git a/src/torrent-cell-renderer.c b/src/torrent-cell-renderer.c new file mode 100644 index 0000000..95f4d37 --- /dev/null +++ b/src/torrent-cell-renderer.c @@ -0,0 +1,1207 @@ +/* + * This file Copyright (C) Mnemosyne LLC + * + * This file is licensed by the GPL version 2. Works owned by the + * Transmission project are granted a special exemption to clause 2(b) + * so that the bulk of its code can remain under the MIT license. + * This exemption does not extend to derived works not owned by + * the Transmission project. + * + * $Id: torrent-cell-renderer.c 13388 2012-07-14 19:26:55Z jordan $ + */ + +/* This cell renderer has been modified heavily to work with the + * TrgTorrentModel instead of libtransmission. */ + +#include +#include +#include + +#include "hig.h" +#include "icons.h" +#include "trg-client.h" +#include "torrent.h" +#include "util.h" +#include "torrent-cell-renderer.h" + +enum { + P_STATUS = 1, + P_CLIENT, + P_RATIO, + P_SEEDRATIOLIMIT, + P_SEEDRATIOMODE, + P_DOWNLOADED, + P_HAVEVALID, + P_HAVEUNCHECKED, + P_ERROR, + P_SIZEWHENDONE, + P_TOTALSIZE, + P_UPLOADED, + P_PERCENTCOMPLETE, + P_METADATAPERCENTCOMPLETE, + P_UPSPEED, + P_DOWNSPEED, + P_PEERSGETTINGFROMUS, + P_WEBSEEDSTOUS, + P_PEERSTOUS, + P_ETA, + P_JSON, + P_CONNECTED, + P_FILECOUNT, + P_BAR_HEIGHT, + P_OWNER, + P_COMPACT +}; + +#define DEFAULT_BAR_HEIGHT 12 +#define SMALL_SCALE 0.9 +#define COMPACT_ICON_SIZE GTK_ICON_SIZE_MENU +#define FULL_ICON_SIZE GTK_ICON_SIZE_DND + +#if GTK_CHECK_VERSION( 3, 0, 0 ) +#define FOREGROUND_COLOR_KEY "foreground-rgba" +typedef GdkRGBA GtrColor; +typedef cairo_t GtrDrawable; +typedef GtkRequisition GtrRequisition; +#else +#define FOREGROUND_COLOR_KEY "foreground-gdk" +typedef GdkColor GtrColor; +typedef GdkWindow GtrDrawable; +typedef GdkRectangle GtrRequisition; +#endif + +/*** +**** +***/ + +static void +render_compact(TorrentCellRenderer * cell, + GtrDrawable * window, + GtkWidget * widget, + const GdkRectangle * background_area, + const GdkRectangle * cell_area, GtkCellRendererState flags); + +static void +render_full(TorrentCellRenderer * cell, + GtrDrawable * window, + GtkWidget * widget, + const GdkRectangle * background_area, + const GdkRectangle * cell_area, GtkCellRendererState flags); + +struct TorrentCellRendererPrivate { + GtkCellRenderer *text_renderer; + GtkCellRenderer *progress_renderer; + GtkCellRenderer *icon_renderer; + GString *gstr1; + GString *gstr2; + int bar_height; + + guint flags; + guint fileCount; + gint64 uploadedEver; + gint64 sizeWhenDone; + gint64 totalSize; + gint64 downloaded; + gint64 haveValid; + gint64 haveUnchecked; + gint64 upSpeed; + gint64 downSpeed; + gint64 peersFromUs; + gint64 webSeedsToUs; + gint64 peersToUs; + gint64 connected; + gint64 eta; + gint64 error; + gint64 seedRatioMode; + gdouble done; + gdouble metadataPercentComplete; + gdouble ratio; + gdouble seedRatioLimit; + gpointer json; + TrgClient *client; + GtkTreeView *owner; + gboolean compact; +}; + +static gboolean getSeedRatio(TorrentCellRenderer * r, gdouble * ratio) +{ + struct TorrentCellRendererPrivate *p = r->priv; + + if ((p->seedRatioMode == 0) + && (trg_client_get_seed_ratio_limited(p->client) == TRUE)) { + *ratio = trg_client_get_seed_ratio_limit(p->client); + return TRUE; + } else if (p->seedRatioMode == 1) { + *ratio = p->seedRatioLimit; + return TRUE; + } + + return FALSE; +} + +static void getProgressString(GString * gstr, TorrentCellRenderer * r) +{ + struct TorrentCellRendererPrivate *p = r->priv; + + const gint64 haveTotal = p->haveUnchecked + p->haveValid; + const int isSeed = p->haveValid >= p->totalSize; + char buf1[32], buf2[32], buf3[32], buf4[32], buf5[32], buf6[32]; + double seedRatio; + const gboolean hasSeedRatio = getSeedRatio(r, &seedRatio); + + if (p->flags & TORRENT_FLAG_DOWNLOADING) { /* downloading */ + g_string_append_printf(gstr, + /* %1$s is how much we've got, + %2$s is how much we'll have when done, + %3$s%% is a percentage of the two */ + _("%1$s of %2$s (%3$s)"), + tr_strlsize(buf1, haveTotal, sizeof(buf1)), + tr_strlsize(buf2, p->sizeWhenDone, + sizeof(buf2)), + tr_strlpercent(buf3, p->done, + sizeof(buf3))); + } else if (!isSeed) { /* Partial seed */ + if (hasSeedRatio) { + g_string_append_printf(gstr, + _ + ("%1$s of %2$s (%3$s), uploaded %4$s (Ratio: %5$s Goal: %6$s)"), + tr_strlsize(buf1, haveTotal, + sizeof(buf1)), + tr_strlsize(buf2, p->totalSize, + sizeof(buf2)), + tr_strlpercent(buf3, p->done, + sizeof(buf3)), + tr_strlsize(buf4, p->uploadedEver, + sizeof(buf4)), + tr_strlratio(buf5, p->ratio, + sizeof(buf5)), + tr_strlratio(buf6, seedRatio, + sizeof(buf6))); + } else { + g_string_append_printf(gstr, + _ + ("%1$s of %2$s (%3$s), uploaded %4$s (Ratio: %5$s)"), + tr_strlsize(buf1, haveTotal, + sizeof(buf1)), + tr_strlsize(buf2, p->totalSize, + sizeof(buf2)), + tr_strlpercent(buf3, p->done, + sizeof(buf3)), + tr_strlsize(buf4, p->uploadedEver, + sizeof(buf4)), + tr_strlratio(buf5, p->ratio, + sizeof(buf5))); + } + } else { /* seeding */ + + if (hasSeedRatio) { + g_string_append_printf(gstr, + _ + ("%1$s, uploaded %2$s (Ratio: %3$s Goal: %4$s)"), + tr_strlsize(buf1, p->totalSize, + sizeof(buf1)), + tr_strlsize(buf2, p->uploadedEver, + sizeof(buf2)), + tr_strlratio(buf3, p->ratio, + sizeof(buf3)), + tr_strlratio(buf4, seedRatio, + sizeof(buf4))); + } else { + g_string_append_printf(gstr, + /* %1$s is the torrent's total size, + %2$s is how much we've uploaded, + %3$s is our upload-to-download ratio */ + _("%1$s, uploaded %2$s (Ratio: %3$s)"), + tr_strlsize(buf1, p->sizeWhenDone, + sizeof(buf1)), + tr_strlsize(buf2, p->uploadedEver, + sizeof(buf2)), + tr_strlratio(buf3, p->ratio, + sizeof(buf3))); + } + } + + /* add time when downloading */ + if ((p->flags & TORRENT_FLAG_DOWNLOADING) + || (hasSeedRatio && (p->flags & TORRENT_FLAG_SEEDING))) { + gint64 eta = p->eta; + g_string_append(gstr, " - "); + if (eta < 0) + g_string_append(gstr, _("Remaining time unknown")); + else { + char timestr[128]; + tr_strltime_long(timestr, eta, sizeof(timestr)); + /* time remaining */ + g_string_append_printf(gstr, _("%s remaining"), timestr); + } + } +} + +static char *getShortTransferString(TorrentCellRenderer * r, + char *buf, size_t buflen) +{ + struct TorrentCellRendererPrivate *priv = r->priv; + + char downStr[32], upStr[32]; + const gboolean haveMeta = priv->fileCount > 0; + const gboolean haveUp = haveMeta && priv->peersFromUs > 0; + const gboolean haveDown = haveMeta && priv->peersToUs > 0; + + if (haveDown) + tr_formatter_speed_KBps(downStr, priv->downSpeed / speed_K, + sizeof(downStr)); + if (haveUp) + tr_formatter_speed_KBps(upStr, priv->upSpeed / speed_K, + sizeof(upStr)); + + if (haveDown && haveUp) + /* 1==down arrow, 2==down speed, 3==up arrow, 4==down speed */ + g_snprintf(buf, buflen, _("%1$s %2$s, %3$s %4$s"), + GTR_UNICODE_DOWN, downStr, GTR_UNICODE_UP, upStr); + else if (haveDown) + /* bandwidth speed + unicode arrow */ + g_snprintf(buf, buflen, _("%1$s %2$s"), GTR_UNICODE_DOWN, downStr); + else if (haveUp) + /* bandwidth speed + unicode arrow */ + g_snprintf(buf, buflen, _("%1$s %2$s"), GTR_UNICODE_UP, upStr); + /*else if( st->isStalled ) + g_strlcpy( buf, _( "Stalled" ), buflen ); */ + else if (haveMeta) + g_strlcpy(buf, _("Idle"), buflen); + else + *buf = '\0'; + + return buf; +} + +static void getShortStatusString(GString * gstr, TorrentCellRenderer * r) +{ + struct TorrentCellRendererPrivate *priv = r->priv; + guint flags = priv->flags; + + if (flags & TORRENT_FLAG_PAUSED) { + g_string_append(gstr, + (flags & TORRENT_FLAG_COMPLETE) ? _("Finished") : + _("Paused")); + } else if (flags & TORRENT_FLAG_WAITING_CHECK) { + g_string_append(gstr, _("Queued for verification")); + } else if (flags & TORRENT_FLAG_DOWNLOADING_WAIT) { + g_string_append(gstr, _("Queued for download")); + } else if (flags & TORRENT_FLAG_SEEDING_WAIT) { + g_string_append(gstr, _("Queued for seeding")); + } else if (flags & TORRENT_FLAG_CHECKING) { + char buf1[32]; + g_string_append_printf(gstr, _("Verifying data (%1$s tested)"), + tr_strlpercent(buf1, priv->done, + sizeof(buf1))); + } else if ((flags & TORRENT_FLAG_DOWNLOADING) + || (flags & TORRENT_FLAG_SEEDING)) { + char buf[512]; + if (flags & ~TORRENT_FLAG_DOWNLOADING) { + tr_strlratio(buf, priv->ratio, sizeof(buf)); + g_string_append_printf(gstr, _("Ratio %s"), buf); + g_string_append(gstr, ", "); + } + getShortTransferString(r, buf, sizeof(buf)); + g_string_append(gstr, buf); + } +} + +static void getStatusString(GString * gstr, TorrentCellRenderer * r) +{ + struct TorrentCellRendererPrivate *priv = r->priv; + char buf[256]; + + if (priv->error) { + const char *fmt[] = { NULL, N_("Tracker gave a warning: \"%s\""), + N_("Tracker gave an error: \"%s\""), + N_("Error: %s") + }; + g_string_append_printf(gstr, _(fmt[priv->error]), + torrent_get_errorstr(priv->json)); + } else if ((priv->flags & TORRENT_FLAG_PAUSED) + || (priv->flags & TORRENT_FLAG_WAITING_CHECK) + || (priv->flags & TORRENT_FLAG_CHECKING) + || (priv->flags & TORRENT_FLAG_DOWNLOADING_WAIT) + || (priv->flags & TORRENT_FLAG_SEEDING_WAIT)) { + getShortStatusString(gstr, r); + } else if (priv->flags & TORRENT_FLAG_DOWNLOADING) { + if (priv->fileCount > 0) { + g_string_append_printf(gstr, + ngettext + ("Downloading from %1$li of %2$li connected peer", + "Downloading from %1$li of %2$li connected peers", + priv->webSeedsToUs + priv->peersToUs), + priv->webSeedsToUs + priv->peersToUs, + priv->webSeedsToUs + priv->connected); + } else { + g_string_append_printf(gstr, + ngettext + ("Downloading metadata from %1$li peer (%2$s done)", + "Downloading metadata from %1$li peers (%2$s done)", + priv->connected + priv->webSeedsToUs), + priv->connected + priv->webSeedsToUs, + tr_strlpercent(buf, + priv->metadataPercentComplete, + sizeof(buf))); + } + } else if (priv->flags & TORRENT_FLAG_SEEDING) { + g_string_append_printf(gstr, + ngettext + ("Seeding to %1$li of %2$li connected peer", + "Seeding to %1$li of %2$li connected peers", + priv->connected), priv->peersFromUs, + priv->connected); + } + + if ((priv->flags & ~TORRENT_FLAG_WAITING_CHECK) && + (priv->flags & ~TORRENT_FLAG_CHECKING) && + (priv->flags & ~TORRENT_FLAG_DOWNLOADING_WAIT) && + (priv->flags & ~TORRENT_FLAG_SEEDING_WAIT) && + (priv->flags & ~TORRENT_FLAG_PAUSED)) { + getShortTransferString(r, buf, sizeof(buf)); + if (*buf) + g_string_append_printf(gstr, " - %s", buf); + } +} + +/*** +**** +***/ + +static GdkPixbuf *get_icon(TorrentCellRenderer * r, GtkIconSize icon_size, + GtkWidget * for_widget) +{ + struct TorrentCellRendererPrivate *p = r->priv; + + const char *mime_type; + + if (p->fileCount == 0) + mime_type = UNKNOWN_MIME_TYPE; + else if (p->fileCount > 1) + mime_type = DIRECTORY_MIME_TYPE; + /*else if( strchr( info->files[0].name, '/' ) != NULL ) + mime_type = DIRECTORY_MIME_TYPE; + else + mime_type = gtr_get_mime_type_from_filename( info->files[0].name ); */ + else + mime_type = FILE_MIME_TYPE; + + //return NULL; + return gtr_get_mime_type_icon(mime_type, icon_size, for_widget); +} + +/*** +**** +***/ + +static void +gtr_cell_renderer_get_preferred_size(GtkCellRenderer * renderer, + GtkWidget * widget, + GtkRequisition * minimum_size, + GtkRequisition * natural_size) +{ +#if GTK_CHECK_VERSION( 3, 0, 0 ) + gtk_cell_renderer_get_preferred_size(renderer, widget, minimum_size, + natural_size); +#else + GtkRequisition r; + gtk_cell_renderer_get_size(renderer, widget, NULL, NULL, NULL, + &r.width, &r.height); + if (minimum_size) + *minimum_size = r; + if (natural_size) + *natural_size = r; +#endif +} + +static void +get_size_compact(TorrentCellRenderer * cell, + GtkWidget * widget, gint * width, gint * height) +{ + int xpad, ypad; + GtkRequisition icon_size; + GtkRequisition name_size; + GtkRequisition stat_size; + GdkPixbuf *icon; + + struct TorrentCellRendererPrivate *p = cell->priv; + GString *gstr_stat = p->gstr1; + + icon = get_icon(cell, COMPACT_ICON_SIZE, widget); + g_string_truncate(gstr_stat, 0); + getShortStatusString(gstr_stat, cell); + gtk_cell_renderer_get_padding(GTK_CELL_RENDERER(cell), &xpad, &ypad); + + /* get the idealized cell dimensions */ + g_object_set(p->icon_renderer, "pixbuf", icon, NULL); + gtr_cell_renderer_get_preferred_size(p->icon_renderer, widget, NULL, + &icon_size); + g_object_set(p->text_renderer, "text", torrent_get_name(p->json), + "ellipsize", PANGO_ELLIPSIZE_NONE, "scale", 1.0, NULL); + gtr_cell_renderer_get_preferred_size(p->text_renderer, widget, NULL, + &name_size); + g_object_set(p->text_renderer, "text", gstr_stat->str, "scale", + SMALL_SCALE, NULL); + gtr_cell_renderer_get_preferred_size(p->text_renderer, widget, NULL, + &stat_size); + + /** + *** LAYOUT + **/ + +#define BAR_WIDTH 50 + if (width != NULL) + *width = + xpad * 2 + icon_size.width + GUI_PAD + name_size.width + + GUI_PAD + BAR_WIDTH + GUI_PAD + stat_size.width; + if (height != NULL) + *height = ypad * 2 + MAX(name_size.height, p->bar_height); + + /* cleanup */ + g_object_unref(icon); +} + +#define MAX3(a,b,c) MAX(a,MAX(b,c)) + +static void +get_size_full(TorrentCellRenderer * cell, + GtkWidget * widget, gint * width, gint * height) +{ + int xpad, ypad; + GtkRequisition icon_size; + GtkRequisition name_size; + GtkRequisition stat_size; + GtkRequisition prog_size; + GdkPixbuf *icon; + + struct TorrentCellRendererPrivate *p = cell->priv; + GString *gstr_prog = p->gstr1; + GString *gstr_stat = p->gstr2; + + icon = get_icon(cell, FULL_ICON_SIZE, widget); + + g_string_truncate(gstr_stat, 0); + getStatusString(gstr_stat, cell); + g_string_truncate(gstr_prog, 0); + getProgressString(gstr_prog, cell); + gtk_cell_renderer_get_padding(GTK_CELL_RENDERER(cell), &xpad, &ypad); + + /* get the idealized cell dimensions */ + g_object_set(p->icon_renderer, "pixbuf", icon, NULL); + gtr_cell_renderer_get_preferred_size(p->icon_renderer, widget, NULL, + &icon_size); + g_object_set(p->text_renderer, "text", torrent_get_name(p->json), + "weight", PANGO_WEIGHT_BOLD, "scale", 1.0, "ellipsize", + PANGO_ELLIPSIZE_NONE, NULL); + gtr_cell_renderer_get_preferred_size(p->text_renderer, widget, NULL, + &name_size); + g_object_set(p->text_renderer, "text", gstr_prog->str, "weight", + PANGO_WEIGHT_NORMAL, "scale", SMALL_SCALE, NULL); + gtr_cell_renderer_get_preferred_size(p->text_renderer, widget, NULL, + &prog_size); + g_object_set(p->text_renderer, "text", gstr_stat->str, NULL); + gtr_cell_renderer_get_preferred_size(p->text_renderer, widget, NULL, + &stat_size); + + /** + *** LAYOUT + **/ + + if (width != NULL) + *width = + xpad * 2 + icon_size.width + GUI_PAD + MAX3(name_size.width, + prog_size.width, + stat_size.width); + if (height != NULL) + *height = + ypad * 2 + name_size.height + prog_size.height + + GUI_PAD_SMALL + p->bar_height + GUI_PAD_SMALL + + stat_size.height; + + /* cleanup */ + g_object_unref(icon); +} + + +static void +torrent_cell_renderer_get_size(GtkCellRenderer * cell, GtkWidget * widget, +#if GTK_CHECK_VERSION( 3,0,0 ) + const GdkRectangle * cell_area, +#else + GdkRectangle * cell_area, +#endif + gint * x_offset, + gint * y_offset, + gint * width, gint * height) +{ + TorrentCellRenderer *self = TORRENT_CELL_RENDERER(cell); + + if (self) { + int w, h; + struct TorrentCellRendererPrivate *p = self->priv; + + if (p->compact) + get_size_compact(TORRENT_CELL_RENDERER(cell), widget, &w, &h); + else + get_size_full(TORRENT_CELL_RENDERER(cell), widget, &w, &h); + + if (width) + *width = w; + + if (height) + *height = h; + + if (x_offset) + *x_offset = cell_area ? cell_area->x : 0; + + if (y_offset) { + int xpad, ypad; + gtk_cell_renderer_get_padding(cell, &xpad, &ypad); + *y_offset = + cell_area ? (int) ((cell_area->height - (ypad * 2 + h)) / + 2.0) : 0; + } + } +} + +static void +get_text_color(TorrentCellRenderer * r, GtkWidget * widget, + GtrColor * setme) +{ + struct TorrentCellRendererPrivate *p = r->priv; +#if GTK_CHECK_VERSION( 3,0,0 ) + static const GdkRGBA red = { 1.0, 0, 0, 0 }; + + if (p->error) + *setme = red; + else if (p->flags & TORRENT_FLAG_PAUSED) + gtk_style_context_get_color(gtk_widget_get_style_context(widget), + GTK_STATE_FLAG_INSENSITIVE, setme); + else + gtk_style_context_get_color(gtk_widget_get_style_context(widget), + GTK_STATE_FLAG_NORMAL, setme); +#else + static const GdkColor red = { 0, 65535, 0, 0 }; + + if (p->error) + *setme = red; + else if (p->flags & TORRENT_FLAG_PAUSED) + *setme = gtk_widget_get_style(widget)->text[GTK_STATE_INSENSITIVE]; + else + *setme = gtk_widget_get_style(widget)->text[GTK_STATE_NORMAL]; +#endif +} + +static double get_percent_done(TorrentCellRenderer * r, gboolean * seed) +{ + struct TorrentCellRendererPrivate *priv = r->priv; + gdouble d; + + if ((priv->flags & TORRENT_FLAG_SEEDING) && getSeedRatio(r, &d)) { + *seed = TRUE; + const gint64 baseline = + priv->downloaded ? priv->downloaded : priv->sizeWhenDone; + const gint64 goal = baseline * priv->seedRatioLimit; + const gint64 bytesLeft = + goal > priv->uploadedEver ? goal - priv->uploadedEver : 0; + float seedRatioPercentDone = (gdouble) (goal - bytesLeft) / goal; + + d = MAX(0.0, seedRatioPercentDone * 100.0); + } else { + *seed = FALSE; + d = MAX(0.0, priv->done); + } + + return d; +} + +static void +gtr_cell_renderer_render(GtkCellRenderer * renderer, + GtrDrawable * drawable, + GtkWidget * widget, + const GdkRectangle * area, + GtkCellRendererState flags) +{ +#if GTK_CHECK_VERSION( 3, 0, 0 ) + gtk_cell_renderer_render(renderer, drawable, widget, area, area, + flags); +#else + gtk_cell_renderer_render(renderer, drawable, widget, area, area, area, + flags); +#endif +} + +static void +torrent_cell_renderer_render(GtkCellRenderer * cell, + GtrDrawable * window, GtkWidget * widget, +#if GTK_CHECK_VERSION( 3,0,0 ) + const GdkRectangle * background_area, + const GdkRectangle * cell_area, +#else + GdkRectangle * background_area, + GdkRectangle * cell_area, + GdkRectangle * expose_area, +#endif + GtkCellRendererState flags) +{ + TorrentCellRenderer *self = TORRENT_CELL_RENDERER(cell); + + if (self) { + struct TorrentCellRendererPrivate *p = self->priv; + if (p->compact) + render_compact(self, window, widget, background_area, + cell_area, flags); + else + render_full(self, window, widget, background_area, cell_area, + flags); + } +} + +static void torrent_cell_renderer_set_property(GObject * object, + guint property_id, + const GValue * v, + GParamSpec * pspec) +{ + TorrentCellRenderer *self = TORRENT_CELL_RENDERER(object); + struct TorrentCellRendererPrivate *p = self->priv; + + switch (property_id) { + case P_JSON: + p->json = g_value_get_pointer(v); + break; + case P_STATUS: + p->flags = g_value_get_uint(v); + break; + case P_TOTALSIZE: + p->totalSize = g_value_get_int64(v); + break; + case P_SIZEWHENDONE: + p->sizeWhenDone = g_value_get_int64(v); + break; + case P_DOWNLOADED: + p->downloaded = g_value_get_int64(v); + break; + case P_HAVEVALID: + p->haveValid = g_value_get_int64(v); + break; + case P_HAVEUNCHECKED: + p->haveUnchecked = g_value_get_int64(v); + break; + case P_UPLOADED: + p->uploadedEver = g_value_get_int64(v); + break; + case P_UPSPEED: + p->upSpeed = g_value_get_int64(v); + break; + case P_DOWNSPEED: + p->downSpeed = g_value_get_int64(v); + break; + case P_PEERSGETTINGFROMUS: + p->peersFromUs = g_value_get_int64(v); + break; + case P_WEBSEEDSTOUS: + p->webSeedsToUs = g_value_get_int64(v); + break; + case P_CONNECTED: + p->connected = g_value_get_int64(v); + break; + case P_FILECOUNT: + p->fileCount = g_value_get_uint(v); + break; + case P_ETA: + p->eta = g_value_get_int64(v); + break; + case P_PEERSTOUS: + p->peersToUs = g_value_get_int64(v); + break; + case P_ERROR: + p->error = g_value_get_int64(v); + break; + case P_RATIO: + p->ratio = g_value_get_double(v); + break; + case P_PERCENTCOMPLETE: + p->done = g_value_get_double(v); + break; + case P_METADATAPERCENTCOMPLETE: + p->metadataPercentComplete = g_value_get_double(v); + break; + case P_BAR_HEIGHT: + p->bar_height = g_value_get_int(v); + break; + case P_COMPACT: + p->compact = g_value_get_boolean(v); + break; + case P_SEEDRATIOMODE: + p->seedRatioMode = g_value_get_int64(v); + break; + case P_SEEDRATIOLIMIT: + p->seedRatioLimit = g_value_get_double(v); + break; + case P_CLIENT: + p->client = g_value_get_pointer(v); + break; + case P_OWNER: + p->owner = g_value_get_pointer(v); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + break; + } +} + +static void +torrent_cell_renderer_get_property(GObject * object, + guint property_id, + GValue * v, GParamSpec * pspec) +{ + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); +} + +G_DEFINE_TYPE(TorrentCellRenderer, torrent_cell_renderer, + GTK_TYPE_CELL_RENDERER) + +static void torrent_cell_renderer_dispose(GObject * o) +{ + TorrentCellRenderer *r = TORRENT_CELL_RENDERER(o); + + if (r && r->priv) { + g_string_free(r->priv->gstr1, TRUE); + g_string_free(r->priv->gstr2, TRUE); + g_object_unref(G_OBJECT(r->priv->text_renderer)); + g_object_unref(G_OBJECT(r->priv->progress_renderer)); + g_object_unref(G_OBJECT(r->priv->icon_renderer)); + r->priv = NULL; + } + + G_OBJECT_CLASS(torrent_cell_renderer_parent_class)->dispose(o); +} + +static void +torrent_cell_renderer_class_init(TorrentCellRendererClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS(klass); + + g_type_class_add_private(klass, + sizeof(struct TorrentCellRendererPrivate)); + + cell_class->render = torrent_cell_renderer_render; + cell_class->get_size = torrent_cell_renderer_get_size; + gobject_class->set_property = torrent_cell_renderer_set_property; + gobject_class->get_property = torrent_cell_renderer_get_property; + gobject_class->dispose = torrent_cell_renderer_dispose; + + g_object_class_install_property(gobject_class, P_JSON, + g_param_spec_pointer("json", NULL, + "json", + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, P_CLIENT, + g_param_spec_pointer("client", NULL, + "client", + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, P_OWNER, + g_param_spec_pointer("owner", NULL, + "owner", + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, P_RATIO, + g_param_spec_double("ratio", NULL, + "ratio", + 0, G_MAXDOUBLE, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, P_SEEDRATIOLIMIT, + g_param_spec_double("seedRatioLimit", + NULL, + "seedRatioLimit", + 0, G_MAXDOUBLE, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, P_SEEDRATIOMODE, + g_param_spec_int64("seedRatioMode", + NULL, + "seedRatioMode", 0, + 2, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, P_PERCENTCOMPLETE, + g_param_spec_double("percentComplete", + NULL, + "percentComplete", + 0, 100.00, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, + P_METADATAPERCENTCOMPLETE, + g_param_spec_double + ("metadataPercentComplete", NULL, + "metadataPercentComplete", 0, 100.00, + 0, G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, P_TOTALSIZE, + g_param_spec_int64("totalSize", NULL, + "totalSize", + 0, G_MAXINT64, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, P_SIZEWHENDONE, + g_param_spec_int64("sizeWhenDone", + NULL, + "sizeWhenDone", 0, + G_MAXINT64, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, P_STATUS, + g_param_spec_uint("status", NULL, + "status", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, P_FILECOUNT, + g_param_spec_uint("fileCount", NULL, + "fileCount", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, P_ERROR, + g_param_spec_int64("error", NULL, + "error", + 0, G_MAXINT64, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, P_UPSPEED, + g_param_spec_int64("upSpeed", NULL, + "upSpeed", + 0, G_MAXINT64, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, P_DOWNSPEED, + g_param_spec_int64("downSpeed", NULL, + "downSpeed", + 0, G_MAXINT64, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, P_DOWNLOADED, + g_param_spec_int64("downloaded", NULL, + "downloaded", + 0, G_MAXINT64, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, P_HAVEVALID, + g_param_spec_int64("haveValid", NULL, + "haveValid", + 0, G_MAXINT64, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, P_HAVEUNCHECKED, + g_param_spec_int64("haveUnchecked", + NULL, + "haveUnchecked", 0, + G_MAXINT64, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, P_UPLOADED, + g_param_spec_int64("uploaded", NULL, + "uploaded", + 0, G_MAXINT64, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, P_PEERSGETTINGFROMUS, + g_param_spec_int64 + ("peersGettingFromUs", NULL, + "peersGettingFromUs", -1, G_MAXINT64, + 0, G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, P_PEERSTOUS, + g_param_spec_int64("peersToUs", NULL, + "peersToUs", + -1, G_MAXINT64, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, P_WEBSEEDSTOUS, + g_param_spec_int64("webSeedsToUs", + NULL, + "webSeedsToUs", 0, + G_MAXINT64, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, P_ETA, + g_param_spec_int64("eta", NULL, + "eta", + -2, G_MAXINT64, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, P_CONNECTED, + g_param_spec_int64("connected", NULL, + "connected", + 0, G_MAXINT64, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, P_BAR_HEIGHT, + g_param_spec_int("bar-height", NULL, + "Bar Height", + 1, INT_MAX, + DEFAULT_BAR_HEIGHT, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, P_COMPACT, + g_param_spec_boolean("compact", NULL, + "Compact Mode", + FALSE, + G_PARAM_READWRITE)); +} + +static void torrent_cell_renderer_init(TorrentCellRenderer * self) +{ + struct TorrentCellRendererPrivate *p; + + p = self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, + TORRENT_CELL_RENDERER_TYPE, + struct + TorrentCellRendererPrivate); + + p->gstr1 = g_string_new(NULL); + p->gstr2 = g_string_new(NULL); + p->text_renderer = gtk_cell_renderer_text_new(); + g_object_set(p->text_renderer, "xpad", 0, "ypad", 0, NULL); + p->progress_renderer = gtk_cell_renderer_progress_new(); + p->icon_renderer = gtk_cell_renderer_pixbuf_new(); + g_object_ref_sink(p->text_renderer); + g_object_ref_sink(p->progress_renderer); + g_object_ref_sink(p->icon_renderer); + + p->bar_height = DEFAULT_BAR_HEIGHT; +} + + +GtkCellRenderer *torrent_cell_renderer_new(void) +{ + return (GtkCellRenderer *) g_object_new(TORRENT_CELL_RENDERER_TYPE, + NULL); +} + +static void +render_compact(TorrentCellRenderer * cell, + GtrDrawable * window, + GtkWidget * widget, + const GdkRectangle * background_area, + const GdkRectangle * cell_area, GtkCellRendererState flags) +{ + int xpad, ypad; + GtkRequisition size; + GdkRectangle icon_area; + GdkRectangle name_area; + GdkRectangle stat_area; + GdkRectangle prog_area; + GdkRectangle fill_area; + GdkPixbuf *icon; + GtrColor text_color; + gboolean seed; + + struct TorrentCellRendererPrivate *p = cell->priv; + const gboolean active = (p->flags & ~TORRENT_FLAG_PAUSED) + && (p->flags & ~TORRENT_FLAG_DOWNLOADING_WAIT) + && (p->flags & ~TORRENT_FLAG_SEEDING_WAIT); + const double percentDone = get_percent_done(cell, &seed); + const gboolean sensitive = active || p->error; + GString *gstr_stat = p->gstr1; + + icon = get_icon(cell, COMPACT_ICON_SIZE, widget); + + g_string_truncate(gstr_stat, 0); + getShortStatusString(gstr_stat, cell); + gtk_cell_renderer_get_padding(GTK_CELL_RENDERER(cell), &xpad, &ypad); + get_text_color(cell, widget, &text_color); + + fill_area = *background_area; + fill_area.x += xpad; + fill_area.y += ypad; + fill_area.width -= xpad * 2; + fill_area.height -= ypad * 2; + icon_area = name_area = stat_area = prog_area = fill_area; + + g_object_set(p->icon_renderer, "pixbuf", icon, NULL); + gtr_cell_renderer_get_preferred_size(p->icon_renderer, widget, NULL, + &size); + icon_area.width = size.width; + g_object_set(p->text_renderer, "text", torrent_get_name(p->json), + "ellipsize", PANGO_ELLIPSIZE_NONE, "scale", 1.0, NULL); + gtr_cell_renderer_get_preferred_size(p->text_renderer, widget, NULL, + &size); + name_area.width = size.width; + g_object_set(p->text_renderer, "text", gstr_stat->str, "scale", + SMALL_SCALE, NULL); + gtr_cell_renderer_get_preferred_size(p->text_renderer, widget, NULL, + &size); + stat_area.width = size.width; + + icon_area.x = fill_area.x; + prog_area.x = fill_area.x + fill_area.width - BAR_WIDTH; + prog_area.width = BAR_WIDTH; + stat_area.x = prog_area.x - GUI_PAD - stat_area.width; + name_area.x = icon_area.x + icon_area.width + GUI_PAD; + name_area.y = fill_area.y; + name_area.width = stat_area.x - GUI_PAD - name_area.x; + + /** + *** RENDER + **/ + + g_object_set(p->icon_renderer, "pixbuf", icon, "sensitive", sensitive, + NULL); + gtr_cell_renderer_render(p->icon_renderer, window, widget, &icon_area, + flags); + g_object_set(p->progress_renderer, "value", (gint) percentDone, "text", + NULL, "sensitive", sensitive, NULL); + gtr_cell_renderer_render(p->progress_renderer, window, widget, + &prog_area, flags); + g_object_set(p->text_renderer, "text", gstr_stat->str, "scale", + SMALL_SCALE, "ellipsize", PANGO_ELLIPSIZE_END, + FOREGROUND_COLOR_KEY, &text_color, NULL); + gtr_cell_renderer_render(p->text_renderer, window, widget, &stat_area, + flags); + g_object_set(p->text_renderer, "text", torrent_get_name(p->json), + "scale", 1.0, FOREGROUND_COLOR_KEY, &text_color, NULL); + gtr_cell_renderer_render(p->text_renderer, window, widget, &name_area, + flags); + + /* cleanup */ + g_object_unref(icon); +} + +static void +render_full(TorrentCellRenderer * cell, + GtrDrawable * window, + GtkWidget * widget, + const GdkRectangle * background_area, + const GdkRectangle * cell_area, GtkCellRendererState flags) +{ + int xpad, ypad; + GtkRequisition size; + GdkRectangle fill_area; + GdkRectangle icon_area; + GdkRectangle name_area; + GdkRectangle stat_area; + GdkRectangle prog_area; + GdkRectangle prct_area; + GdkPixbuf *icon; + GtrColor text_color; + gboolean seed; + + struct TorrentCellRendererPrivate *p = cell->priv; + const gboolean active = (p->flags & ~TORRENT_FLAG_PAUSED) + && (p->flags & ~TORRENT_FLAG_DOWNLOADING_WAIT) + && (p->flags & ~TORRENT_FLAG_SEEDING_WAIT); + const gboolean sensitive = active || p->error; + const double percentDone = get_percent_done(cell, &seed); + GString *gstr_prog = p->gstr1; + GString *gstr_stat = p->gstr2; + + icon = get_icon(cell, FULL_ICON_SIZE, widget); + g_string_truncate(gstr_prog, 0); + getProgressString(gstr_prog, cell); + g_string_truncate(gstr_stat, 0); + getStatusString(gstr_stat, cell); + gtk_cell_renderer_get_padding(GTK_CELL_RENDERER(cell), &xpad, &ypad); + get_text_color(cell, widget, &text_color); + + /* get the idealized cell dimensions */ + g_object_set(p->icon_renderer, "pixbuf", icon, NULL); + gtr_cell_renderer_get_preferred_size(p->icon_renderer, widget, NULL, + &size); + icon_area.width = size.width; + icon_area.height = size.height; + g_object_set(p->text_renderer, "text", torrent_get_name(p->json), + "weight", PANGO_WEIGHT_BOLD, "ellipsize", + PANGO_ELLIPSIZE_NONE, "scale", 1.0, NULL); + gtr_cell_renderer_get_preferred_size(p->text_renderer, widget, NULL, + &size); + name_area.width = size.width; + name_area.height = size.height; + g_object_set(p->text_renderer, "text", gstr_prog->str, "weight", + PANGO_WEIGHT_NORMAL, "scale", SMALL_SCALE, NULL); + gtr_cell_renderer_get_preferred_size(p->text_renderer, widget, NULL, + &size); + prog_area.width = size.width; + prog_area.height = size.height; + g_object_set(p->text_renderer, "text", gstr_stat->str, NULL); + gtr_cell_renderer_get_preferred_size(p->text_renderer, widget, NULL, + &size); + stat_area.width = size.width; + stat_area.height = size.height; + + /** + *** LAYOUT + **/ + + fill_area = *background_area; + fill_area.x += xpad; + fill_area.y += ypad; + fill_area.width -= xpad * 2; + fill_area.height -= ypad * 2; + + /* icon */ + icon_area.x = fill_area.x; + icon_area.y = fill_area.y + (fill_area.height - icon_area.height) / 2; + + /* name */ + name_area.x = icon_area.x + icon_area.width + GUI_PAD; + name_area.y = fill_area.y; + name_area.width = + fill_area.width - GUI_PAD - icon_area.width - GUI_PAD_SMALL; + + /* prog */ + prog_area.x = name_area.x; + prog_area.y = name_area.y + name_area.height; + prog_area.width = name_area.width; + + /* progressbar */ + prct_area.x = prog_area.x; + prct_area.y = prog_area.y + prog_area.height + GUI_PAD_SMALL; + prct_area.width = prog_area.width; + prct_area.height = p->bar_height; + + /* status */ + stat_area.x = prct_area.x; + stat_area.y = prct_area.y + prct_area.height + GUI_PAD_SMALL; + stat_area.width = prct_area.width; + + /** + *** RENDER + **/ + + g_object_set(p->icon_renderer, "pixbuf", icon, "sensitive", sensitive, + NULL); + gtr_cell_renderer_render(p->icon_renderer, window, widget, &icon_area, + flags); + g_object_set(p->text_renderer, "text", torrent_get_name(p->json), + "scale", 1.0, FOREGROUND_COLOR_KEY, &text_color, + "ellipsize", PANGO_ELLIPSIZE_END, "weight", + PANGO_WEIGHT_BOLD, NULL); + gtr_cell_renderer_render(p->text_renderer, window, widget, &name_area, + flags); + g_object_set(p->text_renderer, "text", gstr_prog->str, "scale", + SMALL_SCALE, "weight", PANGO_WEIGHT_NORMAL, NULL); + gtr_cell_renderer_render(p->text_renderer, window, widget, &prog_area, + flags); + g_object_set(p->progress_renderer, "value", (gint) percentDone, + "text", "", "sensitive", sensitive, NULL); + gtr_cell_renderer_render(p->progress_renderer, window, widget, + &prct_area, flags); + g_object_set(p->text_renderer, "text", gstr_stat->str, + FOREGROUND_COLOR_KEY, &text_color, NULL); + gtr_cell_renderer_render(p->text_renderer, window, widget, &stat_area, + flags); + + /* cleanup */ + g_object_unref(icon); +} + +GtkTreeView *torrent_cell_renderer_get_owner(TorrentCellRenderer * r) +{ + return r->priv->owner; +} -- cgit v1.2.3