/* * 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #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 #define FOREGROUND_COLOR_KEY "foreground-rgba" typedef GdkRGBA GtrColor; typedef cairo_t GtrDrawable; typedef GtkRequisition GtrRequisition; /*** **** ***/ 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$"G_GUINT64_FORMAT" of %2$"G_GUINT64_FORMAT" connected peer", "Downloading from %1$"G_GUINT64_FORMAT" of %2$"G_GUINT64_FORMAT" 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$"G_GUINT64_FORMAT" peer (%2$s done)", "Downloading metadata from %1$"G_GUINT64_FORMAT" 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$"G_GUINT64_FORMAT" of %2$"G_GUINT64_FORMAT" connected peer", "Seeding to %1$"G_GUINT64_FORMAT" of %2$"G_GUINT64_FORMAT" 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) { gtk_cell_renderer_get_preferred_size(renderer, widget, minimum_size, natural_size); } 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); } 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, const GdkRectangle * cell_area, 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; 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); } 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) { gtk_cell_renderer_render(renderer, drawable, widget, area, area, flags); } static void torrent_cell_renderer_render(GtkCellRenderer * cell, GtrDrawable * window, GtkWidget * widget, const GdkRectangle * background_area, const GdkRectangle * cell_area, 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; }