summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Alan Fitton <ajf@eth0.org.uk>2011-01-30 10:57:07 +0000
committerGravatar Alan Fitton <ajf@eth0.org.uk>2011-01-30 10:57:07 +0000
commiteca35c468094fc6b7177f33ef51fa873eb88e79c (patch)
treefa07fd0ea1db72ff2ad4bd8b04b8e546717f2731 /src
hello world!
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am28
-rw-r--r--src/base64.c66
-rw-r--r--src/base64.h25
-rw-r--r--src/dispatch.c109
-rw-r--r--src/dispatch.h40
-rw-r--r--src/hig.c208
-rw-r--r--src/hig.h85
-rw-r--r--src/http.c145
-rw-r--r--src/http.h43
-rw-r--r--src/json.c93
-rw-r--r--src/json.h34
-rw-r--r--src/main.c135
-rw-r--r--src/protocol-constants.h115
-rw-r--r--src/requests.c155
-rw-r--r--src/requests.h39
-rw-r--r--src/session-get.c161
-rw-r--r--src/session-get.h89
-rw-r--r--src/tfile.c44
-rw-r--r--src/tfile.h31
-rw-r--r--src/torrent.c234
-rw-r--r--src/torrent.h71
-rw-r--r--src/tpeer.c71
-rw-r--r--src/tpeer.h47
-rw-r--r--src/transmission-remote-gtk.desktop12
-rw-r--r--src/transmission-remote-gtk.desktop.in12
-rw-r--r--src/transmission-remote-gtk.schemas109
-rw-r--r--src/trg-about-window.c82
-rw-r--r--src/trg-about-window.h27
-rw-r--r--src/trg-cell-renderer-eta.c116
-rw-r--r--src/trg-cell-renderer-eta.h51
-rw-r--r--src/trg-cell-renderer-priority.c115
-rw-r--r--src/trg-cell-renderer-priority.h52
-rw-r--r--src/trg-cell-renderer-ratio.c113
-rw-r--r--src/trg-cell-renderer-ratio.h51
-rw-r--r--src/trg-cell-renderer-size.c112
-rw-r--r--src/trg-cell-renderer-size.h52
-rw-r--r--src/trg-cell-renderer-speed.c114
-rw-r--r--src/trg-cell-renderer-speed.h52
-rw-r--r--src/trg-cell-renderer-wanted.c105
-rw-r--r--src/trg-cell-renderer-wanted.h52
-rw-r--r--src/trg-client.c80
-rw-r--r--src/trg-client.h45
-rw-r--r--src/trg-files-model.c177
-rw-r--r--src/trg-files-model.h74
-rw-r--r--src/trg-files-tree-view.c369
-rw-r--r--src/trg-files-tree-view.h56
-rw-r--r--src/trg-general-panel.c209
-rw-r--r--src/trg-general-panel.h59
-rw-r--r--src/trg-json-widgets.c71
-rw-r--r--src/trg-json-widgets.h40
-rw-r--r--src/trg-main-window.c1449
-rw-r--r--src/trg-main-window.h71
-rw-r--r--src/trg-menu-bar.c374
-rw-r--r--src/trg-menu-bar.h57
-rw-r--r--src/trg-model.c65
-rw-r--r--src/trg-model.h29
-rw-r--r--src/trg-peers-model.c225
-rw-r--r--src/trg-peers-model.h85
-rw-r--r--src/trg-peers-tree-view.c69
-rw-r--r--src/trg-peers-tree-view.h54
-rw-r--r--src/trg-preferences-dialog.c327
-rw-r--r--src/trg-preferences-dialog.h56
-rw-r--r--src/trg-preferences.h32
-rw-r--r--src/trg-remote-prefs-dialog.c541
-rw-r--r--src/trg-remote-prefs-dialog.h57
-rw-r--r--src/trg-state-selector.c161
-rw-r--r--src/trg-state-selector.h63
-rw-r--r--src/trg-status-bar.c130
-rw-r--r--src/trg-status-bar.h60
-rw-r--r--src/trg-toolbar.c267
-rw-r--r--src/trg-toolbar.h56
-rw-r--r--src/trg-torrent-add-url-dialog.c129
-rw-r--r--src/trg-torrent-add-url-dialog.h57
-rw-r--r--src/trg-torrent-model.c297
-rw-r--r--src/trg-torrent-model.h102
-rw-r--r--src/trg-torrent-props-dialog.c372
-rw-r--r--src/trg-torrent-props-dialog.h57
-rw-r--r--src/trg-torrent-tree-view.c122
-rw-r--r--src/trg-torrent-tree-view.h58
-rw-r--r--src/trg-trackers-model.c72
-rw-r--r--src/trg-trackers-model.h62
-rw-r--r--src/trg-trackers-tree-view.c51
-rw-r--r--src/trg-trackers-tree-view.h54
-rw-r--r--src/trg-tree-view.c178
-rw-r--r--src/trg-tree-view.h74
-rw-r--r--src/util.c261
-rw-r--r--src/util.h56
87 files changed, 10705 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..34fb0d1
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,28 @@
+EXTRA_DIST = transmission-remote-gtk.desktop.in
+CLEANFILES = transmission-remote-gtk.desktop
+
+desktopdir = $(datadir)/applications
+desktop_DATA = transmission-remote-gtk.desktop
+
+schemadir = @GCONF_SCHEMA_FILE_DIR@
+schema_DATA = transmission-remote-gtk.schemas
+
+bin_PROGRAMS = transmission-remote-gtk
+INCLUDES = --pedantic -Wall -I.. -O2 $(jsonglib_CFLAGS) $(gthread_CFLAGS) $(gtk_CFLAGS) $(gconf_CFLAGS) $(gio_CFLAGS) $(unique_CFLAGS) $(notify_CFLAGS)
+
+transmission_remote_gtk_SOURCES = main.c requests.c base64.c json.c http.c dispatch.c trg-main-window.c util.c trg-about-window.c torrent.c \
+ tpeer.c tfile.c session-get.c trg-client.c trg-preferences-dialog.c hig.c trg-torrent-tree-view.c \
+ trg-tree-view.c trg-torrent-model.c trg-peers-model.c trg-peers-tree-view.c trg-model.c trg-files-model.c \
+ trg-files-tree-view.c trg-state-selector.c trg-general-panel.c trg-toolbar.c trg-menu-bar.c \
+ trg-status-bar.c trg-trackers-tree-view.c trg-trackers-model.c trg-torrent-props-dialog.c \
+ trg-json-widgets.c trg-torrent-add-url-dialog.c trg-cell-renderer-speed.c trg-cell-renderer-size.c \
+ trg-cell-renderer-ratio.c trg-cell-renderer-eta.c trg-remote-prefs-dialog.c trg-cell-renderer-wanted.c \
+ trg-cell-renderer-priority.c
+
+transmission_remote_gtk_LDFLAGS = -lcurl $(jsonglib_LIBS) $(gtk_LIBS) $(gthread_LIBS) $(GEOIP_LIBS) $(gconf_LIBS) $(gio_LIBS) $(unique_LIBS) $(notify_LIBS)
+
+install-data-local:
+ GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) gconftool-2 --makefile-install-rule $(srcdir)/$(schema_DATA)
+
+transmission-remote-gtk.desktop: transmission-remote-gtk.desktop.in
+ sed -e 's,@bindir\@,$(bindir),g' $< > $@
diff --git a/src/base64.c b/src/base64.c
new file mode 100644
index 0000000..fbf07cb
--- /dev/null
+++ b/src/base64.c
@@ -0,0 +1,66 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "base64.h"
+
+static const char cb64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+void encodeblock(unsigned char in[3], unsigned char out[4], int len)
+{
+ out[0] = cb64[in[0] >> 2];
+ out[1] = cb64[((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4)];
+ out[2] =
+ (unsigned char) (len >
+ 1 ? cb64[((in[1] & 0x0f) << 2) |
+ ((in[2] & 0xc0) >> 6)] : '=');
+ out[3] = (unsigned char) (len > 2 ? cb64[in[2] & 0x3f] : '=');
+}
+
+char *base64encode(char *filename)
+{
+ FILE *infile = fopen(filename, "rb");
+ unsigned char in[3], out[4];
+ int i, len, j = 0;
+ char *output = NULL;
+ while (!feof(infile)) {
+ len = 0;
+ for (i = 0; i < 3; i++) {
+ in[i] = (unsigned char) getc(infile);
+ if (!feof(infile)) {
+ len++;
+ } else {
+ in[i] = 0;
+ }
+ }
+ if (len) {
+ output = (char *) realloc(output, j + 5);
+ encodeblock(in, out, len);
+ for (i = 0; i < 4; i++) {
+ output[j++] = out[i];
+ }
+ }
+ }
+ output[j] = '\0';
+ fclose(infile);
+ return output;
+}
diff --git a/src/base64.h b/src/base64.h
new file mode 100644
index 0000000..45cef3d
--- /dev/null
+++ b/src/base64.h
@@ -0,0 +1,25 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef BASE64_H_
+#define BASE64_H_
+
+char *base64encode(char *filename);
+
+#endif /* BASE64_H_ */
diff --git a/src/dispatch.c b/src/dispatch.c
new file mode 100644
index 0000000..8967ded
--- /dev/null
+++ b/src/dispatch.c
@@ -0,0 +1,109 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <json-glib/json-glib.h>
+#include <curl/curl.h>
+
+#include "dispatch.h"
+#include "http.h"
+#include "json.h"
+
+static gpointer dispatch_async_threadfunc(gpointer ptr);
+
+JsonObject *dispatch(trg_client * client, JsonNode * req, int *status)
+{
+ gchar *serialized;
+ struct http_response *response;
+ JsonObject *deserialized;
+ JsonNode *result;
+ GError *decode_error = NULL;
+
+ serialized = trg_serialize(req);
+ json_node_free(req);
+
+ g_printf("=>(outgoing)=> %s\n", serialized);
+ response = trg_http_perform(client, serialized);
+ g_free(serialized);
+
+ if (status != NULL)
+ *status = response->status;
+
+ if (response->status != CURLE_OK) {
+ http_response_free(response);
+ return NULL;
+ }
+
+ deserialized = trg_deserialize(response, &decode_error);
+ http_response_free(response);
+
+ if (decode_error != NULL) {
+ g_printf("JSON decoding error: %s\n", decode_error->message);
+ g_error_free(decode_error);
+ if (status != NULL)
+ *status = FAIL_JSON_DECODE;
+ return NULL;
+ }
+
+ result = json_object_get_member(deserialized, "result");
+ if (status != NULL
+ && (result == NULL
+ || g_strcmp0(json_node_get_string(result), "success") != 0))
+ *status = FAIL_RESPONSE_UNSUCCESSFUL;
+
+ return deserialized;
+}
+
+static gpointer dispatch_async_threadfunc(gpointer ptr)
+{
+ struct dispatch_async_args *args = (struct dispatch_async_args *) ptr;
+ int status;
+ JsonObject *result = dispatch(args->client, args->req, &status);
+ if (args->callback != NULL)
+ args->callback(result, status, args->data);
+ g_free(args);
+ return NULL;
+}
+
+GThread *dispatch_async(trg_client * client, JsonNode * req,
+ void (*callback) (JsonObject *, int, gpointer),
+ gpointer data)
+{
+ GError *error = NULL;
+ GThread *thread;
+ struct dispatch_async_args *args;
+
+ args = g_new(struct dispatch_async_args, 1);
+ args->callback = callback;
+ args->data = data;
+ args->req = req;
+ args->client = client;
+
+ thread =
+ g_thread_create(dispatch_async_threadfunc, args, FALSE, &error);
+ if (error != NULL) {
+ g_printf("thread creation error: %s\n", error->message);
+ g_error_free(error);
+ g_free(args);
+ return NULL;
+ } else {
+ return thread;
+ }
+}
diff --git a/src/dispatch.h b/src/dispatch.h
new file mode 100644
index 0000000..7c755b7
--- /dev/null
+++ b/src/dispatch.h
@@ -0,0 +1,40 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef DISPATCH_H_
+#define DISPATCH_H_
+
+#include "trg-client.h"
+
+#define FAIL_JSON_DECODE -2
+#define FAIL_RESPONSE_UNSUCCESSFUL -3
+
+struct dispatch_async_args {
+ gpointer *data;
+ JsonNode *req;
+ trg_client *client;
+ void (*callback) (JsonObject *, int, gpointer);
+};
+
+JsonObject *dispatch(trg_client * client, JsonNode * req, int *status);
+GThread *dispatch_async(trg_client * client, JsonNode * req,
+ void (*callback) (JsonObject *, int, gpointer),
+ gpointer data);
+
+#endif /* DISPATCH_H_ */
diff --git a/src/hig.c b/src/hig.c
new file mode 100644
index 0000000..3c0586f
--- /dev/null
+++ b/src/hig.c
@@ -0,0 +1,208 @@
+/*
+ * This file Copyright (C) 2007-2010 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: hig.c 9868 2010-01-04 21:00:47Z charles $
+ */
+
+#include <gtk/gtk.h>
+#include "hig.h"
+
+GtkWidget *hig_workarea_create(void)
+{
+ GtkWidget *t = gtk_table_new(1, 2, FALSE);
+
+ gtk_container_set_border_width(GTK_CONTAINER(t), GUI_PAD_BIG);
+ gtk_table_set_col_spacing(GTK_TABLE(t), 0, GUI_PAD_BIG);
+ gtk_table_set_row_spacings(GTK_TABLE(t), GUI_PAD);
+ return t;
+}
+
+void hig_workarea_add_section_divider(GtkWidget * t, int *row)
+{
+ GtkWidget *w = gtk_alignment_new(0.0f, 0.0f, 0.0f, 0.0f);
+
+ gtk_widget_set_size_request(w, 0u, 6u);
+ gtk_table_attach(GTK_TABLE(t), w, 0, 2, *row, *row + 1, 0, 0, 0, 0);
+ ++*row;
+}
+
+void
+hig_workarea_add_section_title_widget(GtkWidget * t,
+ int *row, GtkWidget * w)
+{
+ gtk_table_attach(GTK_TABLE(t), w, 0, 2, *row, *row + 1, ~0, 0, 0, 0);
+ ++*row;
+}
+
+void
+hig_workarea_add_section_title(GtkWidget * t,
+ int *row, const char *section_title)
+{
+ char buf[512];
+ GtkWidget *l;
+
+ g_snprintf(buf, sizeof(buf), "<b>%s</b>", section_title);
+ l = gtk_label_new(buf);
+ gtk_misc_set_alignment(GTK_MISC(l), 0.0f, 0.5f);
+ gtk_label_set_use_markup(GTK_LABEL(l), TRUE);
+ hig_workarea_add_section_title_widget(t, row, l);
+}
+
+static GtkWidget *rowNew(GtkWidget * w)
+{
+ GtkWidget *a;
+ GtkWidget *h = gtk_hbox_new(FALSE, 0);
+
+ /* spacer */
+ a = gtk_alignment_new(0.0f, 0.0f, 0.0f, 0.0f);
+ gtk_widget_set_size_request(a, 18u, 0u);
+ gtk_box_pack_start(GTK_BOX(h), a, FALSE, FALSE, 0);
+
+ /* lhs widget */
+ if (GTK_IS_MISC(w))
+ gtk_misc_set_alignment(GTK_MISC(w), 0.0f, 0.5f);
+ if (GTK_IS_LABEL(w))
+ gtk_label_set_use_markup(GTK_LABEL(w), TRUE);
+ gtk_box_pack_start(GTK_BOX(h), w, TRUE, TRUE, 0);
+
+ return h;
+}
+
+void hig_workarea_add_wide_control(GtkWidget * t, int *row, GtkWidget * w)
+{
+ GtkWidget *r = rowNew(w);
+
+ gtk_table_attach(GTK_TABLE(t), r, 0, 2, *row, *row + 1, GTK_FILL,
+ 0, 0, 0);
+ ++*row;
+}
+
+void
+hig_workarea_add_wide_tall_control(GtkWidget * t, int *row, GtkWidget * w)
+{
+ GtkWidget *r = rowNew(w);
+
+ gtk_table_attach(GTK_TABLE(t), r, 0, 2, *row, *row + 1,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
+
+ ++*row;
+}
+
+GtkWidget *hig_workarea_add_wide_checkbutton(GtkWidget * t,
+ int *row,
+ const char *mnemonic_string,
+ gboolean is_active)
+{
+ GtkWidget *w = gtk_check_button_new_with_mnemonic(mnemonic_string);
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), is_active);
+ hig_workarea_add_wide_control(t, row, w);
+ return w;
+}
+
+void hig_workarea_add_label_w(GtkWidget * t, int row, GtkWidget * l)
+{
+ GtkWidget *w = rowNew(l);
+
+ gtk_table_attach(GTK_TABLE(t), w, 0, 1, row, row + 1, GTK_FILL,
+ GTK_FILL, 0, 0);
+}
+
+GtkWidget *hig_workarea_add_label(GtkWidget * t,
+ int row, const char *mnemonic_string)
+{
+ GtkWidget *l = gtk_label_new_with_mnemonic(mnemonic_string);
+
+ hig_workarea_add_label_w(t, row, l);
+ return l;
+}
+
+static void
+hig_workarea_add_tall_control(GtkWidget * t, int row, GtkWidget * control)
+{
+ if (GTK_IS_MISC(control))
+ gtk_misc_set_alignment(GTK_MISC(control), 0.0f, 0.5f);
+
+ gtk_table_attach(GTK_TABLE(t), control,
+ 1, 2, row, row + 1,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
+}
+
+void hig_workarea_add_control(GtkWidget * t, int row, GtkWidget * control)
+{
+ if (GTK_IS_MISC(control))
+ gtk_misc_set_alignment(GTK_MISC(control), 0.0f, 0.5f);
+
+ gtk_table_attach(GTK_TABLE(t), control,
+ 1, 2, row, row + 1,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0, 0);
+}
+
+void
+hig_workarea_add_row_w(GtkWidget * t,
+ int *row,
+ GtkWidget * label,
+ GtkWidget * control, GtkWidget * mnemonic)
+{
+ hig_workarea_add_label_w(t, *row, label);
+ hig_workarea_add_control(t, *row, control);
+ if (GTK_IS_LABEL(label))
+ gtk_label_set_mnemonic_widget(GTK_LABEL(label),
+ mnemonic ? mnemonic : control);
+ ++*row;
+}
+
+GtkWidget *hig_workarea_add_row(GtkWidget * t,
+ int *row,
+ const char *mnemonic_string,
+ GtkWidget * control, GtkWidget * mnemonic)
+{
+ GtkWidget *l = gtk_label_new_with_mnemonic(mnemonic_string);
+
+ hig_workarea_add_row_w(t, row, l, control, mnemonic);
+ return l;
+}
+
+GtkWidget *hig_workarea_add_tall_row(GtkWidget * table,
+ int *row,
+ const char *mnemonic_string,
+ GtkWidget * control,
+ GtkWidget * mnemonic)
+{
+ GtkWidget *l = gtk_label_new_with_mnemonic(mnemonic_string);
+ GtkWidget *h = gtk_hbox_new(FALSE, 0);
+ GtkWidget *v = gtk_vbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(h), l, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(v), h, FALSE, FALSE, GUI_PAD_SMALL);
+
+ hig_workarea_add_label_w(table, *row, v);
+ hig_workarea_add_tall_control(table, *row, control);
+
+ if (GTK_IS_LABEL(l))
+ gtk_label_set_mnemonic_widget(GTK_LABEL(l),
+ mnemonic ? mnemonic : control);
+
+ ++*row;
+ return l;
+}
+
+void hig_workarea_finish(GtkWidget * t, int *row)
+{
+ gtk_table_resize(GTK_TABLE(t), *row, 2);
+}
+
+void
+hig_message_dialog_set_text(GtkMessageDialog * dialog,
+ const char *primary, const char *secondary)
+{
+ gtk_message_dialog_set_markup(dialog, primary);
+ gtk_message_dialog_format_secondary_text(dialog, "%s", secondary);
+}
diff --git a/src/hig.h b/src/hig.h
new file mode 100644
index 0000000..186e66a
--- /dev/null
+++ b/src/hig.h
@@ -0,0 +1,85 @@
+/*
+ * This file Copyright (C) 2007-2010 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: hig.h 9868 2010-01-04 21:00:47Z charles $
+ */
+
+#ifndef __HIG_H__
+#define __HIG_H__
+
+#include <gtk/gtk.h>
+
+/**
+*** utility code for making dialog layout that follows the Gnome HIG.
+*** see section 8.2.2, Visual Design > Window Layout > Dialogs.
+**/
+
+GtkWidget *hig_workarea_create(void);
+
+void hig_workarea_add_section_divider(GtkWidget * table, int *row);
+
+void hig_workarea_add_section_title_widget(GtkWidget * t,
+ int *row, GtkWidget * w);
+
+void hig_workarea_add_section_title(GtkWidget * table,
+ int *row, const char *section_title);
+
+void hig_workarea_add_wide_tall_control(GtkWidget * table,
+ int *row, GtkWidget * w);
+
+void hig_workarea_add_wide_control(GtkWidget * table,
+ int *row, GtkWidget * w);
+
+GtkWidget *hig_workarea_add_wide_checkbutton(GtkWidget * table,
+ int *row,
+ const char *mnemonic_string,
+ gboolean is_active);
+
+GtkWidget *hig_workarea_add_label(GtkWidget * table,
+ int row, const char *mnemonic_string);
+
+void hig_workarea_add_label_w(GtkWidget * table,
+ int row, GtkWidget * label_widget);
+
+void hig_workarea_add_control(GtkWidget * table,
+ int row, GtkWidget * control);
+
+GtkWidget *hig_workarea_add_tall_row(GtkWidget * table,
+ int *row,
+ const char *mnemonic_string,
+ GtkWidget * control,
+ GtkWidget *
+ mnemonic_or_null_for_control);
+
+GtkWidget *hig_workarea_add_row(GtkWidget * table,
+ int *row,
+ const char *mnemonic_string,
+ GtkWidget * control,
+ GtkWidget * mnemonic_or_null_for_control);
+
+void hig_workarea_add_row_w(GtkWidget * table,
+ int *row,
+ GtkWidget * label,
+ GtkWidget * control,
+ GtkWidget * mnemonic_or_null_for_control);
+
+void hig_workarea_finish(GtkWidget * table, int *row);
+
+void hig_message_dialog_set_text(GtkMessageDialog * dialog,
+ const char *primary,
+ const char *secondary);
+
+enum {
+ GUI_PAD_SMALL = 3,
+ GUI_PAD = 6,
+ GUI_PAD_BIG = 12,
+ GUI_PAD_LARGE = 12
+};
+
+#endif /* __HIG_H__ */
diff --git a/src/http.c b/src/http.c
new file mode 100644
index 0000000..8239ac8
--- /dev/null
+++ b/src/http.c
@@ -0,0 +1,145 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <curl/curl.h>
+#include <curl/types.h>
+#include <curl/easy.h>
+
+#include <glib-object.h>
+#include <json-glib/json-glib.h>
+
+#include "trg-client.h"
+#include "http.h"
+
+static struct http_response *trg_http_perform_inner(trg_client * client,
+ gchar * req,
+ gboolean recurse);
+
+static size_t http_receive_callback(void *ptr, size_t size, size_t nmemb,
+ void *data);
+
+static size_t header_callback(void *ptr, size_t size, size_t nmemb,
+ void *data);
+
+void http_response_free(struct http_response *response)
+{
+ if (response->data != NULL)
+ g_free(response->data);
+
+ g_free(response);
+}
+
+static struct http_response *trg_http_perform_inner(trg_client * tc,
+ gchar * req,
+ gboolean recurse)
+{
+ CURL *handle;
+ long httpCode;
+ struct http_response *response;
+ struct curl_slist *headers = NULL;
+
+ response = g_new(struct http_response, 1);
+ response->size = 0;
+ response->status = -1;
+ response->data = NULL;
+
+ handle = curl_easy_init();
+
+ curl_easy_setopt(handle, CURLOPT_USERAGENT, "trg");
+ curl_easy_setopt(handle, CURLOPT_PASSWORD, tc->password);
+ curl_easy_setopt(handle, CURLOPT_USERNAME, tc->username);
+ curl_easy_setopt(handle, CURLOPT_URL, tc->url);
+ curl_easy_setopt(handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
+ curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION,
+ &http_receive_callback);
+ curl_easy_setopt(handle, CURLOPT_WRITEDATA, (void *) response);
+ curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, &header_callback);
+ curl_easy_setopt(handle, CURLOPT_WRITEHEADER, (void *) tc);
+ curl_easy_setopt(handle, CURLOPT_POSTFIELDS, req);
+ /*curl_easy_setopt(handle, CURLOPT_VERBOSE, 1); */
+
+ if (tc->session_id != NULL) {
+ headers = curl_slist_append(headers, tc->session_id);
+ curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
+ }
+
+ response->status = curl_easy_perform(handle);
+
+ curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &httpCode);
+ curl_easy_cleanup(handle);
+
+ if (headers != NULL)
+ curl_slist_free_all(headers);
+
+ if (response->status == CURLE_OK) {
+ if (httpCode == HTTP_CONFLICT && recurse == TRUE) {
+ http_response_free(response);
+ return trg_http_perform_inner(tc, req, FALSE);
+ } else if (httpCode != HTTP_OK) {
+ response->status = (-httpCode) - 100;
+ }
+ }
+
+ return response;
+}
+
+struct http_response *trg_http_perform(trg_client * tc, gchar * req)
+{
+ return trg_http_perform_inner(tc, req, TRUE);
+}
+
+static size_t
+http_receive_callback(void *ptr, size_t size, size_t nmemb, void *data)
+{
+ size_t realsize = size * nmemb;
+ struct http_response *mem = (struct http_response *) data;
+
+ mem->data = realloc(mem->data, mem->size + realsize + 1);
+ if (mem->data) {
+ memcpy(&(mem->data[mem->size]), ptr, realsize);
+ mem->size += realsize;
+ mem->data[mem->size] = 0;
+ }
+ return realsize;
+}
+
+static size_t header_callback(void *ptr, size_t size, size_t nmemb,
+ void *data)
+{
+ char *header = (char *) (ptr);
+ trg_client *client = (trg_client *) data;
+
+ if (g_str_has_prefix(header, "X-Transmission-Session-Id: ") == TRUE) {
+ char *nl;
+
+ if (client->session_id != NULL)
+ g_free(client->session_id);
+
+ client->session_id = g_strdup(header);
+ nl = strrchr(client->session_id, '\r');
+ if (nl)
+ *nl = '\0';
+ }
+
+ return (nmemb * size);
+}
diff --git a/src/http.h b/src/http.h
new file mode 100644
index 0000000..cc7a30f
--- /dev/null
+++ b/src/http.h
@@ -0,0 +1,43 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef HTTP_H_
+#define HTTP_H_
+
+#include <stdlib.h>
+
+#include <glib-object.h>
+#include <json-glib/json-glib.h>
+#include <curl/curl.h>
+
+#include "trg-client.h"
+
+#define HTTP_OK 200
+#define HTTP_CONFLICT 409
+
+struct http_response {
+ int status;
+ char *data;
+ int size;
+};
+
+void http_response_free(struct http_response *response);
+struct http_response *trg_http_perform(trg_client * client, gchar * req);
+
+#endif /* HTTP_H_ */
diff --git a/src/json.c b/src/json.c
new file mode 100644
index 0000000..fab44a8
--- /dev/null
+++ b/src/json.c
@@ -0,0 +1,93 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <glib-object.h>
+#include <glib/gprintf.h>
+#include <json-glib/json-glib.h>
+#include <gtk/gtk.h>
+
+#include "protocol-constants.h"
+#include "requests.h"
+#include "dispatch.h"
+#include "http.h"
+#include "json.h"
+
+gchar *trg_serialize(JsonNode * req)
+{
+ JsonGenerator *generator;
+ gsize length;
+ gchar *response;
+
+ generator = json_generator_new();
+ json_generator_set_root(generator, req);
+
+ response = json_generator_to_data(generator, &length);
+ g_object_unref(generator);
+
+ return response;
+}
+
+JsonObject *trg_deserialize(struct http_response * response,
+ GError ** error)
+{
+ JsonParser *parser;
+ JsonNode *root;
+ JsonObject *ret = NULL;
+
+ parser = json_parser_new();
+ json_parser_load_from_data(parser, response->data, response->size,
+ error);
+ if (*error == NULL) {
+ root = json_parser_get_root(parser);
+ if (g_getenv("SHOW_INCOMING") != NULL) {
+ g_printf("incoming JSON data:\n%s\n", response->data);
+ } else if (g_getenv("SHOW_INCOMING_PRETTY") != NULL) {
+ JsonGenerator *pg;
+ gsize len;
+ gchar *pgdata;
+
+ pg = json_generator_new();
+ g_object_set(pg, "pretty", TRUE, NULL);
+ json_generator_set_root(pg, root);
+
+ pgdata = json_generator_to_data(pg, &len);
+ g_printf("incoming JSON data:\n%s\n", pgdata);
+ g_free(pgdata);
+
+ g_object_unref(pg);
+ }
+
+ ret = json_node_get_object(root);
+ json_object_ref(ret);
+ }
+
+ g_object_unref(parser);
+ return ret;
+}
+
+JsonObject *node_get_arguments(JsonNode * req)
+{
+ JsonObject *rootObj = json_node_get_object(req);
+ return get_arguments(rootObj);
+}
+
+JsonObject *get_arguments(JsonObject * req)
+{
+ return json_object_get_object_member(req, PARAM_ARGUMENTS);
+}
diff --git a/src/json.h b/src/json.h
new file mode 100644
index 0000000..bd0dbd3
--- /dev/null
+++ b/src/json.h
@@ -0,0 +1,34 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef JSON_H_
+#define JSON_H_
+
+#include <glib-object.h>
+#include <json-glib/json-glib.h>
+
+#include "http.h"
+
+gchar *trg_serialize(JsonNode * req);
+JsonObject *trg_deserialize(struct http_response *response,
+ GError ** error);
+JsonObject *get_arguments(JsonObject * req);
+JsonObject *node_get_arguments(JsonNode * req);
+
+#endif /* JSON_H_ */
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..cf7d54a
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,135 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+
+#include <curl/curl.h>
+#include <curl/types.h>
+#include <curl/easy.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+#include <unique/unique.h>
+#include "http.h"
+#include "trg-main-window.h"
+#include "trg-client.h"
+
+enum {
+ COMMAND_0,
+ COMMAND_ADD
+};
+
+static UniqueResponse
+message_received_cb(UniqueApp * app,
+ gint command,
+ UniqueMessageData * message,
+ guint time_, gpointer user_data)
+{
+ TrgMainWindow *win;
+ UniqueResponse res;
+
+ win = TRG_MAIN_WINDOW(user_data);
+
+ switch (command) {
+ case UNIQUE_ACTIVATE:
+ gtk_window_set_screen(GTK_WINDOW(user_data),
+ unique_message_data_get_screen(message));
+ gtk_window_present_with_time(GTK_WINDOW(user_data), time_);
+ res = UNIQUE_RESPONSE_OK;
+ break;
+ case COMMAND_ADD:
+ res =
+ trg_add_from_filename(win,
+ unique_message_data_get_filename
+ (message)) ? UNIQUE_RESPONSE_OK :
+ UNIQUE_RESPONSE_FAIL;
+ break;
+ default:
+ res = UNIQUE_RESPONSE_OK;
+ break;
+ }
+
+ return res;
+}
+
+int main(int argc, char *argv[])
+{
+ int returnValue;
+ UniqueApp *app;
+ TrgMainWindow *window;
+ trg_client *client;
+
+ g_type_init();
+ g_thread_init(NULL);
+ gdk_threads_init();
+ gtk_init(&argc, &argv);
+
+ app = unique_app_new_with_commands("org.eth0.uk.org.trg", NULL,
+ "add", COMMAND_ADD, NULL);
+
+ if (unique_app_is_running(app)) {
+ UniqueCommand command;
+ UniqueResponse response;
+ UniqueMessageData *message;
+
+ if (argc > 1) {
+ command = COMMAND_ADD;
+ message = unique_message_data_new();
+ unique_message_data_set_filename(message, argv[1]);
+ } else {
+ command = UNIQUE_ACTIVATE;
+ message = NULL;
+ }
+
+ response = unique_app_send_message(app, command, message);
+ unique_message_data_free(message);
+
+ if (response == UNIQUE_RESPONSE_OK) {
+ returnValue = 0;
+ } else {
+ returnValue = 1;
+ }
+ } else {
+ client = trg_init_client();
+
+ curl_global_init(CURL_GLOBAL_ALL);
+
+ window = trg_main_window_new(client);
+ unique_app_watch_window(app, GTK_WINDOW(window));
+
+ g_signal_connect(app, "message-received",
+ G_CALLBACK(message_received_cb), window);
+
+ gtk_widget_show_all(GTK_WIDGET(window));
+
+ auto_connect_if_required(window, client);
+ gtk_main();
+
+ curl_global_cleanup();
+ }
+
+ g_object_unref(app);
+ gdk_threads_leave();
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/protocol-constants.h b/src/protocol-constants.h
new file mode 100644
index 0000000..462a612
--- /dev/null
+++ b/src/protocol-constants.h
@@ -0,0 +1,115 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PROTOCOL_CONSTANTS_H_
+#define PROTOCOL_CONSTANTS_H_
+
+/* generic contstants */
+
+#define PARAM_METHOD "method"
+#define FIELD_ID "id"
+
+/* torrents */
+
+#define FIELD_TORRENTS "torrents" /* parent node */
+#define FIELD_ANNOUNCE_URL "announceUrl"
+#define FIELD_LEFT_UNTIL_DONE "leftUntilDone"
+#define FIELD_TOTAL_SIZE "totalSize"
+#define FIELD_DONE_DATE "doneDate"
+#define FIELD_ADDED_DATE "addedDate"
+#define FIELD_TRACKERS "trackers"
+#define FIELD_DOWNLOAD_DIR "downloadDir"
+#define FIELD_HASH_STRING "hashString"
+#define FIELD_SWARM_SPEED "swarmSpeed"
+#define FIELD_ERROR_STRING "errorString"
+#define FIELD_NAME "name"
+#define FIELD_SIZEWHENDONE "sizeWhenDone"
+#define FIELD_STATUS "status"
+#define FIELD_RATEDOWNLOAD "rateDownload"
+#define FIELD_RATEUPLOAD "rateUpload"
+#define FIELD_ETA "eta"
+#define FIELD_UPLOADEDEVER "uploadedEver"
+#define FIELD_DOWNLOADEDEVER "downloadedEver"
+#define FIELD_HAVEVALID "haveValid"
+#define FIELD_HAVEUNCHECKED "haveUnchecked"
+#define FIELD_PERCENTDONE "percentDone"
+#define FIELD_TRACKERS "trackers"
+#define FIELD_PEERS "peers"
+#define FIELD_FILES "files"
+#define FIELD_WANTED "wanted"
+#define FIELD_PRIORITIES "priorities"
+#define FIELD_COMMENT "comment"
+#define FIELD_LEFTUNTILDONE "leftUntilDone"
+#define FIELD_ISFINISHED "isFinished"
+#define FIELD_ERRORSTR "errorString"
+#define FIELD_BANDWIDTH_PRIORITY "bandwidthPriority"
+#define FIELD_UPLOAD_LIMIT "uploadLimit"
+#define FIELD_UPLOAD_LIMITED "uploadLimited"
+#define FIELD_DOWNLOAD_LIMIT "downloadLimit"
+#define FIELD_DOWNLOAD_LIMITED "downloadLimited"
+#define FIELD_HONORS_SESSION_LIMITS "honorsSessionLimits"
+#define FIELD_SEED_RATIO_MODE "seedRatioMode"
+#define FIELD_SEED_RATIO_LIMIT "seedRatioLimit"
+#define FIELD_PEER_LIMIT "peer-limit"
+
+#define FIELD_FILES_WANTED "files-wanted"
+#define FIELD_FILES_UNWANTED "files-unwanted"
+#define FIELD_FILES_PRIORITY_HIGH "priority-high"
+#define FIELD_FILES_PRIORITY_NORMAL "priority-normal"
+#define FIELD_FILES_PRIORITY_LOW "priority-low"
+
+/* trackers */
+
+#define FIELD_TIER "tier"
+#define FIELD_ANNOUNCE "announce"
+#define FIELD_SCRAPE "scrape"
+
+/* methods */
+
+#define METHOD_TORRENT_START "torrent-start"
+#define METHOD_SESSION_GET "session-get"
+#define METHOD_SESSION_SET "session-set"
+#define METHOD_TORRENT_GET "torrent-get"
+#define METHOD_TORRENT_SET "torrent-set"
+#define METHOD_TORRENT_STOP "torrent-stop"
+#define METHOD_TORRENT_VERIFY "torrent-verify"
+#define METHOD_TORRENT_REMOVE "torrent-remove"
+#define METHOD_TORRENT_ADD "torrent-add"
+
+#define PARAM_IDS "ids"
+#define PARAM_DELETE_LOCAL_DATA "delete-local-data"
+#define PARAM_ARGUMENTS "arguments"
+#define PARAM_FIELDS "fields"
+#define PARAM_METAINFO "metainfo"
+#define PARAM_FILENAME "filename"
+#define PARAM_TAG "tag"
+
+enum {
+ STATUS_WAITING_TO_CHECK = 1,
+ STATUS_CHECKING = 2,
+ STATUS_DOWNLOADING = 4,
+ STATUS_SEEDING = 8,
+ STATUS_PAUSED = 16
+} TorrentState;
+
+#define TFILE_LENGTH "length"
+#define TFILE_BYTES_COMPLETED "bytesCompleted"
+#define TFILE_NAME "name"
+
+#endif /* PROTOCOL_CONSTANTS_H_ */
diff --git a/src/requests.c b/src/requests.c
new file mode 100644
index 0000000..7ee8782
--- /dev/null
+++ b/src/requests.c
@@ -0,0 +1,155 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+
+#include <glib-object.h>
+#include <json-glib/json-glib.h>
+
+#include "protocol-constants.h"
+#include "base64.h"
+#include "json.h"
+#include "requests.h"
+
+static JsonNode *base_request(gchar * method);
+
+JsonNode *generic_request(gchar * method, JsonArray * ids)
+{
+ JsonNode *root = base_request(method);
+
+ if (ids != NULL)
+ json_object_set_array_member(node_get_arguments(root),
+ PARAM_IDS, ids);
+
+ return root;
+}
+
+JsonNode *session_get()
+{
+ return generic_request(METHOD_SESSION_GET, NULL);
+}
+
+JsonNode *torrent_start(JsonArray * array)
+{
+ return generic_request(METHOD_TORRENT_START, array);
+}
+
+JsonNode *torrent_pause(JsonArray * array)
+{
+ return generic_request(METHOD_TORRENT_STOP, array);
+}
+
+JsonNode *torrent_verify(JsonArray * array)
+{
+ return generic_request(METHOD_TORRENT_VERIFY, array);
+}
+
+JsonNode *session_set(void)
+{
+ return generic_request(METHOD_SESSION_SET, NULL);
+}
+
+JsonNode *torrent_set(JsonArray * array)
+{
+ return generic_request(METHOD_TORRENT_SET, array);
+}
+
+JsonNode *torrent_remove(JsonArray * array, gboolean removeData)
+{
+ JsonNode *root = base_request(METHOD_TORRENT_REMOVE);
+ JsonObject *args = node_get_arguments(root);
+ json_object_set_array_member(args, PARAM_IDS, array);
+ json_object_set_boolean_member(args, PARAM_DELETE_LOCAL_DATA,
+ removeData);
+ return root;
+}
+
+JsonNode *torrent_get()
+{
+ JsonNode *root = base_request(METHOD_TORRENT_GET);
+ JsonArray *fields = json_array_new();
+ json_array_add_string_element(fields, FIELD_ETA);
+ json_array_add_string_element(fields, FIELD_PEERS);
+ json_array_add_string_element(fields, FIELD_FILES);
+ json_array_add_string_element(fields, FIELD_HAVEVALID);
+ json_array_add_string_element(fields, FIELD_HAVEUNCHECKED);
+ json_array_add_string_element(fields, FIELD_RATEUPLOAD);
+ json_array_add_string_element(fields, FIELD_RATEDOWNLOAD);
+ json_array_add_string_element(fields, FIELD_STATUS);
+ json_array_add_string_element(fields, FIELD_UPLOADEDEVER);
+ json_array_add_string_element(fields, FIELD_SIZEWHENDONE);
+ json_array_add_string_element(fields, FIELD_ID);
+ json_array_add_string_element(fields, FIELD_NAME);
+ json_array_add_string_element(fields, FIELD_PERCENTDONE);
+ json_array_add_string_element(fields, FIELD_COMMENT);
+ json_array_add_string_element(fields, FIELD_ADDED_DATE);
+ json_array_add_string_element(fields, FIELD_TOTAL_SIZE);
+ json_array_add_string_element(fields, FIELD_LEFT_UNTIL_DONE);
+ json_array_add_string_element(fields, FIELD_ANNOUNCE_URL);
+ json_array_add_string_element(fields, FIELD_ERROR_STRING);
+ json_array_add_string_element(fields, FIELD_SWARM_SPEED);
+ json_array_add_string_element(fields, FIELD_TRACKERS);
+ json_array_add_string_element(fields, FIELD_DOWNLOAD_DIR);
+ json_array_add_string_element(fields, FIELD_HASH_STRING);
+ json_array_add_string_element(fields, FIELD_DONE_DATE);
+ json_array_add_string_element(fields, FIELD_HONORS_SESSION_LIMITS);
+ json_array_add_string_element(fields, FIELD_UPLOAD_LIMIT);
+ json_array_add_string_element(fields, FIELD_UPLOAD_LIMITED);
+ json_array_add_string_element(fields, FIELD_DOWNLOAD_LIMIT);
+ json_array_add_string_element(fields, FIELD_DOWNLOAD_LIMITED);
+ json_array_add_string_element(fields, FIELD_BANDWIDTH_PRIORITY);
+ json_array_add_string_element(fields, FIELD_SEED_RATIO_LIMIT);
+ json_array_add_string_element(fields, FIELD_SEED_RATIO_MODE);
+ json_array_add_string_element(fields, FIELD_PEER_LIMIT);
+ json_array_add_string_element(fields, FIELD_ERRORSTR);
+ json_array_add_string_element(fields, FIELD_WANTED);
+ json_array_add_string_element(fields, FIELD_PRIORITIES);
+ json_object_set_array_member(node_get_arguments(root),
+ PARAM_FIELDS, fields);
+ return root;
+}
+
+JsonNode *torrent_add_url(const gchar * url, gboolean paused)
+{
+ JsonNode *root = base_request(METHOD_TORRENT_ADD);
+ JsonObject *args = node_get_arguments(root);
+ json_object_set_string_member(args, PARAM_FILENAME, url);
+ return root;
+}
+
+JsonNode *torrent_add(gchar * filename, gboolean paused)
+{
+ JsonNode *root = base_request(METHOD_TORRENT_ADD);
+ JsonObject *args = node_get_arguments(root);
+ gchar *encodedFile = base64encode(filename);
+ json_object_set_string_member(args, PARAM_METAINFO, encodedFile);
+ g_free(encodedFile);
+ return root;
+}
+
+static JsonNode *base_request(gchar * method)
+{
+ JsonNode *root = json_node_new(JSON_NODE_OBJECT);
+ JsonObject *object = json_object_new();
+ JsonObject *args = json_object_new();
+ json_object_set_string_member(object, PARAM_METHOD, method);
+ json_object_set_object_member(object, PARAM_ARGUMENTS, args);
+ json_node_take_object(root, object);
+ return root;
+}
diff --git a/src/requests.h b/src/requests.h
new file mode 100644
index 0000000..85f0b26
--- /dev/null
+++ b/src/requests.h
@@ -0,0 +1,39 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef REQUESTS_H_
+#define REQUESTS_H_
+
+#include <glib-object.h>
+#include <json-glib/json-glib.h>
+
+JsonNode *generic_request(gchar * method, JsonArray * array);
+
+JsonNode *session_set(void);
+JsonNode *session_get();
+JsonNode *torrent_get();
+JsonNode *torrent_set(JsonArray * array);
+JsonNode *torrent_pause(JsonArray * array);
+JsonNode *torrent_start(JsonArray * array);
+JsonNode *torrent_verify(JsonArray * array);
+JsonNode *torrent_remove(JsonArray * array, int removeData);
+JsonNode *torrent_add(gchar * filename, gboolean paused);
+JsonNode *torrent_add_url(const gchar * url, gboolean paused);
+
+#endif /* REQUESTS_H_ */
diff --git a/src/session-get.c b/src/session-get.c
new file mode 100644
index 0000000..18b0bf5
--- /dev/null
+++ b/src/session-get.c
@@ -0,0 +1,161 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+
+#include <json-glib/json-glib.h>
+
+#include "protocol-constants.h"
+#include "session-get.h"
+
+int session_get_version(JsonObject * s, float *version, int *revision)
+{
+ return sscanf(json_object_get_string_member(s, SGET_VERSION),
+ "%f (%d)", version, revision);
+}
+
+gboolean session_get_pex_enabled(JsonObject * s)
+{
+ return json_object_get_boolean_member(s, SGET_PEX_ENABLED);
+}
+
+gboolean session_get_lpd_enabled(JsonObject * s)
+{
+ return json_object_get_boolean_member(s, SGET_LPD_ENABLED);
+}
+
+const gchar *session_get_download_dir(JsonObject * s)
+{
+ return json_object_get_string_member(s, SGET_DOWNLOAD_DIR);
+}
+
+gboolean session_get_peer_port_random(JsonObject * s)
+{
+ return json_object_get_boolean_member(s,
+ SGET_PEER_PORT_RANDOM_ON_START);
+}
+
+gint64 session_get_peer_port(JsonObject * s)
+{
+ return json_object_get_int_member(s, SGET_PEER_PORT);
+}
+
+gboolean session_get_port_forwarding_enabled(JsonObject * s)
+{
+ return json_object_get_boolean_member(s, SGET_PORT_FORWARDING_ENABLED);
+}
+
+const gchar *session_get_blocklist_url(JsonObject * s)
+{
+ return json_object_get_string_member(s, SGET_BLOCKLIST_URL);
+}
+
+gboolean session_get_blocklist_enabled(JsonObject * s)
+{
+ return json_object_get_boolean_member(s, SGET_BLOCKLIST_ENABLED);
+}
+
+gboolean session_get_rename_partial_files(JsonObject * s)
+{
+ return json_object_get_boolean_member(s, SGET_RENAME_PARTIAL_FILES);
+}
+
+const gchar *session_get_encryption(JsonObject * s)
+{
+ return json_object_get_string_member(s, SGET_ENCRYPTION);
+}
+
+const gchar *session_get_incomplete_dir(JsonObject * s)
+{
+ return json_object_get_string_member(s, SGET_INCOMPLETE_DIR);
+}
+
+gboolean session_get_incomplete_dir_enabled(JsonObject * s)
+{
+ return json_object_get_boolean_member(s, SGET_INCOMPLETE_DIR_ENABLED);
+}
+
+gboolean session_get_seed_ratio_limited(JsonObject * s)
+{
+ return json_object_get_boolean_member(s, SGET_SEED_RATIO_LIMITED);
+}
+
+const gchar *session_get_torrent_done_filename(JsonObject * s)
+{
+ return json_object_get_string_member(s,
+ SGET_SCRIPT_TORRENT_DONE_FILENAME);
+}
+
+gboolean session_get_torrent_done_enabled(JsonObject * s)
+{
+ return json_object_get_boolean_member(s,
+ SGET_SCRIPT_TORRENT_DONE_ENABLED);
+}
+
+gint64 session_get_cache_size_mb(JsonObject * s)
+{
+ return json_object_get_int_member(s, SGET_CACHE_SIZE_MB);
+}
+
+gdouble session_get_seed_ratio_limit(JsonObject * s)
+{
+ return json_object_get_double_member(s, SGET_SEED_RATIO_LIMIT);
+}
+
+gboolean session_get_start_added_torrents(JsonObject * s)
+{
+ return json_object_get_boolean_member(s, SGET_START_ADDED_TORRENTS);
+}
+
+gboolean session_get_trash_original_torrent_files(JsonObject * s)
+{
+ return json_object_get_boolean_member(s,
+ SGET_TRASH_ORIGINAL_TORRENT_FILES);
+}
+
+gboolean session_get_speed_limit_up_enabled(JsonObject * s)
+{
+ return json_object_get_boolean_member(s, SGET_SPEED_LIMIT_UP_ENABLED);
+}
+
+gint64 session_get_peer_limit_per_torrent(JsonObject * s)
+{
+ return json_object_get_int_member(s, SGET_PEER_LIMIT_PER_TORRENT);
+}
+
+gint64 session_get_peer_limit_global(JsonObject * s)
+{
+ return json_object_get_int_member(s, SGET_PEER_LIMIT_GLOBAL);
+}
+
+gint64 session_get_speed_limit_up(JsonObject * s)
+{
+ return json_object_get_int_member(s, SGET_SPEED_LIMIT_UP);
+}
+
+gboolean session_get_speed_limit_down_enabled(JsonObject * s)
+{
+ return json_object_get_boolean_member(s,
+ SGET_SPEED_LIMIT_DOWN_ENABLED);
+}
+
+gint64 session_get_speed_limit_down(JsonObject * s)
+{
+ return json_object_get_int_member(s, SGET_SPEED_LIMIT_DOWN);
+}
diff --git a/src/session-get.h b/src/session-get.h
new file mode 100644
index 0000000..26e15e6
--- /dev/null
+++ b/src/session-get.h
@@ -0,0 +1,89 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef SESSION_GET_H_
+#define SESSION_GET_H_
+
+#include <glib-object.h>
+
+#define SGET_ALT_SPEED_DOWN "alt-speed-down"
+#define SGET_ALT_SPEED_ENABLED "alt-speed-enabled"
+#define SGET_ALT_SPEED_TIME_BEGIN "alt-speed-time-begin"
+#define SGET_ALT_SPEED_TIME_DAY "alt-speed-time-day"
+#define SGET_ALT_SPEED_TIME_END "alt-speed-time-end"
+#define SGET_ALT_SPEED_UP "alt-speed-up"
+#define SGET_BLOCKLIST_ENABLED "blocklist-enabled"
+#define SGET_BLOCKLIST_URL "blocklist-url"
+#define SGET_BLOCKLIST_SIZE "blocklist-size"
+#define SGET_DHT_ENABLED "dht-enabled"
+#define SGET_LPD_ENABLED "lpd-enabled"
+#define SGET_DOWNLOAD_DIR "download-dir"
+#define SGET_INCOMPLETE_DIR "incomplete-dir"
+#define SGET_INCOMPLETE_DIR_ENABLED "incomplete-dir-enabled"
+#define SGET_ENCRYPTION "encryption"
+#define SGET_PEER_LIMIT_GLOBAL "peer-limit-global"
+#define SGET_PEER_LIMIT_PER_TORRENT "peer-limit-per-torrent"
+#define SGET_PEER_PORT "peer-port"
+#define SGET_PEER_PORT_RANDOM_ON_START "peer-port-random-on-start"
+#define SGET_PEX_ENABLED "pex-enabled"
+#define SGET_PORT_FORWARDING_ENABLED "port-forwarding-enabled"
+#define SGET_RPC_VERSION "rpc-version"
+#define SGET_RPC_VERSION_MINIMUM "rpc-version-minimum"
+#define SGET_SEED_RATIO_LIMIT "seedRatioLimit"
+#define SGET_SEED_RATIO_LIMITED "seedRatioLimited"
+#define SGET_SPEED_LIMIT_DOWN "speed-limit-down"
+#define SGET_SPEED_LIMIT_DOWN_ENABLED "speed-limit-down-enabled"
+#define SGET_SPEED_LIMIT_UP "speed-limit-up"
+#define SGET_SPEED_LIMIT_UP_ENABLED "speed-limit-up-enabled"
+#define SGET_VERSION "version"
+#define SGET_TRASH_ORIGINAL_TORRENT_FILES "trash-original-torrent-files"
+#define SGET_START_ADDED_TORRENTS "start-added-torrents"
+#define SGET_RENAME_PARTIAL_FILES "rename-partial-files"
+#define SGET_CACHE_SIZE_MB "cache-size-mb"
+#define SGET_SCRIPT_TORRENT_DONE_FILENAME "script-torrent-done-filename"
+#define SGET_SCRIPT_TORRENT_DONE_ENABLED "script-torrent-done-enabled"
+
+const gchar *session_get_torrent_done_filename(JsonObject * s);
+gboolean session_get_torrent_done_enabled(JsonObject * s);
+gint64 session_get_cache_size_mb(JsonObject * s);
+int session_get_version(JsonObject * s, float *version, int *revision);
+gboolean session_get_pex_enabled(JsonObject * s);
+gboolean session_get_lpd_enabled(JsonObject * s);
+const gchar *session_get_download_dir(JsonObject * s);
+gboolean session_get_peer_port_random(JsonObject * s);
+gint64 session_get_peer_port(JsonObject * s);
+gint64 session_get_peer_limit_global(JsonObject * s);
+gint64 session_get_peer_limit_per_torrent(JsonObject * s);
+gboolean session_get_port_forwarding_enabled(JsonObject * s);
+const gchar *session_get_blocklist_url(JsonObject * s);
+gboolean session_get_blocklist_enabled(JsonObject * s);
+gboolean session_get_rename_partial_files(JsonObject * s);
+const gchar *session_get_encryption(JsonObject * s);
+const gchar *session_get_incomplete_dir(JsonObject * s);
+gboolean session_get_incomplete_dir_enabled(JsonObject * s);
+gboolean session_get_seed_ratio_limited(JsonObject * s);
+gdouble session_get_seed_ratio_limit(JsonObject * s);
+gboolean session_get_start_added_torrents(JsonObject * s);
+gboolean session_get_trash_original_torrent_files(JsonObject * s);
+gboolean session_get_speed_limit_up_enabled(JsonObject * s);
+gint64 session_get_speed_limit_up(JsonObject * s);
+gboolean session_get_speed_limit_down_enabled(JsonObject * s);
+gint64 session_get_speed_limit_down(JsonObject * s);
+
+#endif /* SESSION_GET_H_ */
diff --git a/src/tfile.c b/src/tfile.c
new file mode 100644
index 0000000..0c0a757
--- /dev/null
+++ b/src/tfile.c
@@ -0,0 +1,44 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <json-glib/json-glib.h>
+
+#include "protocol-constants.h"
+#include "tfile.h"
+
+gdouble file_get_progress(JsonObject * f)
+{
+ return ((gdouble) file_get_bytes_completed(f) /
+ (gdouble) file_get_length(f)) * 100.0;
+}
+
+gint64 file_get_length(JsonObject * f)
+{
+ return json_object_get_int_member(f, TFILE_LENGTH);
+}
+
+gint64 file_get_bytes_completed(JsonObject * f)
+{
+ return json_object_get_int_member(f, TFILE_BYTES_COMPLETED);
+}
+
+const gchar *file_get_name(JsonObject * f)
+{
+ return json_object_get_string_member(f, TFILE_NAME);
+}
diff --git a/src/tfile.h b/src/tfile.h
new file mode 100644
index 0000000..cf9fdbb
--- /dev/null
+++ b/src/tfile.h
@@ -0,0 +1,31 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TFILE_H_
+#define TFILE_H_
+
+#include <glib-object.h>
+#include <json-glib/json-glib.h>
+
+gint64 file_get_length(JsonObject * f);
+gint64 file_get_bytes_completed(JsonObject * f);
+const gchar *file_get_name(JsonObject * f);
+gdouble file_get_progress(JsonObject * f);
+
+#endif /* TFILE_H_ */
diff --git a/src/torrent.c b/src/torrent.c
new file mode 100644
index 0000000..a49935d
--- /dev/null
+++ b/src/torrent.c
@@ -0,0 +1,234 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+
+#include "torrent.h"
+#include "protocol-constants.h"
+
+JsonArray *torrent_get_peers(JsonObject * t)
+{
+ return json_object_get_array_member(t, FIELD_PEERS);
+}
+
+JsonArray *torrent_get_wanted(JsonObject * t)
+{
+ return json_object_get_array_member(t, FIELD_WANTED);
+}
+
+JsonArray *torrent_get_priorities(JsonObject * t)
+{
+ return json_object_get_array_member(t, FIELD_PRIORITIES);
+}
+
+JsonArray *torrent_get_trackers(JsonObject * t)
+{
+ return json_object_get_array_member(t, FIELD_TRACKERS);
+}
+
+gint64 torrent_get_id(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_ID);
+}
+
+const gchar *torrent_get_name(JsonObject * t)
+{
+ return json_object_get_string_member(t, FIELD_NAME);
+}
+
+gboolean torrent_get_honors_session_limits(JsonObject * t)
+{
+ return json_object_get_boolean_member(t, FIELD_HONORS_SESSION_LIMITS);
+}
+
+gint64 torrent_get_bandwidth_priority(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_BANDWIDTH_PRIORITY);
+}
+
+gint64 torrent_get_upload_limit(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_UPLOAD_LIMIT);
+}
+
+gint64 torrent_get_peer_limit(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_PEER_LIMIT);
+}
+
+gboolean torrent_get_upload_limited(JsonObject * t)
+{
+ return json_object_get_boolean_member(t, FIELD_UPLOAD_LIMITED);
+}
+
+gint64 torrent_get_seed_ratio_mode(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_SEED_RATIO_MODE);
+}
+
+gdouble torrent_get_seed_ratio_limit(JsonObject * t)
+{
+ return json_object_get_double_member(t, FIELD_SEED_RATIO_LIMIT);
+}
+
+gint64 torrent_get_download_limit(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_DOWNLOAD_LIMIT);
+}
+
+gboolean torrent_get_download_limited(JsonObject * t)
+{
+ return json_object_get_boolean_member(t, FIELD_DOWNLOAD_LIMITED);
+}
+
+gint64 torrent_get_size(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_SIZEWHENDONE);
+}
+
+gint64 torrent_get_rate_down(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_RATEDOWNLOAD);
+}
+
+gint64 torrent_get_rate_up(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_RATEUPLOAD);
+}
+
+gint64 torrent_get_eta(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_ETA);
+}
+
+gint64 torrent_get_uploaded(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_UPLOADEDEVER);
+}
+
+gint64 torrent_get_have_valid(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_HAVEVALID);
+}
+
+gint64 torrent_get_have_unchecked(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_HAVEUNCHECKED);
+}
+
+gint64 torrent_get_downloaded(JsonObject * t)
+{
+ return torrent_get_have_valid(t) + torrent_get_have_unchecked(t);
+}
+
+gint64 torrent_get_status(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_STATUS);
+}
+
+gboolean torrent_get_is_finished(JsonObject * t)
+{
+ return torrent_get_left_until_done(t) <= 0;
+}
+
+gdouble torrent_get_percent_done(JsonObject * t)
+{
+ JsonNode *percentDone = json_object_get_member(t, FIELD_PERCENTDONE);
+ GValue a = { 0 };
+ json_node_get_value(percentDone, &a);
+ switch (G_VALUE_TYPE(&a)) {
+ case G_TYPE_INT64:
+ return (gdouble) g_value_get_int64(&a) * 100.0;
+ case G_TYPE_DOUBLE:
+ return g_value_get_double(&a) * 100.0;
+ default:
+ return 0.0;
+ }
+}
+
+gchar *torrent_get_status_icon(guint flags)
+{
+ if (flags & TORRENT_FLAG_ERROR)
+ return g_strdup(GTK_STOCK_DIALOG_WARNING);
+ else if (flags & TORRENT_FLAG_DOWNLOADING)
+ return g_strdup(GTK_STOCK_GO_DOWN);
+ else if (flags & TORRENT_FLAG_PAUSED)
+ return g_strdup(GTK_STOCK_MEDIA_PAUSE);
+ else if (flags & TORRENT_FLAG_SEEDING)
+ return g_strdup(GTK_STOCK_GO_UP);
+ else if (flags & TORRENT_FLAG_CHECKING)
+ return g_strdup(GTK_STOCK_REFRESH);
+ else
+ g_strdup(GTK_STOCK_DIALOG_QUESTION);
+}
+
+const gchar *torrent_get_errorstr(JsonObject * t)
+{
+ return json_object_get_string_member(t, FIELD_ERRORSTR);
+}
+
+gchar *torrent_get_status_string(gint64 value)
+{
+ switch (value) {
+ case STATUS_DOWNLOADING:
+ return g_strdup("Downloading");
+ case STATUS_PAUSED:
+ return g_strdup("Paused");
+ case STATUS_SEEDING:
+ return g_strdup("Seeding");
+ case STATUS_CHECKING:
+ return g_strdup("Checking");
+ case STATUS_WAITING_TO_CHECK:
+ return g_strdup("Waiting To Check");
+ default:
+ return g_strdup("Unknown");
+ }
+}
+
+gint64 tracker_get_tier(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_TIER);
+}
+
+gint64 torrent_get_left_until_done(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_LEFTUNTILDONE);
+}
+
+const gchar *tracker_get_announce(JsonObject * t)
+{
+ return json_object_get_string_member(t, FIELD_ANNOUNCE);
+}
+
+const gchar *tracker_get_scrape(JsonObject * t)
+{
+ return json_object_get_string_member(t, FIELD_SCRAPE);
+}
+
+JsonArray *get_torrents(JsonObject * response)
+{
+ return json_object_get_array_member(response, FIELD_TORRENTS);
+}
+
+JsonArray *torrent_get_files(JsonObject * args)
+{
+ return json_object_get_array_member(args, FIELD_FILES);
+}
diff --git a/src/torrent.h b/src/torrent.h
new file mode 100644
index 0000000..832adb1
--- /dev/null
+++ b/src/torrent.h
@@ -0,0 +1,71 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TORRENT_H_
+#define TORRENT_H_
+
+#include <json-glib/json-glib.h>
+
+#define TORRENT_FLAG_ERROR (1 << 0) /* 0x01 */
+#define TORRENT_FLAG_COMPLETE (1 << 1) /* 0x02 */
+#define TORRENT_FLAG_INCOMPLETE (1 << 2) /* 0x02 */
+#define TORRENT_FLAG_SEEDING (1 << 3) /* 0x04 */
+#define TORRENT_FLAG_CHECKING (1 << 4) /* 0x08 */
+#define TORRENT_FLAG_WAITING_CHECK (1 << 5) /* 0x16 */
+#define TORRENT_FLAG_DOWNLOADING (1 << 6) /* 0x32 */
+#define TORRENT_FLAG_PAUSED (1 << 7) /* 0x64 */
+
+gint64 torrent_get_size(JsonObject * t);
+const gchar *torrent_get_name(JsonObject * t);
+gint64 torrent_get_rate_down(JsonObject * t);
+gint64 torrent_get_rate_up(JsonObject * t);
+gint64 torrent_get_eta(JsonObject * t);
+gint64 torrent_get_uploaded(JsonObject * t);
+gint64 torrent_get_downloaded(JsonObject * t);
+const gchar *torrent_get_errorstr(JsonObject * t);
+gint64 torrent_get_have_unchecked(JsonObject * t);
+gint64 torrent_get_have_valid(JsonObject * t);
+gint64 torrent_get_status(JsonObject * t);
+gchar *torrent_get_status_string(gint64 value);
+gchar *torrent_get_status_icon(guint flags);
+JsonArray *torrent_get_peers(JsonObject * t);
+JsonArray *torrent_get_trackers(JsonObject * t);
+JsonArray *torrent_get_wanted(JsonObject * t);
+JsonArray *torrent_get_priorities(JsonObject * t);
+gint64 torrent_get_id(JsonObject * t);
+gint64 tracker_get_tier(JsonObject * t);
+const gchar *tracker_get_announce(JsonObject * t);
+const gchar *tracker_get_scrape(JsonObject * t);
+JsonArray *torrent_get_files(JsonObject * args);
+gdouble torrent_get_percent_done(JsonObject * t);
+gint64 torrent_get_left_until_done(JsonObject * t);
+gboolean torrent_get_is_finished(JsonObject * t);
+gboolean torrent_get_honors_session_limits(JsonObject * t);
+gint64 torrent_get_bandwidth_priority(JsonObject * t);
+gint64 torrent_get_upload_limit(JsonObject * t);
+gboolean torrent_get_upload_limited(JsonObject * t);
+gint64 torrent_get_download_limit(JsonObject * t);
+gboolean torrent_get_download_limited(JsonObject * t);
+gdouble torrent_get_seed_ratio_limit(JsonObject * t);
+gint64 torrent_get_seed_ratio_mode(JsonObject * t);
+gint64 torrent_get_peer_limit(JsonObject * t);
+
+JsonArray *get_torrents(JsonObject * response);
+
+#endif /* TORRENT_H_ */
diff --git a/src/tpeer.c b/src/tpeer.c
new file mode 100644
index 0000000..37e63b2
--- /dev/null
+++ b/src/tpeer.c
@@ -0,0 +1,71 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <glib-object.h>
+#include <json-glib/json-glib.h>
+
+#include "tpeer.h"
+#include "protocol-constants.h"
+
+const gchar *peer_get_address(JsonObject * p)
+{
+ return json_object_get_string_member(p, TPEER_ADDRESS);
+}
+
+const gchar *peer_get_flagstr(JsonObject * p)
+{
+ return json_object_get_string_member(p, TPEER_FLAGSTR);
+}
+
+const gchar *peer_get_client_name(JsonObject * p)
+{
+ return json_object_get_string_member(p, TPEER_CLIENT_NAME);
+}
+
+gboolean peer_get_is_encrypted(JsonObject * p)
+{
+ return json_object_get_boolean_member(p, TPEER_IS_ENCRYPTED);
+}
+
+gboolean peer_get_is_uploading_to(JsonObject * p)
+{
+ return json_object_get_boolean_member(p, TPEER_IS_UPLOADING_TO);
+}
+
+gboolean peer_get_is_downloading_from(JsonObject * p)
+{
+ return json_object_get_boolean_member(p, TPEER_IS_DOWNLOADING_FROM);
+}
+
+gdouble peer_get_progress(JsonObject * p)
+{
+ return json_object_get_double_member(p, TPEER_PROGRESS) * 100.0;
+}
+
+gint64 peer_get_rate_to_client(JsonObject * p)
+{
+ return (gint64)
+ json_node_get_int(json_object_get_member(p, TPEER_RATE_TO_CLIENT));
+}
+
+gint64 peer_get_rate_to_peer(JsonObject * p)
+{
+ return (gint64)
+ json_node_get_int(json_object_get_member(p, TPEER_RATE_TO_PEER));
+}
diff --git a/src/tpeer.h b/src/tpeer.h
new file mode 100644
index 0000000..c9a0e02
--- /dev/null
+++ b/src/tpeer.h
@@ -0,0 +1,47 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TPEER_H_
+#define TPEER_H_
+
+#include <glib-object.h>
+#include <json-glib/json-glib.h>
+
+#define TPEER_ADDRESS "address"
+#define TPEER_CLIENT_NAME "client_name"
+#define TPEER_PROGRESS "progress"
+#define TPEER_RATE_TO_CLIENT "rateToClient"
+#define TPEER_RATE_TO_PEER "rateToPeer"
+#define TPEER_IS_ENCRYPTED "isEncrypted"
+#define TPEER_IS_DOWNLOADING_FROM "isDownloadingFrom"
+#define TPEER_IS_UPLOADING_TO "isUploadingTo"
+
+#define TPEER_FLAGSTR "flagStr"
+
+const gchar *peer_get_address(JsonObject * p);
+const gchar *peer_get_client_name(JsonObject * p);
+gboolean peer_get_is_encrypted(JsonObject * p);
+gdouble peer_get_progress(JsonObject * p);
+const gchar *peer_get_flagstr(JsonObject * p);
+gint64 peer_get_rate_to_client(JsonObject * p);
+gint64 peer_get_rate_to_peer(JsonObject * p);
+gboolean peer_get_is_uploading_to(JsonObject * p);
+gboolean peer_get_is_downloading_from(JsonObject * p);
+
+#endif /* TPEER_H_ */
diff --git a/src/transmission-remote-gtk.desktop b/src/transmission-remote-gtk.desktop
new file mode 100644
index 0000000..7d608e0
--- /dev/null
+++ b/src/transmission-remote-gtk.desktop
@@ -0,0 +1,12 @@
+[Desktop Entry]
+Name=transmission-remote-gtk
+GenericName=Remote BitTorrent Client
+X-GNOME-FullName=Transmission BitTorrent Remote Control
+Comment=Remotely control Transmission BitTorrent client
+Exec=/usr/local/bin/transmission-remote-gtk %F
+Icon=transmission
+Terminal=false
+TryExec=transmission-remote-gtk
+Type=Application
+MimeType=application/x-bittorrent;
+Categories=Network;FileTransfer;P2P;GTK;
diff --git a/src/transmission-remote-gtk.desktop.in b/src/transmission-remote-gtk.desktop.in
new file mode 100644
index 0000000..3edeb63
--- /dev/null
+++ b/src/transmission-remote-gtk.desktop.in
@@ -0,0 +1,12 @@
+[Desktop Entry]
+Name=transmission-remote-gtk
+GenericName=Remote BitTorrent Client
+X-GNOME-FullName=Transmission BitTorrent Remote Control
+Comment=Remotely control Transmission BitTorrent client
+Exec=@bindir@/transmission-remote-gtk %F
+Icon=transmission
+Terminal=false
+TryExec=transmission-remote-gtk
+Type=Application
+MimeType=application/x-bittorrent;
+Categories=Network;FileTransfer;P2P;GTK;
diff --git a/src/transmission-remote-gtk.schemas b/src/transmission-remote-gtk.schemas
new file mode 100644
index 0000000..11c8cdb
--- /dev/null
+++ b/src/transmission-remote-gtk.schemas
@@ -0,0 +1,109 @@
+<gconfschemafile>
+ <schemalist>
+
+ <schema>
+ <key>/schemas/apps/trg/auto-connect</key>
+ <applyto>/apps/trg/auto-connect</applyto>
+ <owner>trg</owner>
+ <type>bool</type>
+ <default>0</default>
+
+ <locale name="C">
+ <short>Auto connect</short>
+ <long>Auto connect</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/trg/system-tray</key>
+ <applyto>/apps/trg/system-tray</applyto>
+ <owner>trg</owner>
+ <type>bool</type>
+ <default>0</default>
+
+ <locale name="C">
+ <short>Show system tray icon</short>
+ <long>Show system tray icon while app is running</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/trg/window_height</key>
+ <applyto>/apps/trg/window_height</applyto>
+ <owner>trg</owner>
+ <type>int</type>
+ <default>600</default>
+
+ <locale name="C">
+ <short>Window height</short>
+ <long>Window height</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/trg/window_width</key>
+ <applyto>/apps/trg/window_width</applyto>
+ <owner>trg</owner>
+ <type>int</type>
+ <default>800</default>
+
+ <locale name="C">
+ <short>Window width</short>
+ <long>Window width</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/trg/port</key>
+ <applyto>/apps/trg/port</applyto>
+ <owner>trg</owner>
+ <type>int</type>
+ <default>9091</default>
+
+ <locale name="C">
+ <short>Port</short>
+ <long>Port</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/trg/username</key>
+ <applyto>/apps/trg/username</applyto>
+ <owner>trg</owner>
+ <type>string</type>
+ <default></default>
+
+ <locale name="C">
+ <short>Username</short>
+ <long>Username</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/trg/password</key>
+ <applyto>/apps/trg/password</applyto>
+ <owner>trg</owner>
+ <type>string</type>
+ <default></default>
+
+ <locale name="C">
+ <short>Password</short>
+ <long>Password</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/trg/hostname</key>
+ <applyto>/apps/trg/hostname</applyto>
+ <owner>trg</owner>
+ <type>string</type>
+ <default></default>
+
+ <locale name="C">
+ <short>Hostname</short>
+ <long>Hostname</long>
+ </locale>
+ </schema>
+
+ </schemalist>
+</gconfschemafile>
diff --git a/src/trg-about-window.c b/src/trg-about-window.c
new file mode 100644
index 0000000..32e35ca
--- /dev/null
+++ b/src/trg-about-window.c
@@ -0,0 +1,82 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+
+#include "dispatch.h"
+#include "trg-about-window.h"
+#include "util.h"
+
+GtkWidget *trg_about_window_new(GtkWindow * parent)
+{
+ GtkWidget *dialog;
+ GdkPixbuf *logo;
+ GError *error = NULL;
+ const gchar *trgAuthors[] = { "Alan Fitton <alan@eth0.org.uk>", NULL };
+
+ dialog = gtk_about_dialog_new();
+ gtk_window_set_transient_for(GTK_WINDOW(dialog), parent);
+ gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
+
+ logo =
+ gdk_pixbuf_new_from_file("/usr/share/pixmaps/transmission.png",
+ &error);
+ if (error == NULL)
+ gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(dialog), logo);
+ else {
+ if (error->domain == GDK_PIXBUF_ERROR)
+ g_print("GdkPixbufError: %s\n", error->message);
+ else if (error->domain == G_FILE_ERROR)
+ g_print("GFileError: %s\n", error->message);
+ else
+ g_print
+ ("An error in the domain: %d has occurred!\n",
+ error->domain);
+ g_error_free(error);
+ }
+
+ /* Set application data that will be displayed in the main dialog. */
+ gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(dialog), PACKAGE_NAME);
+ gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(dialog),
+ PACKAGE_VERSION);
+ gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(dialog),
+ "(C) 2009 Alan F");
+ gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(dialog),
+ "A remote client to transmission-daemon.");
+ /* Set the license text, which is usually loaded from a file. Also, set the
+ * web site address and label. */
+ gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(dialog), "GPL2");
+ gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(dialog),
+ "http://eth0.org.uk/");
+ gtk_about_dialog_set_website_label(GTK_ABOUT_DIALOG(dialog),
+ "eth0.org.uk");
+
+ /* Set the application authors, documenters and translators. */
+ gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(dialog), trgAuthors);
+ /*gtk_about_dialog_set_documenters(GTK_ABOUT_DIALOG(dialog), documenters);
+ gtk_about_dialog_set_translator_credits(GTK_ABOUT_DIALOG(dialog),
+ "Translator #1\nTranslator #2"); */
+ return dialog;
+}
diff --git a/src/trg-about-window.h b/src/trg-about-window.h
new file mode 100644
index 0000000..4b1dcbe
--- /dev/null
+++ b/src/trg-about-window.h
@@ -0,0 +1,27 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef ABOUT_WINDOW_H_
+#define ABOUT_WINDOW_H_
+
+#include <gtk/gtk.h>
+
+GtkWidget *trg_about_window_new(GtkWindow * parent);
+
+#endif /* ABOUT_WINDOW_H_ */
diff --git a/src/trg-cell-renderer-eta.c b/src/trg-cell-renderer-eta.c
new file mode 100644
index 0000000..93ef7d7
--- /dev/null
+++ b/src/trg-cell-renderer-eta.c
@@ -0,0 +1,116 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdint.h>
+#include <gtk/gtk.h>
+
+#include "trg-cell-renderer-eta.h"
+#include "util.h"
+
+enum {
+ PROP_0,
+ PROP_ETA_VALUE
+};
+
+G_DEFINE_TYPE(TrgCellRendererEta, trg_cell_renderer_eta,
+ GTK_TYPE_CELL_RENDERER_TEXT)
+#define TRG_CELL_RENDERER_ETA_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_CELL_RENDERER_ETA, TrgCellRendererEtaPrivate))
+typedef struct _TrgCellRendererEtaPrivate TrgCellRendererEtaPrivate;
+
+struct _TrgCellRendererEtaPrivate {
+ gdouble eta_value;
+};
+
+static void
+trg_cell_renderer_eta_get_property(GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ TrgCellRendererEtaPrivate *priv =
+ TRG_CELL_RENDERER_ETA_GET_PRIVATE(object);
+ switch (property_id) {
+ case PROP_ETA_VALUE:
+ g_value_set_int64(value, priv->eta_value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_cell_renderer_eta_set_property(GObject * object, guint property_id,
+ const GValue * value,
+ GParamSpec * pspec)
+{
+ TrgCellRendererEtaPrivate *priv =
+ TRG_CELL_RENDERER_ETA_GET_PRIVATE(object);
+
+ if (property_id == PROP_ETA_VALUE) {
+ priv->eta_value = g_value_get_int64(value);
+ if (priv->eta_value > 0) {
+ char etaString[32];
+ tr_strltime_short(etaString, priv->eta_value,
+ sizeof(etaString));
+ g_object_set(object, "text", etaString, NULL);
+ } else if (priv->eta_value == -2) {
+ g_object_set(object, "text", "∞", NULL);
+ } else {
+ g_object_set(object, "text", "", NULL);
+ }
+ } else {
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_cell_renderer_eta_class_init(TrgCellRendererEtaClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ object_class->get_property = trg_cell_renderer_eta_get_property;
+ object_class->set_property = trg_cell_renderer_eta_set_property;
+
+ g_object_class_install_property(object_class,
+ PROP_ETA_VALUE,
+ g_param_spec_int64("eta-value",
+ "Eta Value",
+ "Eta Value",
+ INT64_MIN,
+ INT64_MAX,
+ 0,
+ G_PARAM_READWRITE
+ |
+ G_PARAM_STATIC_NAME
+ |
+ G_PARAM_STATIC_NICK
+ |
+ G_PARAM_STATIC_BLURB));
+
+ g_type_class_add_private(klass, sizeof(TrgCellRendererEtaPrivate));
+}
+
+static void trg_cell_renderer_eta_init(TrgCellRendererEta * self)
+{
+}
+
+GtkCellRenderer *trg_cell_renderer_eta_new(void)
+{
+ return
+ GTK_CELL_RENDERER(g_object_new(TRG_TYPE_CELL_RENDERER_ETA, NULL));
+}
diff --git a/src/trg-cell-renderer-eta.h b/src/trg-cell-renderer-eta.h
new file mode 100644
index 0000000..62b0b4b
--- /dev/null
+++ b/src/trg-cell-renderer-eta.h
@@ -0,0 +1,51 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TRG_CELL_RENDERER_ETA_H_
+#define TRG_CELL_RENDERER_ETA_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+#define TRG_TYPE_CELL_RENDERER_ETA trg_cell_renderer_eta_get_type()
+#define TRG_CELL_RENDERER_ETA(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_CELL_RENDERER_ETA, TrgCellRendererEta))
+#define TRG_CELL_RENDERER_ETA_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_CELL_RENDERER_ETA, TrgCellRendererEtaClass))
+#define TRG_IS_CELL_RENDERER_ETA(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_CELL_RENDERER_ETA))
+#define TRG_IS_CELL_RENDERER_ETA_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_CELL_RENDERER_ETA))
+#define TRG_CELL_RENDERER_ETA_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_CELL_RENDERER_ETA, TrgCellRendererEtaClass))
+ typedef struct {
+ GtkCellRendererText parent;
+} TrgCellRendererEta;
+
+typedef struct {
+ GtkCellRendererTextClass parent_class;
+} TrgCellRendererEtaClass;
+
+GType trg_cell_renderer_eta_get_type(void);
+
+GtkCellRenderer *trg_cell_renderer_eta_new(void);
+
+G_END_DECLS
+#endif /* TRG_CELL_RENDERER_ETA_H_ */
diff --git a/src/trg-cell-renderer-priority.c b/src/trg-cell-renderer-priority.c
new file mode 100644
index 0000000..549bab7
--- /dev/null
+++ b/src/trg-cell-renderer-priority.c
@@ -0,0 +1,115 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdint.h>
+#include <gtk/gtk.h>
+
+#include "trg-cell-renderer-priority.h"
+#include "trg-files-model.h"
+#include "util.h"
+
+enum {
+ PROP_0,
+ PROP_PRIORITY_VALUE
+};
+
+G_DEFINE_TYPE(TrgCellRendererPriority, trg_cell_renderer_priority,
+ GTK_TYPE_CELL_RENDERER_TEXT)
+#define TRG_CELL_RENDERER_PRIORITY_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_CELL_RENDERER_PRIORITY, TrgCellRendererPriorityPrivate))
+typedef struct _TrgCellRendererPriorityPrivate
+ TrgCellRendererPriorityPrivate;
+
+struct _TrgCellRendererPriorityPrivate {
+ gint64 priority_value;
+};
+
+static void
+trg_cell_renderer_priority_get_property(GObject * object,
+ guint property_id, GValue * value,
+ GParamSpec * pspec)
+{
+ TrgCellRendererPriorityPrivate *priv =
+ TRG_CELL_RENDERER_PRIORITY_GET_PRIVATE(object);
+ switch (property_id) {
+ case PROP_PRIORITY_VALUE:
+ g_value_set_int64(value, priv->priority_value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_cell_renderer_priority_set_property(GObject * object,
+ guint property_id,
+ const GValue * value,
+ GParamSpec * pspec)
+{
+ TrgCellRendererPriorityPrivate *priv =
+ TRG_CELL_RENDERER_PRIORITY_GET_PRIVATE(object);
+
+ if (property_id == PROP_PRIORITY_VALUE) {
+ priv->priority_value = g_value_get_int64(value);
+ if (priv->priority_value == T_PRIORITY_LOW) {
+ g_object_set(object, "text", "Low", NULL);
+ } else if (priv->priority_value == T_PRIORITY_HIGH) {
+ g_object_set(object, "text", "High", NULL);
+ } else {
+ g_object_set(object, "text", "Normal", NULL);
+ }
+ } else {
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_cell_renderer_priority_class_init(TrgCellRendererPriorityClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ object_class->get_property = trg_cell_renderer_priority_get_property;
+ object_class->set_property = trg_cell_renderer_priority_set_property;
+
+ g_object_class_install_property(object_class,
+ PROP_PRIORITY_VALUE,
+ g_param_spec_int64
+ ("priority-value",
+ "Priority Value",
+ "Priority Value", T_PRIORITY_LOW,
+ T_PRIORITY_HIGH, T_PRIORITY_NORMAL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+
+ g_type_class_add_private(klass,
+ sizeof(TrgCellRendererPriorityPrivate));
+}
+
+static void trg_cell_renderer_priority_init(TrgCellRendererPriority * self)
+{
+}
+
+GtkCellRenderer *trg_cell_renderer_priority_new(void)
+{
+ return
+ GTK_CELL_RENDERER(g_object_new
+ (TRG_TYPE_CELL_RENDERER_PRIORITY, NULL));
+}
diff --git a/src/trg-cell-renderer-priority.h b/src/trg-cell-renderer-priority.h
new file mode 100644
index 0000000..d6d39c2
--- /dev/null
+++ b/src/trg-cell-renderer-priority.h
@@ -0,0 +1,52 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef TRG_CELL_RENDERER_PRIORITY_H_
+#define TRG_CELL_RENDERER_PRIORITY_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+#define TRG_TYPE_CELL_RENDERER_PRIORITY trg_cell_renderer_priority_get_type()
+#define TRG_CELL_RENDERER_PRIORITY(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_CELL_RENDERER_PRIORITY, TrgCellRendererPriority))
+#define TRG_CELL_RENDERER_PRIORITY_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_CELL_RENDERER_PRIORITY, TrgCellRendererPriorityClass))
+#define TRG_IS_CELL_RENDERER_PRIORITY(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_CELL_RENDERER_PRIORITY))
+#define TRG_IS_CELL_RENDERER_PRIORITY_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_CELL_RENDERER_PRIORITY))
+#define TRG_CELL_RENDERER_PRIORITY_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_CELL_RENDERER_PRIORITY, TrgCellRendererPriorityClass))
+ typedef struct {
+ GtkCellRendererText parent;
+} TrgCellRendererPriority;
+
+typedef struct {
+ GtkCellRendererTextClass parent_class;
+} TrgCellRendererPriorityClass;
+
+GType trg_cell_renderer_priority_get_type(void);
+
+GtkCellRenderer *trg_cell_renderer_priority_new(void);
+
+G_END_DECLS
+#endif /* TRG_CELL_RENDERER_PRIORITY_H_ */
diff --git a/src/trg-cell-renderer-ratio.c b/src/trg-cell-renderer-ratio.c
new file mode 100644
index 0000000..f08b220
--- /dev/null
+++ b/src/trg-cell-renderer-ratio.c
@@ -0,0 +1,113 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <limits.h>
+#include <gtk/gtk.h>
+
+#include "trg-cell-renderer-ratio.h"
+#include "util.h"
+
+enum {
+ PROP_0,
+ PROP_RATIO_VALUE
+};
+
+G_DEFINE_TYPE(TrgCellRendererRatio, trg_cell_renderer_ratio,
+ GTK_TYPE_CELL_RENDERER_TEXT)
+#define TRG_CELL_RENDERER_RATIO_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_CELL_RENDERER_RATIO, TrgCellRendererRatioPrivate))
+typedef struct _TrgCellRendererRatioPrivate TrgCellRendererRatioPrivate;
+
+struct _TrgCellRendererRatioPrivate {
+ gdouble ratio_value;
+};
+
+static void
+trg_cell_renderer_ratio_get_property(GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ TrgCellRendererRatioPrivate *priv =
+ TRG_CELL_RENDERER_RATIO_GET_PRIVATE(object);
+ switch (property_id) {
+ case PROP_RATIO_VALUE:
+ g_value_set_double(value, priv->ratio_value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_cell_renderer_ratio_set_property(GObject * object, guint property_id,
+ const GValue * value,
+ GParamSpec * pspec)
+{
+ TrgCellRendererRatioPrivate *priv =
+ TRG_CELL_RENDERER_RATIO_GET_PRIVATE(object);
+ if (property_id == PROP_RATIO_VALUE) {
+ priv->ratio_value = g_value_get_double(value);
+ if (priv->ratio_value > 0) {
+ char ratioString[32];
+ trg_strlratio(ratioString, priv->ratio_value);
+ g_object_set(object, "text", ratioString, NULL);
+ } else {
+ g_object_set(object, "text", "", NULL);
+ }
+ } else {
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_cell_renderer_ratio_class_init(TrgCellRendererRatioClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ object_class->get_property = trg_cell_renderer_ratio_get_property;
+ object_class->set_property = trg_cell_renderer_ratio_set_property;
+
+ g_object_class_install_property(object_class,
+ PROP_RATIO_VALUE,
+ g_param_spec_double("ratio-value",
+ "Ratio Value",
+ "Ratio Value",
+ 0,
+ DBL_MAX,
+ 0,
+ G_PARAM_READWRITE
+ |
+ G_PARAM_STATIC_NAME
+ |
+ G_PARAM_STATIC_NICK
+ |
+ G_PARAM_STATIC_BLURB));
+
+ g_type_class_add_private(klass, sizeof(TrgCellRendererRatioPrivate));
+}
+
+static void trg_cell_renderer_ratio_init(TrgCellRendererRatio * self)
+{
+}
+
+GtkCellRenderer *trg_cell_renderer_ratio_new(void)
+{
+ return
+ GTK_CELL_RENDERER(g_object_new
+ (TRG_TYPE_CELL_RENDERER_RATIO, NULL));
+}
diff --git a/src/trg-cell-renderer-ratio.h b/src/trg-cell-renderer-ratio.h
new file mode 100644
index 0000000..64c3e82
--- /dev/null
+++ b/src/trg-cell-renderer-ratio.h
@@ -0,0 +1,51 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TRG_CELL_RENDERER_RATIO_H_
+#define TRG_CELL_RENDERER_RATIO_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+#define TRG_TYPE_CELL_RENDERER_RATIO trg_cell_renderer_ratio_get_type()
+#define TRG_CELL_RENDERER_RATIO(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_CELL_RENDERER_RATIO, TrgCellRendererRatio))
+#define TRG_CELL_RENDERER_RATIO_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_CELL_RENDERER_RATIO, TrgCellRendererRatioClass))
+#define TRG_IS_CELL_RENDERER_RATIO(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_CELL_RENDERER_RATIO))
+#define TRG_IS_CELL_RENDERER_RATIO_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_CELL_RENDERER_RATIO))
+#define TRG_CELL_RENDERER_RATIO_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_CELL_RENDERER_RATIO, TrgCellRendererRatioClass))
+ typedef struct {
+ GtkCellRendererText parent;
+} TrgCellRendererRatio;
+
+typedef struct {
+ GtkCellRendererTextClass parent_class;
+} TrgCellRendererRatioClass;
+
+GType trg_cell_renderer_ratio_get_type(void);
+
+GtkCellRenderer *trg_cell_renderer_ratio_new(void);
+
+G_END_DECLS
+#endif /* TRG_CELL_RENDERER_RATIO_H_ */
diff --git a/src/trg-cell-renderer-size.c b/src/trg-cell-renderer-size.c
new file mode 100644
index 0000000..758c3f5
--- /dev/null
+++ b/src/trg-cell-renderer-size.c
@@ -0,0 +1,112 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdint.h>
+#include <gtk/gtk.h>
+
+#include "trg-cell-renderer-size.h"
+#include "util.h"
+
+enum {
+ PROP_0,
+ PROP_SIZE_VALUE
+};
+
+G_DEFINE_TYPE(TrgCellRendererSize, trg_cell_renderer_size,
+ GTK_TYPE_CELL_RENDERER_TEXT)
+#define TRG_CELL_RENDERER_SIZE_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_CELL_RENDERER_SIZE, TrgCellRendererSizePrivate))
+typedef struct _TrgCellRendererSizePrivate TrgCellRendererSizePrivate;
+
+struct _TrgCellRendererSizePrivate {
+ gint64 size_value;
+};
+
+static void
+trg_cell_renderer_size_get_property(GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ TrgCellRendererSizePrivate *priv =
+ TRG_CELL_RENDERER_SIZE_GET_PRIVATE(object);
+ switch (property_id) {
+ case PROP_SIZE_VALUE:
+ g_value_set_int64(value, priv->size_value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_cell_renderer_size_set_property(GObject * object, guint property_id,
+ const GValue * value,
+ GParamSpec * pspec)
+{
+ TrgCellRendererSizePrivate *priv =
+ TRG_CELL_RENDERER_SIZE_GET_PRIVATE(object);
+ if (property_id == PROP_SIZE_VALUE) {
+ priv->size_value = g_value_get_int64(value);
+ if (priv->size_value > 0) {
+ char sizeString[32];
+ trg_strlsize(sizeString, priv->size_value);
+ g_object_set(object, "text", sizeString, NULL);
+ } else {
+ g_object_set(object, "text", "", NULL);
+ }
+ } else {
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_cell_renderer_size_class_init(TrgCellRendererSizeClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ object_class->get_property = trg_cell_renderer_size_get_property;
+ object_class->set_property = trg_cell_renderer_size_set_property;
+
+ g_object_class_install_property(object_class,
+ PROP_SIZE_VALUE,
+ g_param_spec_int64("size-value",
+ "Size Value",
+ "Size Value",
+ 0,
+ INT64_MAX,
+ 0,
+ G_PARAM_READWRITE
+ |
+ G_PARAM_STATIC_NAME
+ |
+ G_PARAM_STATIC_NICK
+ |
+ G_PARAM_STATIC_BLURB));
+
+ g_type_class_add_private(klass, sizeof(TrgCellRendererSizePrivate));
+}
+
+static void trg_cell_renderer_size_init(TrgCellRendererSize * self)
+{
+}
+
+GtkCellRenderer *trg_cell_renderer_size_new(void)
+{
+ return
+ GTK_CELL_RENDERER(g_object_new(TRG_TYPE_CELL_RENDERER_SIZE, NULL));
+}
diff --git a/src/trg-cell-renderer-size.h b/src/trg-cell-renderer-size.h
new file mode 100644
index 0000000..5b901e3
--- /dev/null
+++ b/src/trg-cell-renderer-size.h
@@ -0,0 +1,52 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef TRG_CELL_RENDERER_SIZE_H_
+#define TRG_CELL_RENDERER_SIZE_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+#define TRG_TYPE_CELL_RENDERER_SIZE trg_cell_renderer_size_get_type()
+#define TRG_CELL_RENDERER_SIZE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_CELL_RENDERER_SIZE, TrgCellRendererSize))
+#define TRG_CELL_RENDERER_SIZE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_CELL_RENDERER_SIZE, TrgCellRendererSizeClass))
+#define TRG_IS_CELL_RENDERER_SIZE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_CELL_RENDERER_SIZE))
+#define TRG_IS_CELL_RENDERER_SIZE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_CELL_RENDERER_SIZE))
+#define TRG_CELL_RENDERER_SIZE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_CELL_RENDERER_SIZE, TrgCellRendererSizeClass))
+ typedef struct {
+ GtkCellRendererText parent;
+} TrgCellRendererSize;
+
+typedef struct {
+ GtkCellRendererTextClass parent_class;
+} TrgCellRendererSizeClass;
+
+GType trg_cell_renderer_size_get_type(void);
+
+GtkCellRenderer *trg_cell_renderer_size_new(void);
+
+G_END_DECLS
+#endif /* TRG_CELL_RENDERER_SIZE_H_ */
diff --git a/src/trg-cell-renderer-speed.c b/src/trg-cell-renderer-speed.c
new file mode 100644
index 0000000..35ccc5f
--- /dev/null
+++ b/src/trg-cell-renderer-speed.c
@@ -0,0 +1,114 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdint.h>
+#include <gtk/gtk.h>
+
+#include "trg-cell-renderer-speed.h"
+#include "util.h"
+
+enum {
+ PROP_0,
+ PROP_SPEED_VALUE
+};
+
+G_DEFINE_TYPE(TrgCellRendererSpeed, trg_cell_renderer_speed,
+ GTK_TYPE_CELL_RENDERER_TEXT)
+#define TRG_CELL_RENDERER_SPEED_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_CELL_RENDERER_SPEED, TrgCellRendererSpeedPrivate))
+typedef struct _TrgCellRendererSpeedPrivate TrgCellRendererSpeedPrivate;
+
+struct _TrgCellRendererSpeedPrivate {
+ gint64 speed_value;
+};
+
+static void
+trg_cell_renderer_speed_get_property(GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ TrgCellRendererSpeedPrivate *priv =
+ TRG_CELL_RENDERER_SPEED_GET_PRIVATE(object);
+ switch (property_id) {
+ case PROP_SPEED_VALUE:
+ g_value_set_int64(value, priv->speed_value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_cell_renderer_speed_set_property(GObject * object, guint property_id,
+ const GValue * value,
+ GParamSpec * pspec)
+{
+ TrgCellRendererSpeedPrivate *priv =
+ TRG_CELL_RENDERER_SPEED_GET_PRIVATE(object);
+ if (property_id == PROP_SPEED_VALUE) {
+ priv->speed_value = g_value_get_int64(value);
+ if (priv->speed_value > 0) {
+ char speedString[32];
+ trg_strlspeed(speedString,
+ priv->speed_value / KILOBYTE_FACTOR);
+ g_object_set(object, "text", speedString, NULL);
+ } else {
+ g_object_set(object, "text", "", NULL);
+ }
+ } else {
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_cell_renderer_speed_class_init(TrgCellRendererSpeedClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ object_class->get_property = trg_cell_renderer_speed_get_property;
+ object_class->set_property = trg_cell_renderer_speed_set_property;
+
+ g_object_class_install_property(object_class,
+ PROP_SPEED_VALUE,
+ g_param_spec_int64("speed-value",
+ "Speed Value",
+ "Speed Value",
+ 0,
+ INT64_MAX,
+ 0,
+ G_PARAM_READWRITE
+ |
+ G_PARAM_STATIC_NAME
+ |
+ G_PARAM_STATIC_NICK
+ |
+ G_PARAM_STATIC_BLURB));
+
+ g_type_class_add_private(klass, sizeof(TrgCellRendererSpeedPrivate));
+}
+
+static void trg_cell_renderer_speed_init(TrgCellRendererSpeed * self)
+{
+}
+
+GtkCellRenderer *trg_cell_renderer_speed_new(void)
+{
+ return
+ GTK_CELL_RENDERER(g_object_new
+ (TRG_TYPE_CELL_RENDERER_SPEED, NULL));
+}
diff --git a/src/trg-cell-renderer-speed.h b/src/trg-cell-renderer-speed.h
new file mode 100644
index 0000000..9a9bac6
--- /dev/null
+++ b/src/trg-cell-renderer-speed.h
@@ -0,0 +1,52 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef TRG_CELL_RENDERER_SPEED_H_
+#define TRG_CELL_RENDERER_SPEED_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+#define TRG_TYPE_CELL_RENDERER_SPEED trg_cell_renderer_speed_get_type()
+#define TRG_CELL_RENDERER_SPEED(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_CELL_RENDERER_SPEED, TrgCellRendererSpeed))
+#define TRG_CELL_RENDERER_SPEED_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_CELL_RENDERER_SPEED, TrgCellRendererSpeedClass))
+#define TRG_IS_CELL_RENDERER_SPEED(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_CELL_RENDERER_SPEED))
+#define TRG_IS_CELL_RENDERER_SPEED_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_CELL_RENDERER_SPEED))
+#define TRG_CELL_RENDERER_SPEED_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_CELL_RENDERER_SPEED, TrgCellRendererSpeedClass))
+ typedef struct {
+ GtkCellRendererText parent;
+} TrgCellRendererSpeed;
+
+typedef struct {
+ GtkCellRendererTextClass parent_class;
+} TrgCellRendererSpeedClass;
+
+GType trg_cell_renderer_speed_get_type(void);
+
+GtkCellRenderer *trg_cell_renderer_speed_new(void);
+
+G_END_DECLS
+#endif /* TRG_CELL_RENDERER_SPEED_H_ */
diff --git a/src/trg-cell-renderer-wanted.c b/src/trg-cell-renderer-wanted.c
new file mode 100644
index 0000000..962dab1
--- /dev/null
+++ b/src/trg-cell-renderer-wanted.c
@@ -0,0 +1,105 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdint.h>
+#include <gtk/gtk.h>
+
+#include "trg-cell-renderer-wanted.h"
+#include "util.h"
+
+enum {
+ PROP_0,
+ PROP_WANTED_VALUE
+};
+
+G_DEFINE_TYPE(TrgCellRendererWanted, trg_cell_renderer_wanted,
+ GTK_TYPE_CELL_RENDERER_TEXT)
+#define TRG_CELL_RENDERER_WANTED_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_CELL_RENDERER_WANTED, TrgCellRendererWantedPrivate))
+typedef struct _TrgCellRendererWantedPrivate TrgCellRendererWantedPrivate;
+
+struct _TrgCellRendererWantedPrivate {
+ gboolean wanted_value;
+};
+
+static void
+trg_cell_renderer_wanted_get_property(GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ TrgCellRendererWantedPrivate *priv =
+ TRG_CELL_RENDERER_WANTED_GET_PRIVATE(object);
+ switch (property_id) {
+ case PROP_WANTED_VALUE:
+ g_value_set_boolean(value, priv->wanted_value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_cell_renderer_wanted_set_property(GObject * object, guint property_id,
+ const GValue * value,
+ GParamSpec * pspec)
+{
+ TrgCellRendererWantedPrivate *priv =
+ TRG_CELL_RENDERER_WANTED_GET_PRIVATE(object);
+ if (property_id == PROP_WANTED_VALUE) {
+ priv->wanted_value = g_value_get_boolean(value);
+ if (priv->wanted_value) {
+ g_object_set(object, "text", "Yes", NULL);
+ } else {
+ g_object_set(object, "text", "No", NULL);
+ }
+ } else {
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_cell_renderer_wanted_class_init(TrgCellRendererWantedClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ object_class->get_property = trg_cell_renderer_wanted_get_property;
+ object_class->set_property = trg_cell_renderer_wanted_set_property;
+
+ g_object_class_install_property(object_class,
+ PROP_WANTED_VALUE,
+ g_param_spec_boolean
+ ("wanted-value", "Wanted Value",
+ "Wanted Value", TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+
+ g_type_class_add_private(klass, sizeof(TrgCellRendererWantedPrivate));
+}
+
+static void trg_cell_renderer_wanted_init(TrgCellRendererWanted * self)
+{
+}
+
+GtkCellRenderer *trg_cell_renderer_wanted_new(void)
+{
+ return
+ GTK_CELL_RENDERER(g_object_new
+ (TRG_TYPE_CELL_RENDERER_WANTED, NULL));
+}
diff --git a/src/trg-cell-renderer-wanted.h b/src/trg-cell-renderer-wanted.h
new file mode 100644
index 0000000..02920ac
--- /dev/null
+++ b/src/trg-cell-renderer-wanted.h
@@ -0,0 +1,52 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef TRG_CELL_RENDERER_WANTED_H_
+#define TRG_CELL_RENDERER_WANTED_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+#define TRG_TYPE_CELL_RENDERER_WANTED trg_cell_renderer_wanted_get_type()
+#define TRG_CELL_RENDERER_WANTED(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_CELL_RENDERER_WANTED, TrgCellRendererWanted))
+#define TRG_CELL_RENDERER_WANTED_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_CELL_RENDERER_WANTED, TrgCellRendererWantedClass))
+#define TRG_IS_CELL_RENDERER_WANTED(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_CELL_RENDERER_WANTED))
+#define TRG_IS_CELL_RENDERER_WANTED_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_CELL_RENDERER_WANTED))
+#define TRG_CELL_RENDERER_WANTED_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_CELL_RENDERER_WANTED, TrgCellRendererWantedClass))
+ typedef struct {
+ GtkCellRendererText parent;
+} TrgCellRendererWanted;
+
+typedef struct {
+ GtkCellRendererTextClass parent_class;
+} TrgCellRendererWantedClass;
+
+GType trg_cell_renderer_wanted_get_type(void);
+
+GtkCellRenderer *trg_cell_renderer_wanted_new(void);
+
+G_END_DECLS
+#endif /* TRG_CELL_RENDERER_WANTED_H_ */
diff --git a/src/trg-client.c b/src/trg-client.c
new file mode 100644
index 0000000..76824b9
--- /dev/null
+++ b/src/trg-client.c
@@ -0,0 +1,80 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <glib-object.h>
+#include <gconf/gconf-client.h>
+
+#include "trg-client.h"
+#include "trg-preferences.h"
+
+trg_client *trg_init_client()
+{
+ trg_client *client;
+
+ client = g_new0(trg_client, 1);
+ client->gconf = gconf_client_get_default();
+ client->updateMutex = g_mutex_new();
+
+ return client;
+}
+
+gboolean trg_client_populate_with_settings(trg_client * tc,
+ GConfClient * gconf)
+{
+ gint port;
+ gchar *host;
+ GError *error = NULL;
+
+ g_free(tc->url);
+ tc->url = NULL;
+
+ g_free(tc->username);
+ tc->username = NULL;
+
+ g_free(tc->password);
+ tc->password = NULL;
+
+ port = gconf_client_get_int(gconf, TRG_GCONF_KEY_PORT, &error);
+ if (error != NULL) {
+ g_error_free(error);
+ return FALSE;
+ }
+
+ if ((host =
+ gconf_client_get_string(gconf, TRG_GCONF_KEY_HOSTNAME,
+ NULL)) == NULL) {
+ return FALSE;
+ } else {
+ tc->url =
+ g_strdup_printf("http://%s:%d/transmission/rpc", host, port);
+ g_free(host);
+ }
+
+ if ((tc->username =
+ gconf_client_get_string(gconf, TRG_GCONF_KEY_USERNAME,
+ NULL)) == NULL)
+ return FALSE;
+
+ if ((tc->password =
+ gconf_client_get_string(gconf, TRG_GCONF_KEY_PASSWORD,
+ NULL)) == NULL)
+ return FALSE;
+
+ return TRUE;
+}
diff --git a/src/trg-client.h b/src/trg-client.h
new file mode 100644
index 0000000..e75e979
--- /dev/null
+++ b/src/trg-client.h
@@ -0,0 +1,45 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TRG_CLIENT_H_
+#define TRG_CLIENT_H_
+
+#include <json-glib/json-glib.h>
+#include <gconf/gconf-client.h>
+
+#include "session-get.h"
+
+typedef struct {
+ char *session_id;
+ gint failCount;
+ gint64 updateSerial;
+ JsonObject *session;
+ char *url;
+ char *username;
+ char *password;
+ JsonArray *torrents;
+ GConfClient *gconf;
+ GMutex *updateMutex;
+} trg_client;
+
+trg_client *trg_init_client();
+gboolean trg_client_populate_with_settings(trg_client * tc,
+ GConfClient * gconf);
+
+#endif /* TRG_CLIENT_H_ */
diff --git a/src/trg-files-model.c b/src/trg-files-model.c
new file mode 100644
index 0000000..16f58f9
--- /dev/null
+++ b/src/trg-files-model.c
@@ -0,0 +1,177 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+
+#include "trg-files-model.h"
+#include "torrent.h"
+#include "tfile.h"
+#include "util.h"
+
+#include "trg-files-model.h"
+
+G_DEFINE_TYPE(TrgFilesModel, trg_files_model, GTK_TYPE_LIST_STORE)
+#define TRG_FILES_MODEL_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_FILES_MODEL, TrgFilesModelPrivate))
+typedef struct _TrgFilesModelPrivate TrgFilesModelPrivate;
+
+struct _TrgFilesModelPrivate {
+ gint torrentId;
+ JsonArray *files;
+ JsonArray *wanted;
+ JsonArray *priorities;
+ gint64 updateBarrier;
+ gint64 currentSerial;
+};
+
+static void trg_files_model_iter_new(TrgFilesModel * model,
+ GtkTreeIter * iter, JsonObject * file,
+ int id)
+{
+ char size[32];
+
+ gtk_list_store_append(GTK_LIST_STORE(model), iter);
+
+ trg_strlsize(size, file_get_length(file));
+ gtk_list_store_set(GTK_LIST_STORE(model), iter,
+ FILESCOL_NAME, file_get_name(file),
+ FILESCOL_SIZE, size, FILESCOL_ID, id, -1);
+}
+
+void trg_files_model_set_update_barrier(TrgFilesModel * model,
+ gint64 serial)
+{
+ TrgFilesModelPrivate *priv = TRG_FILES_MODEL_GET_PRIVATE(model);
+ priv->updateBarrier = serial;
+}
+
+static void
+trg_files_model_iter_update(TrgFilesModel * model,
+ GtkTreeIter * filesIter, JsonObject * file,
+ JsonArray * wantedArray,
+ JsonArray * prioritiesArray, int id)
+{
+ TrgFilesModelPrivate *priv = TRG_FILES_MODEL_GET_PRIVATE(model);
+
+ gboolean wanted = json_node_get_int(json_array_get_element
+ (wantedArray, id)) == 1;
+ gint64 priority =
+ json_node_get_int(json_array_get_element(prioritiesArray, id));
+ gdouble progress = file_get_progress(file);
+
+ gtk_list_store_set(GTK_LIST_STORE(model), filesIter,
+ FILESCOL_PROGRESS, progress, -1);
+
+ if (priv->updateBarrier < 0
+ || priv->currentSerial >= priv->updateBarrier) {
+ gtk_list_store_set(GTK_LIST_STORE(model), filesIter,
+ FILESCOL_ICON,
+ wanted ? GTK_STOCK_FILE :
+ GTK_STOCK_STOP, FILESCOL_WANTED, wanted,
+ FILESCOL_PRIORITY, priority, -1);
+ }
+}
+
+static void trg_files_model_class_init(TrgFilesModelClass * klass)
+{
+ g_type_class_add_private(klass, sizeof(TrgFilesModelPrivate));
+}
+
+static void trg_files_model_init(TrgFilesModel * self)
+{
+ TrgFilesModelPrivate *priv = TRG_FILES_MODEL_GET_PRIVATE(self);
+ GType column_types[FILESCOL_COLUMNS];
+
+ priv->updateBarrier = -1;
+
+ column_types[FILESCOL_ICON] = G_TYPE_STRING;
+ column_types[FILESCOL_NAME] = G_TYPE_STRING;
+ column_types[FILESCOL_SIZE] = G_TYPE_STRING;
+ column_types[FILESCOL_PROGRESS] = G_TYPE_DOUBLE;
+ column_types[FILESCOL_ID] = G_TYPE_INT;
+ column_types[FILESCOL_WANTED] = G_TYPE_BOOLEAN;
+ column_types[FILESCOL_PRIORITY] = G_TYPE_INT;
+
+ gtk_list_store_set_column_types(GTK_LIST_STORE(self),
+ FILESCOL_COLUMNS, column_types);
+}
+
+gboolean
+trg_files_model_update_foreach(GtkListStore * model,
+ GtkTreePath * path, GtkTreeIter * iter,
+ gpointer data)
+{
+ TrgFilesModelPrivate *priv = TRG_FILES_MODEL_GET_PRIVATE(model);
+ JsonObject *file;
+ gint id;
+
+ gtk_tree_model_get(GTK_TREE_MODEL(model), iter, FILESCOL_ID, &id, -1);
+
+ file = json_node_get_object(json_array_get_element(priv->files, id));
+ trg_files_model_iter_update(TRG_FILES_MODEL(model), iter, file,
+ priv->wanted, priv->priorities, id);
+
+ return FALSE;
+}
+
+void
+trg_files_model_update(TrgFilesModel * model, gint64 updateSerial,
+ JsonObject * t, gboolean first)
+{
+ TrgFilesModelPrivate *priv = TRG_FILES_MODEL_GET_PRIVATE(model);
+ int j;
+
+ if (first == TRUE)
+ gtk_list_store_clear(GTK_LIST_STORE(model));
+
+ priv->torrentId = torrent_get_id(t);
+ priv->priorities = torrent_get_priorities(t);
+ priv->wanted = torrent_get_wanted(t);
+ priv->files = torrent_get_files(t);
+ priv->currentSerial = updateSerial;
+
+ if (first == TRUE) {
+ for (j = 0; j < json_array_get_length(priv->files); j++) {
+ JsonObject *file =
+ json_node_get_object(json_array_get_element
+ (priv->files, j));
+ GtkTreeIter filesIter;
+ trg_files_model_iter_new(model, &filesIter, file, j);
+ trg_files_model_iter_update(model, &filesIter,
+ file, priv->wanted,
+ priv->priorities, j);
+ }
+ } else {
+ gtk_tree_model_foreach(GTK_TREE_MODEL(model),
+ (GtkTreeModelForeachFunc)
+ trg_files_model_update_foreach, NULL);
+ }
+}
+
+gint trg_files_model_get_torrent_id(TrgFilesModel * model)
+{
+ TrgFilesModelPrivate *priv = TRG_FILES_MODEL_GET_PRIVATE(model);
+ return priv->torrentId;
+}
+
+TrgFilesModel *trg_files_model_new(void)
+{
+ return g_object_new(TRG_TYPE_FILES_MODEL, NULL);
+}
diff --git a/src/trg-files-model.h b/src/trg-files-model.h
new file mode 100644
index 0000000..658ffd3
--- /dev/null
+++ b/src/trg-files-model.h
@@ -0,0 +1,74 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TRG_FILES_MODEL_H_
+#define TRG_FILES_MODEL_H_
+
+#include <glib-object.h>
+#include <json-glib/json-glib.h>
+
+#include "trg-model.h"
+
+G_BEGIN_DECLS
+#define TRG_TYPE_FILES_MODEL trg_files_model_get_type()
+#define TRG_FILES_MODEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_FILES_MODEL, TrgFilesModel))
+#define TRG_FILES_MODEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_FILES_MODEL, TrgFilesModelClass))
+#define TRG_IS_FILES_MODEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_FILES_MODEL))
+#define TRG_IS_FILES_MODEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_FILES_MODEL))
+#define TRG_FILES_MODEL_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_FILES_MODEL, TrgFilesModelClass))
+ typedef struct {
+ GtkListStore parent;
+} TrgFilesModel;
+
+typedef struct {
+ GtkListStoreClass parent_class;
+} TrgFilesModelClass;
+
+GType trg_files_model_get_type(void);
+
+TrgFilesModel *trg_files_model_new(void);
+
+G_END_DECLS enum {
+ FILESCOL_ICON,
+ FILESCOL_NAME,
+ FILESCOL_SIZE,
+ FILESCOL_PROGRESS,
+ FILESCOL_ID,
+ FILESCOL_WANTED,
+ FILESCOL_PRIORITY,
+ FILESCOL_COLUMNS
+};
+
+#define T_PRIORITY_LOW -1
+#define T_PRIORITY_NORMAL 0
+#define T_PRIORITY_HIGH 1
+
+void
+trg_files_model_update(TrgFilesModel * model, gint64 updateSerial,
+ JsonObject * t, gboolean first);
+gint trg_files_model_get_torrent_id(TrgFilesModel * model);
+void trg_files_model_set_update_barrier(TrgFilesModel * model,
+ gint64 serial);
+
+#endif /* TRG_FILES_MODEL_H_ */
diff --git a/src/trg-files-tree-view.c b/src/trg-files-tree-view.c
new file mode 100644
index 0000000..5486a08
--- /dev/null
+++ b/src/trg-files-tree-view.c
@@ -0,0 +1,369 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <gtk/gtk.h>
+
+#include "trg-client.h"
+#include "trg-tree-view.h"
+#include "trg-files-tree-view.h"
+#include "trg-files-model.h"
+#include "trg-cell-renderer-wanted.h"
+#include "trg-cell-renderer-priority.h"
+#include "trg-main-window.h"
+#include "requests.h"
+#include "json.h"
+#include "dispatch.h"
+#include "protocol-constants.h"
+
+G_DEFINE_TYPE(TrgFilesTreeView, trg_files_tree_view, TRG_TYPE_TREE_VIEW)
+#define TRG_FILES_TREE_VIEW_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_FILES_TREE_VIEW, TrgFilesTreeViewPrivate))
+typedef struct _TrgFilesTreeViewPrivate TrgFilesTreeViewPrivate;
+
+struct _TrgFilesTreeViewPrivate {
+ trg_client *client;
+ TrgMainWindow *win;
+};
+
+static void trg_files_tree_view_class_init(TrgFilesTreeViewClass * klass)
+{
+ g_type_class_add_private(klass, sizeof(TrgFilesTreeViewPrivate));
+}
+
+static void set_unwanted_foreachfunc(GtkTreeModel * model,
+ GtkTreePath * path,
+ GtkTreeIter * iter, gpointer data)
+{
+ gtk_list_store_set(GTK_LIST_STORE(model), iter, FILESCOL_WANTED,
+ FALSE, FILESCOL_ICON, GTK_STOCK_CANCEL, -1);
+}
+
+static void set_wanted_foreachfunc(GtkTreeModel * model,
+ GtkTreePath * path, GtkTreeIter * iter,
+ gpointer data)
+{
+ gtk_list_store_set(GTK_LIST_STORE(model), iter, FILESCOL_WANTED,
+ TRUE, FILESCOL_ICON, GTK_STOCK_FILE, -1);
+}
+
+static void set_low_foreachfunc(GtkTreeModel * model, GtkTreePath * path,
+ GtkTreeIter * iter, gpointer data)
+{
+ gtk_list_store_set(GTK_LIST_STORE(model), iter, FILESCOL_PRIORITY,
+ T_PRIORITY_LOW, -1);
+}
+
+static void set_normal_foreachfunc(GtkTreeModel * model,
+ GtkTreePath * path, GtkTreeIter * iter,
+ gpointer data)
+{
+ gtk_list_store_set(GTK_LIST_STORE(model), iter, FILESCOL_PRIORITY,
+ T_PRIORITY_NORMAL, -1);
+}
+
+static void set_high_foreachfunc(GtkTreeModel * model, GtkTreePath * path,
+ GtkTreeIter * iter, gpointer data)
+{
+ gtk_list_store_set(GTK_LIST_STORE(model), iter, FILESCOL_PRIORITY,
+ T_PRIORITY_HIGH, -1);
+}
+
+static void add_file_id_to_array(JsonObject * args, gchar * key,
+ gint index)
+{
+ JsonArray *array = json_object_get_array_member(args, key);
+ json_array_add_int_element(array, index);
+}
+
+static void send_updated_file_prefs_foreachfunc(GtkTreeModel * model,
+ GtkTreePath * path,
+ GtkTreeIter * iter,
+ gpointer data)
+{
+ JsonObject *args = (JsonObject *) data;
+ gint priority, id;
+ gboolean wanted;
+
+ gtk_tree_model_get(model, iter, FILESCOL_WANTED, &wanted,
+ FILESCOL_PRIORITY, &priority, FILESCOL_ID, &id, -1);
+
+ if (wanted == FALSE)
+ add_file_id_to_array(args, FIELD_FILES_UNWANTED, id);
+ else
+ add_file_id_to_array(args, FIELD_FILES_WANTED, id);
+
+ if (priority == T_PRIORITY_LOW)
+ add_file_id_to_array(args, FIELD_FILES_PRIORITY_LOW, id);
+ else if (priority == T_PRIORITY_HIGH)
+ add_file_id_to_array(args, FIELD_FILES_PRIORITY_HIGH, id);
+ else
+ add_file_id_to_array(args, FIELD_FILES_PRIORITY_NORMAL, id);
+}
+
+static void remove_array_if_empty(JsonObject * args, gchar * key)
+{
+ JsonArray *array = json_object_get_array_member(args, key);
+ if (json_array_get_length(array) == 0)
+ json_object_remove_member(args, key);
+}
+
+static void send_updated_file_prefs(TrgFilesTreeView * tv)
+{
+ TrgFilesTreeViewPrivate *priv = TRG_FILES_TREE_VIEW_GET_PRIVATE(tv);
+ JsonNode *req;
+ JsonObject *args;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ gint targetId;
+ JsonArray *targetIdArray;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv));
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(tv));
+ targetId = trg_files_model_get_torrent_id(TRG_FILES_MODEL(model));
+ targetIdArray = json_array_new();
+ json_array_add_int_element(targetIdArray, targetId);
+
+ req = torrent_set(targetIdArray);
+ args = node_get_arguments(req);
+
+ json_object_set_array_member(args, FIELD_FILES_WANTED,
+ json_array_new());
+ json_object_set_array_member(args, FIELD_FILES_UNWANTED,
+ json_array_new());
+ json_object_set_array_member(args, FIELD_FILES_PRIORITY_HIGH,
+ json_array_new());
+ json_object_set_array_member(args, FIELD_FILES_PRIORITY_NORMAL,
+ json_array_new());
+ json_object_set_array_member(args, FIELD_FILES_PRIORITY_LOW,
+ json_array_new());
+
+ gtk_tree_selection_selected_foreach(selection,
+ send_updated_file_prefs_foreachfunc,
+ args);
+
+ remove_array_if_empty(args, FIELD_FILES_WANTED);
+ remove_array_if_empty(args, FIELD_FILES_UNWANTED);
+ remove_array_if_empty(args, FIELD_FILES_PRIORITY_HIGH);
+ remove_array_if_empty(args, FIELD_FILES_PRIORITY_NORMAL);
+ remove_array_if_empty(args, FIELD_FILES_PRIORITY_LOW);
+
+ trg_files_model_set_update_barrier(TRG_FILES_MODEL(model),
+ priv->client->updateSerial + 1);
+ dispatch_async(priv->client, req, on_generic_interactive_action,
+ priv->win);
+}
+
+static void set_low(GtkWidget * w, gpointer data)
+{
+ TrgFilesTreeView *tv = TRG_FILES_TREE_VIEW(data);
+ GtkTreeSelection *selection =
+ gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
+ gtk_tree_selection_selected_foreach(selection, set_low_foreachfunc,
+ NULL);
+ send_updated_file_prefs(tv);
+}
+
+static void set_normal(GtkWidget * w, gpointer data)
+{
+ TrgFilesTreeView *tv = TRG_FILES_TREE_VIEW(data);
+ GtkTreeSelection *selection =
+ gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
+ gtk_tree_selection_selected_foreach(selection,
+ set_normal_foreachfunc, NULL);
+ send_updated_file_prefs(tv);
+}
+
+static void set_high(GtkWidget * w, gpointer data)
+{
+ TrgFilesTreeView *tv = TRG_FILES_TREE_VIEW(data);
+ GtkTreeSelection *selection =
+ gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
+ gtk_tree_selection_selected_foreach(selection,
+ set_high_foreachfunc, NULL);
+ send_updated_file_prefs(tv);
+}
+
+static void set_unwanted(GtkWidget * w, gpointer data)
+{
+ TrgFilesTreeView *tv = TRG_FILES_TREE_VIEW(data);
+ GtkTreeSelection *selection =
+ gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
+ gtk_tree_selection_selected_foreach(selection,
+ set_unwanted_foreachfunc, NULL);
+ send_updated_file_prefs(tv);
+}
+
+static void set_wanted(GtkWidget * w, gpointer data)
+{
+ TrgFilesTreeView *tv = TRG_FILES_TREE_VIEW(data);
+ GtkTreeSelection *selection =
+ gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
+ gtk_tree_selection_selected_foreach(selection,
+ set_wanted_foreachfunc, NULL);
+ send_updated_file_prefs(tv);
+}
+
+static void
+view_popup_menu(GtkWidget * treeview, GdkEventButton * event,
+ gpointer data)
+{
+ GtkWidget *menu, *menuitem;
+
+ menu = gtk_menu_new();
+
+ menuitem = gtk_menu_item_new_with_label("High Priority");
+ g_signal_connect(menuitem, "activate", G_CALLBACK(set_high), treeview);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+
+ menuitem = gtk_menu_item_new_with_label("Normal Priority");
+ g_signal_connect(menuitem, "activate", G_CALLBACK(set_normal),
+ treeview);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+
+ menuitem = gtk_menu_item_new_with_label("Low Priority");
+ g_signal_connect(menuitem, "activate", G_CALLBACK(set_low), treeview);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu),
+ gtk_separator_menu_item_new());
+
+ menuitem = gtk_image_menu_item_new_with_label(GTK_STOCK_FILE);
+ gtk_image_menu_item_set_use_stock(GTK_IMAGE_MENU_ITEM(menuitem), TRUE);
+ gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM
+ (menuitem), TRUE);
+ gtk_menu_item_set_label(GTK_MENU_ITEM(menuitem), "Download");
+ g_signal_connect(menuitem, "activate", G_CALLBACK(set_wanted),
+ treeview);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+
+ menuitem = gtk_image_menu_item_new_with_label(GTK_STOCK_STOP);
+ gtk_image_menu_item_set_use_stock(GTK_IMAGE_MENU_ITEM(menuitem), TRUE);
+ gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM
+ (menuitem), TRUE);
+ gtk_menu_item_set_label(GTK_MENU_ITEM(menuitem), "Skip");
+ g_signal_connect(menuitem, "activate", G_CALLBACK(set_unwanted),
+ treeview);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+
+ gtk_widget_show_all(menu);
+
+ gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
+ (event != NULL) ? event->button : 0,
+ gdk_event_get_time((GdkEvent *) event));
+}
+
+static gboolean
+view_onButtonPressed(GtkWidget * treeview, GdkEventButton * event,
+ gpointer userdata)
+{
+ GtkTreeSelection *selection;
+ GtkTreePath *path;
+
+ if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
+
+ if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview),
+ (gint) event->x,
+ (gint) event->y, &path,
+ NULL, NULL, NULL)) {
+ if (!gtk_tree_selection_path_is_selected(selection, path)) {
+ gtk_tree_selection_unselect_all(selection);
+ gtk_tree_selection_select_path(selection, path);
+ }
+ gtk_tree_path_free(path);
+
+ view_popup_menu(treeview, event, userdata);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean view_onPopupMenu(GtkWidget * treeview, gpointer userdata)
+{
+ view_popup_menu(treeview, NULL, userdata);
+ return TRUE;
+}
+
+static void trg_files_tree_view_add_priority_column(TrgTreeView * tv,
+ char *title, int index,
+ int width)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ renderer = trg_cell_renderer_priority_new();
+ column = gtk_tree_view_column_new_with_attributes(title, renderer,
+ "priority-value",
+ index, NULL);
+
+ trg_tree_view_std_column_setup(column, index, width);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tv), column);
+}
+
+static void trg_files_tree_view_add_wanted_column(TrgTreeView * tv,
+ char *title, int index,
+ int width)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ renderer = trg_cell_renderer_wanted_new();
+ column = gtk_tree_view_column_new_with_attributes(title, renderer,
+ "wanted-value",
+ index, NULL);
+
+ trg_tree_view_std_column_setup(column, index, width);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tv), column);
+}
+
+static void trg_files_tree_view_init(TrgFilesTreeView * self)
+{
+ trg_tree_view_add_pixbuf_text_column(TRG_TREE_VIEW(self),
+ FILESCOL_ICON, FILESCOL_NAME,
+ "Name", -1);
+ trg_tree_view_add_column(TRG_TREE_VIEW(self), "Size", FILESCOL_SIZE);
+ trg_tree_view_add_prog_column(TRG_TREE_VIEW(self), "Progress",
+ FILESCOL_PROGRESS, -1);
+ trg_files_tree_view_add_wanted_column(TRG_TREE_VIEW(self),
+ "Wanted", FILESCOL_WANTED, -1);
+ trg_files_tree_view_add_priority_column(TRG_TREE_VIEW(self),
+ "Priority",
+ FILESCOL_PRIORITY, -1);
+
+ g_signal_connect(self, "button-press-event",
+ G_CALLBACK(view_onButtonPressed), NULL);
+ g_signal_connect(self, "popup-menu", G_CALLBACK(view_onPopupMenu),
+ NULL);
+}
+
+TrgFilesTreeView *trg_files_tree_view_new(TrgFilesModel * model,
+ TrgMainWindow * win,
+ trg_client * client)
+{
+ GObject *obj = g_object_new(TRG_TYPE_FILES_TREE_VIEW, NULL);
+ TrgFilesTreeViewPrivate *priv = TRG_FILES_TREE_VIEW_GET_PRIVATE(obj);
+
+ gtk_tree_view_set_model(GTK_TREE_VIEW(obj), GTK_TREE_MODEL(model));
+ priv->client = client;
+ priv->win = win;
+
+ return TRG_FILES_TREE_VIEW(obj);
+}
diff --git a/src/trg-files-tree-view.h b/src/trg-files-tree-view.h
new file mode 100644
index 0000000..410ed4c
--- /dev/null
+++ b/src/trg-files-tree-view.h
@@ -0,0 +1,56 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TRG_FILES_TREE_VIEW_H_
+#define TRG_FILES_TREE_VIEW_H_
+
+#include <glib-object.h>
+
+#include "trg-main-window.h"
+#include "trg-client.h"
+#include "trg-files-model.h"
+
+G_BEGIN_DECLS
+#define TRG_TYPE_FILES_TREE_VIEW trg_files_tree_view_get_type()
+#define TRG_FILES_TREE_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_FILES_TREE_VIEW, TrgFilesTreeView))
+#define TRG_FILES_TREE_VIEW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_FILES_TREE_VIEW, TrgFilesTreeViewClass))
+#define TRG_IS_FILES_TREE_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_FILES_TREE_VIEW))
+#define TRG_IS_FILES_TREE_VIEW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_FILES_TREE_VIEW))
+#define TRG_FILES_TREE_VIEW_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_FILES_TREE_VIEW, TrgFilesTreeViewClass))
+ typedef struct {
+ GtkTreeView parent;
+} TrgFilesTreeView;
+
+typedef struct {
+ GtkTreeViewClass parent_class;
+} TrgFilesTreeViewClass;
+
+GType trg_files_tree_view_get_type(void);
+
+TrgFilesTreeView *trg_files_tree_view_new(TrgFilesModel * model,
+ TrgMainWindow * win,
+ trg_client * client);
+
+G_END_DECLS
+#endif /* TRG_FILES_TREE_VIEW_H_ */
diff --git a/src/trg-general-panel.c b/src/trg-general-panel.c
new file mode 100644
index 0000000..b4be30b
--- /dev/null
+++ b/src/trg-general-panel.c
@@ -0,0 +1,209 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "torrent.h"
+#include "util.h"
+#include "trg-general-panel.h"
+#include "trg-torrent-model.h"
+
+static void gtk_label_clear(GtkLabel * l);
+static GtkLabel *trg_general_panel_add_label(TrgGeneralPanel * fixed,
+ char *key, int col, int row);
+
+G_DEFINE_TYPE(TrgGeneralPanel, trg_general_panel, GTK_TYPE_FIXED)
+#define TRG_GENERAL_PANEL_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_GENERAL_PANEL, TrgGeneralPanelPrivate))
+typedef struct _TrgGeneralPanelPrivate TrgGeneralPanelPrivate;
+
+struct _TrgGeneralPanelPrivate {
+ GtkLabel *gen_name_label;
+ GtkLabel *gen_size_label;
+ GtkLabel *gen_completed_label;
+ GtkLabel *gen_seeders_label;
+ GtkLabel *gen_leechers_label;
+ GtkLabel *gen_status_label;
+ GtkLabel *gen_eta_label;
+ GtkLabel *gen_downloaded_label;
+ GtkLabel *gen_uploaded_label;
+ GtkLabel *gen_down_rate_label;
+ GtkLabel *gen_up_rate_label;
+ GtkLabel *gen_ratio_label;
+ GtkTreeModel *model;
+};
+
+void trg_general_panel_clear(TrgGeneralPanel * panel)
+{
+ TrgGeneralPanelPrivate *priv = TRG_GENERAL_PANEL_GET_PRIVATE(panel);
+
+ gtk_label_clear(priv->gen_name_label);
+ gtk_label_clear(priv->gen_size_label);
+ gtk_label_clear(priv->gen_completed_label);
+ gtk_label_clear(priv->gen_seeders_label);
+ gtk_label_clear(priv->gen_leechers_label);
+ gtk_label_clear(priv->gen_status_label);
+ gtk_label_clear(priv->gen_eta_label);
+ gtk_label_clear(priv->gen_downloaded_label);
+ gtk_label_clear(priv->gen_uploaded_label);
+ gtk_label_clear(priv->gen_down_rate_label);
+ gtk_label_clear(priv->gen_up_rate_label);
+ gtk_label_clear(priv->gen_ratio_label);
+}
+
+static void gtk_label_clear(GtkLabel * l)
+{
+ gtk_label_set_text(l, "");
+}
+
+static void trg_general_panel_class_init(TrgGeneralPanelClass * klass)
+{
+ g_type_class_add_private(klass, sizeof(TrgGeneralPanelPrivate));
+}
+
+void trg_general_panel_update(TrgGeneralPanel * panel, JsonObject * t,
+ GtkTreeIter * iter)
+{
+ TrgGeneralPanelPrivate *priv;
+ gchar buf[32];
+ gint sizeOfBuf;
+ gchar *statusString;
+ gint64 eta;
+ gint seeders, leechers;
+
+ priv = TRG_GENERAL_PANEL_GET_PRIVATE(panel);
+
+ sizeOfBuf = sizeof(buf);
+
+ tr_strlsize(buf, torrent_get_size(t), sizeOfBuf);
+ gtk_label_set_text(GTK_LABEL(priv->gen_size_label), buf);
+
+ tr_strlspeed(buf, torrent_get_rate_down(t) / KILOBYTE_FACTOR,
+ sizeOfBuf);
+ gtk_label_set_text(GTK_LABEL(priv->gen_down_rate_label), buf);
+
+ tr_strlspeed(buf, torrent_get_rate_up(t) / KILOBYTE_FACTOR, sizeOfBuf);
+ gtk_label_set_text(GTK_LABEL(priv->gen_up_rate_label), buf);
+
+ tr_strlsize(buf, torrent_get_uploaded(t), sizeOfBuf);
+ gtk_label_set_text(GTK_LABEL(priv->gen_uploaded_label), buf);
+
+ tr_strlsize(buf, torrent_get_downloaded(t), sizeOfBuf);
+ gtk_label_set_text(GTK_LABEL(priv->gen_downloaded_label), buf);
+
+ tr_strlratio(buf,
+ (double) torrent_get_uploaded(t) /
+ (double) torrent_get_downloaded(t), sizeOfBuf);
+ gtk_label_set_text(GTK_LABEL(priv->gen_ratio_label), buf);
+
+ statusString = torrent_get_status_string(torrent_get_status(t));
+ gtk_label_set_text(GTK_LABEL(priv->gen_status_label), statusString);
+ g_free(statusString);
+
+ tr_strlpercent(buf, torrent_get_percent_done(t), sizeOfBuf);
+ gtk_label_set_text(GTK_LABEL(priv->gen_completed_label), buf);
+
+ gtk_label_set_text(GTK_LABEL(priv->gen_name_label),
+ torrent_get_name(t));
+
+ if ((eta = torrent_get_eta(t)) > 0) {
+ tr_strltime_long(buf, eta, sizeOfBuf);
+ gtk_label_set_text(GTK_LABEL(priv->gen_eta_label), buf);
+ } else {
+ gtk_label_set_text(GTK_LABEL(priv->gen_eta_label), "N/A");
+ }
+
+ seeders = 0;
+ leechers = 0;
+ gtk_tree_model_get(GTK_TREE_MODEL(priv->model), iter,
+ TORRENT_COLUMN_SEEDS, &seeders,
+ TORRENT_COLUMN_LEECHERS, &leechers, -1);
+
+ snprintf(buf, sizeof(buf), "%d", seeders);
+ gtk_label_set_text(GTK_LABEL(priv->gen_seeders_label), buf);
+ snprintf(buf, sizeof(buf), "%d", leechers);
+ gtk_label_set_text(GTK_LABEL(priv->gen_leechers_label), buf);
+}
+
+static GtkLabel *trg_general_panel_add_label(TrgGeneralPanel * fixed,
+ char *key, int col, int row)
+{
+ GtkWidget *keyLabel;
+ GtkWidget *value;
+ gchar *keyMarkup;
+
+ keyLabel = gtk_label_new(NULL);
+ keyMarkup = g_markup_printf_escaped("<b>%s</b>", key);
+ gtk_label_set_markup(GTK_LABEL(keyLabel), keyMarkup);
+ g_free(keyMarkup);
+ gtk_fixed_put(GTK_FIXED(fixed), keyLabel, 10 + (col * 300),
+ 10 + (row * 24));
+
+ value = gtk_label_new(NULL);
+ gtk_label_set_selectable(GTK_LABEL(value), TRUE);
+ gtk_fixed_put(GTK_FIXED(fixed), value, 140 + (col * 320),
+ 10 + (row * 24));
+
+ return GTK_LABEL(value);
+}
+
+static void trg_general_panel_init(TrgGeneralPanel * self)
+{
+ TrgGeneralPanelPrivate *priv = TRG_GENERAL_PANEL_GET_PRIVATE(self);
+
+ priv->gen_name_label =
+ trg_general_panel_add_label(self, "Name:", 0, 0);
+ priv->gen_size_label =
+ trg_general_panel_add_label(self, "Size:", 0, 1);
+ priv->gen_completed_label =
+ trg_general_panel_add_label(self, "Completed:", 0, 2);
+ priv->gen_seeders_label =
+ trg_general_panel_add_label(self, "Seeders:", 0, 3);
+ priv->gen_leechers_label =
+ trg_general_panel_add_label(self, "Leechers:", 0, 4);
+ priv->gen_status_label =
+ trg_general_panel_add_label(self, "Status:", 0, 5);
+ priv->gen_eta_label = trg_general_panel_add_label(self, "ETA:", 1, 1);
+ priv->gen_downloaded_label =
+ trg_general_panel_add_label(self, "Downloaded:", 1, 2);
+ priv->gen_uploaded_label =
+ trg_general_panel_add_label(self, "Uploaded:", 1, 3);
+ priv->gen_down_rate_label =
+ trg_general_panel_add_label(self, "Rate Down:", 1, 4);
+ priv->gen_up_rate_label =
+ trg_general_panel_add_label(self, "Rate Up:", 1, 5);
+ priv->gen_ratio_label =
+ trg_general_panel_add_label(self, "Ratio:", 2, 3);
+
+ gtk_widget_set_sensitive(GTK_WIDGET(self), FALSE);
+}
+
+TrgGeneralPanel *trg_general_panel_new(GtkTreeModel * model)
+{
+ GObject *obj;
+ TrgGeneralPanelPrivate *priv;
+
+ obj = g_object_new(TRG_TYPE_GENERAL_PANEL, NULL);
+
+ priv = TRG_GENERAL_PANEL_GET_PRIVATE(obj);
+ priv->model = model;
+
+ return TRG_GENERAL_PANEL(obj);
+}
diff --git a/src/trg-general-panel.h b/src/trg-general-panel.h
new file mode 100644
index 0000000..2765de9
--- /dev/null
+++ b/src/trg-general-panel.h
@@ -0,0 +1,59 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TRG_GENERAL_PANEL_H_
+#define TRG_GENERAL_PANEL_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include <glib-object.h>
+
+#include "trg-torrent-model.h"
+
+G_BEGIN_DECLS
+#define TRG_TYPE_GENERAL_PANEL trg_general_panel_get_type()
+#define TRG_GENERAL_PANEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_GENERAL_PANEL, TrgGeneralPanel))
+#define TRG_GENERAL_PANEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_GENERAL_PANEL, TrgGeneralPanelClass))
+#define TRG_IS_GENERAL_PANEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_GENERAL_PANEL))
+#define TRG_IS_GENERAL_PANEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_GENERAL_PANEL))
+#define TRG_GENERAL_PANEL_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_GENERAL_PANEL, TrgGeneralPanelClass))
+ typedef struct {
+ GtkFixed parent;
+} TrgGeneralPanel;
+
+typedef struct {
+ GtkFixedClass parent_class;
+} TrgGeneralPanelClass;
+
+GType trg_general_panel_get_type(void);
+
+TrgGeneralPanel *trg_general_panel_new(GtkTreeModel * model);
+
+G_END_DECLS
+ void trg_general_panel_update(TrgGeneralPanel * panel, JsonObject * t,
+ GtkTreeIter * iter);
+void trg_general_panel_clear(TrgGeneralPanel * panel);
+
+#endif /* TRG_GENERAL_PANEL_H_ */
diff --git a/src/trg-json-widgets.c b/src/trg-json-widgets.c
new file mode 100644
index 0000000..3b34d1e
--- /dev/null
+++ b/src/trg-json-widgets.c
@@ -0,0 +1,71 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+
+#include "trg-json-widgets.h"
+
+void toggle_active_arg_is_sensitive(GtkToggleButton * b, gpointer data)
+{
+ gtk_widget_set_sensitive(GTK_WIDGET(data),
+ gtk_toggle_button_get_active(b));
+}
+
+void gtk_spin_button_json_int_out(GtkSpinButton * spin, JsonObject * out)
+{
+ gchar *key = g_object_get_data(G_OBJECT(spin), JSON_OBJECT_KEY);
+ json_object_set_int_member(out, key, gtk_spin_button_get_value(spin));
+}
+
+void gtk_combo_box_json_string_output(GtkComboBox * c, JsonObject * out)
+{
+ gchar *key = g_object_get_data(G_OBJECT(c), JSON_OBJECT_KEY);
+ gchar *value = g_object_get_data(G_OBJECT(c), JSON_OBJECT_VALUE);
+ json_object_set_string_member(out, key, value);
+}
+
+void gtk_spin_button_json_double_out(GtkSpinButton * spin,
+ JsonObject * out)
+{
+ gchar *key = g_object_get_data(G_OBJECT(spin), JSON_OBJECT_KEY);
+ json_object_set_double_member(out, key,
+ gtk_spin_button_get_value(spin));
+}
+
+void gtk_entry_json_output(GtkEntry * e, JsonObject * out)
+{
+ gchar *key = g_object_get_data(G_OBJECT(e), JSON_OBJECT_KEY);
+ json_object_set_string_member(out, key, gtk_entry_get_text(e));
+}
+
+void widget_set_json_key(GtkWidget * w, gchar * key)
+{
+ g_object_set_data_full(G_OBJECT(w), JSON_OBJECT_KEY,
+ g_strdup(key), g_free);
+}
+
+gboolean gtk_toggle_button_json_out(GtkToggleButton * button,
+ JsonObject * out)
+{
+ gboolean active = gtk_toggle_button_get_active(button);
+ gchar *key = g_object_get_data(G_OBJECT(button), JSON_OBJECT_KEY);
+ json_object_set_boolean_member(out, key, active);
+ return active;
+}
diff --git a/src/trg-json-widgets.h b/src/trg-json-widgets.h
new file mode 100644
index 0000000..40b9b39
--- /dev/null
+++ b/src/trg-json-widgets.h
@@ -0,0 +1,40 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TRG_JSON_WIDGETS_H_
+#define TRG_JSON_WIDGETS_H_
+
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+
+#define JSON_OBJECT_KEY "json-object-key"
+#define JSON_OBJECT_VALUE "json-object-value"
+
+void widget_set_json_key(GtkWidget * w, gchar * key);
+
+void gtk_spin_button_json_int_out(GtkSpinButton * spin, JsonObject * out);
+void gtk_spin_button_json_double_out(GtkSpinButton * spin,
+ JsonObject * out);
+gboolean gtk_toggle_button_json_out(GtkToggleButton * button,
+ JsonObject * out);
+void gtk_entry_json_output(GtkEntry * e, JsonObject * out);
+void gtk_combo_box_json_string_output(GtkComboBox * c, JsonObject * out);
+void toggle_active_arg_is_sensitive(GtkToggleButton * b, gpointer data);
+
+#endif /* TRG_JSON_WIDGETS_H_ */
diff --git a/src/trg-main-window.c b/src/trg-main-window.c
new file mode 100644
index 0000000..caacd72
--- /dev/null
+++ b/src/trg-main-window.c
@@ -0,0 +1,1449 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+#include <gdk/gdkkeysyms.h>
+#include <curl/curl.h>
+#include <libnotify/notify.h>
+
+#include "dispatch.h"
+#include "trg-client.h"
+#include "http.h"
+#include "json.h"
+
+#include "trg-main-window.h"
+#include "trg-about-window.h"
+#include "trg-tree-view.h"
+#include "trg-preferences.h"
+#include "trg-torrent-model.h"
+#include "trg-torrent-tree-view.h"
+#include "trg-peers-model.h"
+#include "trg-peers-tree-view.h"
+#include "trg-files-tree-view.h"
+#include "trg-files-model.h"
+#include "trg-trackers-tree-view.h"
+#include "trg-trackers-model.h"
+#include "trg-state-selector.h"
+#include "trg-torrent-props-dialog.h"
+#include "trg-torrent-add-url-dialog.h"
+#include "trg-toolbar.h"
+#include "trg-menu-bar.h"
+#include "trg-status-bar.h"
+#include "trg-remote-prefs-dialog.h"
+
+#include "util.h"
+#include "requests.h"
+#include "trg-preferences-dialog.h"
+#include "session-get.h"
+
+/* Events */
+
+static void on_torrent_get(JsonObject * response, int status,
+ gpointer data);
+
+static void on_torrent_get_first(JsonObject * response, int status,
+ gpointer data);
+
+static void on_torrent_get_multipurpose(JsonObject * response,
+ gboolean first, int status,
+ gpointer data);
+
+static void trg_main_window_conn_changed(TrgMainWindow * win,
+ gboolean connected);
+static void
+trg_main_window_update_notebook_displays(TrgMainWindow * win,
+ JsonObject * t,
+ GtkTreeIter * iter,
+ gboolean first);
+
+static gboolean torrent_selection_changed(GtkWidget * w, gpointer data);
+
+static gboolean trg_update_torrents_timerfunc(gpointer data);
+
+static void destroy_window();
+
+static void on_torrent_completed(TrgTorrentModel * model,
+ GtkTreeIter * iter, gpointer * data);
+
+static void on_session_get(JsonObject * response, int status,
+ gpointer data);
+
+static void entry_filter_changed_cb(GtkWidget * w, gpointer data);
+
+/* Actions */
+
+static void connect_cb(GtkWidget * w, gpointer data);
+static void disconnect_cb(GtkWidget * w, gpointer data);
+static void add_cb(GtkWidget * w, gpointer data);
+static void resume_cb(GtkWidget * w, gpointer data);
+static void pause_cb(GtkWidget * w, gpointer data);
+static void verify_cb(GtkWidget * w, gpointer data);
+static void remove_cb(GtkWidget * w, gpointer data);
+static void delete_cb(GtkWidget * w, gpointer data);
+static void open_about_cb(GtkWidget * w, GtkWindow * parent);
+
+/* Utility */
+
+static
+void trg_main_window_torrent_scrub(TrgMainWindow * win);
+static const gchar *make_error_message(JsonObject * response, int status);
+static GtkWidget *my_scrolledwin_new(GtkWidget * child);
+static gboolean
+trg_dialog_error_handler(TrgMainWindow * win, JsonObject * response,
+ int status);
+static void response_unref(JsonObject * response);
+
+G_DEFINE_TYPE(TrgMainWindow, trg_main_window, GTK_TYPE_WINDOW)
+#define TRG_MAIN_WINDOW_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_MAIN_WINDOW, TrgMainWindowPrivate))
+typedef struct _TrgMainWindowPrivate TrgMainWindowPrivate;
+
+struct _TrgMainWindowPrivate {
+ trg_client *client;
+ TrgToolbar *toolBar;
+ TrgMenuBar *menuBar;
+
+ TrgStatusBar *statusBar;
+ GtkStatusIcon *statusIcon;
+ TrgStateSelector *stateSelector;
+ TrgGeneralPanel *genDetails;
+ GtkWidget *notebook;
+
+ TrgTorrentModel *torrentModel;
+ TrgTorrentTreeView *torrentTreeView;
+ GtkTreeModel *filteredTorrentModel;
+ GtkTreeModel *sortedTorrentModel;
+ gint selectedTorrentId;
+
+ TrgTrackersModel *trackersModel;
+ TrgTrackersTreeView *trackersTreeView;
+
+ TrgFilesModel *filesModel;
+ TrgFilesTreeView *filesTreeView;
+
+ TrgPeersModel *peersModel;
+ TrgPeersTreeView *peersTreeView;
+
+ GtkWidget *hpaned, *vpaned;
+ GtkWidget *filterEntry, *filterEntryClearButton;
+};
+
+enum {
+ PROP_0,
+ PROP_CLIENT
+};
+
+static void trg_main_window_init(TrgMainWindow * self)
+{
+}
+
+static gboolean update_selected_torrent_notebook(TrgMainWindow * win,
+ gboolean first)
+{
+ TrgMainWindowPrivate *priv;
+ GtkTreeIter iter;
+ gint newFirstSelected;
+ JsonObject *json = NULL;
+
+ priv = TRG_MAIN_WINDOW_GET_PRIVATE(win);
+ newFirstSelected =
+ get_first_selected(priv->torrentTreeView, &iter, &json);
+
+ if (priv->selectedTorrentId >= 0
+ && (priv->selectedTorrentId != newFirstSelected
+ || newFirstSelected < 0)) {
+ trg_main_window_torrent_scrub(win);
+ }
+
+ if ((priv->selectedTorrentId = newFirstSelected) >= 0) {
+ trg_main_window_update_notebook_displays(win, json, &iter, first);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void response_unref(JsonObject * response)
+{
+ if (response != NULL)
+ json_object_unref(response);
+}
+
+static void on_torrent_completed(TrgTorrentModel * model,
+ GtkTreeIter * iter, gpointer * data)
+{
+ TrgMainWindowPrivate *priv;
+ gchar *name;
+ NotifyNotification *notify;
+
+ priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ if (!gtk_status_icon_is_embedded(priv->statusIcon))
+ return;
+
+ gtk_tree_model_get(GTK_TREE_MODEL(model), iter,
+ TORRENT_COLUMN_NAME, &name, -1);
+
+ notify =
+ notify_notification_new(name,
+ "This torrent has finished downloading",
+ GTK_STOCK_APPLY, NULL);
+
+ notify_notification_attach_to_status_icon(notify, priv->statusIcon);
+ notify_notification_set_urgency(notify, NOTIFY_URGENCY_LOW);
+ notify_notification_set_timeout(notify, 8000);
+
+ g_free(name);
+
+ notify_notification_show(notify, NULL);
+}
+
+static gboolean delete_event(GtkWidget * w, GdkEvent * event,
+ gpointer * data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(w);
+ int width, height;
+ gtk_window_get_size(GTK_WINDOW(w), &width, &height);
+ gconf_client_set_int(priv->client->gconf, TRG_GCONF_KEY_WINDOW_HEIGHT,
+ height, NULL);
+ gconf_client_set_int(priv->client->gconf, TRG_GCONF_KEY_WINDOW_WIDTH,
+ width, NULL);
+
+ return FALSE;
+}
+
+static void destroy_window(GtkWidget * w, gpointer data)
+{
+ gtk_main_quit();
+}
+
+static const gchar *make_error_message(JsonObject * response, int status)
+{
+ if (status == FAIL_JSON_DECODE) {
+ return g_strdup("JSON decoding error.");
+ } else if (status == FAIL_RESPONSE_UNSUCCESSFUL) {
+ const gchar *resultStr =
+ json_object_get_string_member(response, "result");
+ if (resultStr == NULL)
+ return g_strdup("Server responded, but with no result.");
+ else
+ return g_strdup(resultStr);
+ } else if (status <= -100) {
+ return g_strdup_printf("Request failed with HTTP code %d",
+ -(status + 100));
+ } else {
+ return g_strdup(curl_easy_strerror(status));
+ }
+}
+
+static void open_props_cb(GtkWidget * w, gpointer data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+ TrgTorrentPropsDialog *dialog =
+ trg_torrent_props_dialog_new(GTK_WINDOW(data),
+ priv->torrentTreeView,
+ priv->client);
+
+ gtk_widget_show_all(GTK_WIDGET(dialog));
+}
+
+/* Use synchronous dispatch() in our dedicated thread function.
+ * This means torrents are added in sequence, instead of dispatch_async()
+ * working concurrently for each upload.
+ */
+
+static gpointer add_files_threadfunc(gpointer data)
+{
+ JsonObject *response;
+ JsonNode *request;
+ struct add_torrent_threadfunc_args *args;
+ gint status;
+ GSList *li;
+
+ args = (struct add_torrent_threadfunc_args *) data;
+
+ for (li = args->list; li != NULL; li = g_slist_next(li)) {
+ request = torrent_add((gchar *) li->data, FALSE);
+ g_free(li->data);
+ response = dispatch(args->client, request, &status);
+ on_generic_interactive_action(response, status, args->cb_data);
+ }
+
+ g_slist_free(args->list);
+ g_free(args);
+
+ return NULL;
+}
+
+static void add_url_cb(GtkWidget * w, gpointer data)
+{
+ TrgMainWindow *win = TRG_MAIN_WINDOW(data);
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ TrgTorrentAddUrlDialog *dlg =
+ trg_torrent_add_url_dialog_new(win, priv->client);
+ gtk_widget_show_all(GTK_WIDGET(dlg));
+}
+
+static void add_cb(GtkWidget * w, gpointer data)
+{
+ TrgMainWindowPrivate *priv;
+ GtkWidget *dialog;
+ GtkFileFilter *filter;
+
+ priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ dialog = gtk_file_chooser_dialog_new("Open File",
+ GTK_WINDOW(data),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN,
+ GTK_RESPONSE_ACCEPT, NULL);
+ gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
+
+ filter = gtk_file_filter_new();
+ gtk_file_filter_add_pattern(filter, "*.torrent");
+ gtk_file_filter_set_name(filter, "BitTorrent Metadata");
+ gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
+
+ if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
+ GThread *thread;
+ GError *error = NULL;
+ struct add_torrent_threadfunc_args *args;
+
+ args = g_new(struct add_torrent_threadfunc_args, 1);
+ args->list =
+ gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
+ args->cb_data = data;
+ args->client = priv->client;
+
+ thread =
+ g_thread_create(add_files_threadfunc, args, FALSE, &error);
+ if (error != NULL) {
+ g_printf("thread creation error: %s\n", error->message);
+ g_error_free(error);
+ g_free(args);
+ }
+ }
+
+ gtk_widget_destroy(dialog);
+}
+
+static void pause_cb(GtkWidget * w, gpointer data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ dispatch_async(priv->client,
+ torrent_pause(build_json_id_array
+ (priv->torrentTreeView)),
+ on_generic_interactive_action, data);
+}
+
+gboolean trg_add_from_filename(TrgMainWindow * win, gchar * fileName)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(win);
+
+ if (g_file_test(fileName, G_FILE_TEST_EXISTS) == TRUE) {
+ JsonNode *torrentAddReq = torrent_add(fileName, FALSE);
+ dispatch_async(priv->client, torrentAddReq,
+ on_generic_interactive_action, win);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void resume_cb(GtkWidget * w, gpointer data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ dispatch_async(priv->client,
+ torrent_start(build_json_id_array
+ (priv->torrentTreeView)),
+ on_generic_interactive_action, data);
+}
+
+static void disconnect_cb(GtkWidget * w, gpointer data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ trg_main_window_conn_changed(TRG_MAIN_WINDOW(data), FALSE);
+ trg_status_bar_push_connection_msg(priv->statusBar, "Disconnected.");
+}
+
+static void connect_cb(GtkWidget * w, gpointer data)
+{
+ TrgMainWindowPrivate *priv;
+
+ priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ if (trg_client_populate_with_settings
+ (priv->client, priv->client->gconf) == FALSE) {
+ GtkWidget *dialog;
+
+ trg_status_bar_push_connection_msg(priv->statusBar,
+ "Unable to get gconf settings.");
+
+ dialog =
+ gtk_message_dialog_new(GTK_WINDOW(data),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ "Unable to retrieve connection settings from GConf. Schema not installed?");
+ gtk_dialog_run(GTK_DIALOG(dialog));
+
+ return;
+ }
+
+ trg_status_bar_push_connection_msg(priv->statusBar, "Connecting...");
+ dispatch_async(priv->client, session_get(), on_session_get, data);
+}
+
+static void open_local_prefs_cb(GtkWidget * w, gpointer data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ GtkWidget *dlg = trg_preferences_dialog_get_instance(GTK_WINDOW(data),
+ priv->client->
+ gconf);
+ gtk_widget_show_all(dlg);
+}
+
+static void open_remote_prefs_cb(GtkWidget * w, gpointer data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ TrgRemotePrefsDialog *dlg =
+ trg_remote_prefs_dialog_get_instance(TRG_MAIN_WINDOW(data),
+ priv->client);
+
+ gtk_widget_show_all(GTK_WIDGET(dlg));
+}
+
+static TrgToolbar *trg_main_window_toolbar_new(TrgMainWindow * win)
+{
+ GObject *b_connect, *b_disconnect, *b_add, *b_resume, *b_pause;
+ GObject *b_remove, *b_delete, *b_props, *b_local_prefs,
+ *b_remote_prefs;
+
+ TrgToolbar *toolBar = trg_toolbar_new();
+ g_object_get(toolBar,
+ "connect-button", &b_connect,
+ "disconnect-button", &b_disconnect,
+ "add-button", &b_add,
+ "resume-button", &b_resume,
+ "pause-button", &b_pause,
+ "delete-button", &b_delete,
+ "remove-button", &b_remove,
+ "props-button", &b_props,
+ "remote-prefs-button", &b_remote_prefs,
+ "local-prefs-button", &b_local_prefs, NULL);
+
+ g_signal_connect(b_connect, "clicked", G_CALLBACK(connect_cb), win);
+ g_signal_connect(b_disconnect, "clicked",
+ G_CALLBACK(disconnect_cb), win);
+ g_signal_connect(b_add, "clicked", G_CALLBACK(add_cb), win);
+ g_signal_connect(b_resume, "clicked", G_CALLBACK(resume_cb), win);
+ g_signal_connect(b_pause, "clicked", G_CALLBACK(pause_cb), win);
+ g_signal_connect(b_delete, "clicked", G_CALLBACK(delete_cb), win);
+ g_signal_connect(b_remove, "clicked", G_CALLBACK(remove_cb), win);
+ g_signal_connect(b_props, "clicked", G_CALLBACK(open_props_cb), win);
+ g_signal_connect(b_local_prefs, "clicked",
+ G_CALLBACK(open_local_prefs_cb), win);
+ g_signal_connect(b_remote_prefs, "clicked",
+ G_CALLBACK(open_remote_prefs_cb), win);
+
+ return toolBar;
+}
+
+static void verify_cb(GtkWidget * w, gpointer data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ g_printf("verify_cb()\n");
+ dispatch_async(priv->client,
+ torrent_verify(build_json_id_array
+ (priv->torrentTreeView)),
+ on_generic_interactive_action, data);
+}
+
+static gint confirm_action_dialog(GtkWindow * win,
+ GtkTreeSelection * selection,
+ gchar * question_single,
+ gchar * question_multi,
+ gchar * action_stock)
+{
+ TrgMainWindowPrivate *priv;
+ gint selectCount;
+ gint response;
+ GtkWidget *dialog = NULL;
+
+ priv = TRG_MAIN_WINDOW_GET_PRIVATE(win);
+
+ selectCount = gtk_tree_selection_count_selected_rows(selection);
+
+ if (selectCount == 1) {
+ GList *list;
+ GList *firstNode;
+ GtkTreeIter firstIter;
+ gchar *name = NULL;
+
+ list = gtk_tree_selection_get_selected_rows(selection, NULL);
+ firstNode = g_list_first(list);
+
+ gtk_tree_model_get_iter(GTK_TREE_MODEL
+ (priv->sortedTorrentModel),
+ &firstIter, firstNode->data);
+ gtk_tree_model_get(GTK_TREE_MODEL
+ (priv->sortedTorrentModel),
+ &firstIter, TORRENT_COLUMN_NAME, &name, -1);
+ g_list_free(list);
+
+ dialog =
+ gtk_message_dialog_new_with_markup(GTK_WINDOW(win),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_NONE,
+ question_single, name);
+ g_free(name);
+ } else if (selectCount > 1) {
+ dialog =
+ gtk_message_dialog_new_with_markup(GTK_WINDOW(win),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_NONE,
+ question_multi,
+ selectCount);
+
+ } else {
+ return 0;
+ }
+
+ gtk_dialog_add_buttons(GTK_DIALOG(dialog),
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ action_stock, GTK_RESPONSE_ACCEPT, NULL);
+ gtk_dialog_set_default_response(GTK_DIALOG(dialog),
+ GTK_RESPONSE_CANCEL);
+ gtk_dialog_set_alternative_button_order(GTK_DIALOG(dialog),
+ GTK_RESPONSE_ACCEPT,
+ GTK_RESPONSE_CANCEL, -1);
+
+ response = gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ return response;
+}
+
+static void remove_cb(GtkWidget * w, gpointer data)
+{
+ TrgMainWindowPrivate *priv;
+ GtkTreeSelection *selection;
+
+ priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ selection =
+ gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->torrentTreeView));
+
+ if (confirm_action_dialog
+ (GTK_WINDOW(data), selection,
+ "<big><b>Remove torrent \"%s\"?</b></big>",
+ "<big><b>Remove %d torrents?</b></big>",
+ GTK_STOCK_REMOVE) == GTK_RESPONSE_ACCEPT)
+ dispatch_async(priv->client,
+ torrent_remove(build_json_id_array
+ (priv->torrentTreeView),
+ FALSE),
+ on_generic_interactive_action, data);
+}
+
+static void delete_cb(GtkWidget * w, gpointer data)
+{
+ TrgMainWindowPrivate *priv;
+ GtkTreeSelection *selection;
+
+ priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ selection =
+ gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->torrentTreeView));
+
+ if (confirm_action_dialog
+ (GTK_WINDOW(data), selection,
+ "<big><b>Remove and delete torrent \"%s\"?</b></big>",
+ "<big><b>Remove and delete %d torrents?</b></big>",
+ GTK_STOCK_DELETE) == GTK_RESPONSE_ACCEPT)
+ dispatch_async(priv->client,
+ torrent_remove(build_json_id_array
+ (priv->torrentTreeView),
+ TRUE),
+ on_generic_interactive_action, data);
+}
+
+static
+GtkWidget *my_scrolledwin_new(GtkWidget * child)
+{
+ GtkWidget *scrolled_win = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_win),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_container_add(GTK_CONTAINER(scrolled_win), child);
+ return scrolled_win;
+}
+
+static void view_states_toggled_cb(GtkCheckMenuItem * w, gpointer data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+ gtk_widget_set_visible(GTK_WIDGET(priv->stateSelector),
+ gtk_check_menu_item_get_active(w));
+}
+
+static void view_notebook_toggled_cb(GtkCheckMenuItem * w, gpointer data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+ gtk_widget_set_visible(priv->notebook,
+ gtk_check_menu_item_get_active(w));
+}
+
+static
+GtkWidget *trg_main_window_notebook_new(TrgMainWindow * win)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(win);
+
+ GtkWidget *notebook = gtk_notebook_new();
+
+ gtk_widget_set_size_request(notebook, -1, 200);
+
+ priv->genDetails = trg_general_panel_new(priv->sortedTorrentModel);
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
+ GTK_WIDGET(priv->genDetails),
+ gtk_label_new("General"));
+
+ priv->trackersModel = trg_trackers_model_new();
+ priv->trackersTreeView =
+ trg_trackers_tree_view_new(priv->trackersModel);
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
+ my_scrolledwin_new(GTK_WIDGET
+ (priv->trackersTreeView)),
+ gtk_label_new("Trackers"));
+
+ priv->filesModel = trg_files_model_new();
+ priv->filesTreeView =
+ trg_files_tree_view_new(priv->filesModel, win, priv->client);
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
+ my_scrolledwin_new(GTK_WIDGET
+ (priv->filesTreeView)),
+ gtk_label_new("Files"));
+
+ priv->peersModel = trg_peers_model_new();
+ priv->peersTreeView = trg_peers_tree_view_new(priv->peersModel);
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
+ my_scrolledwin_new(GTK_WIDGET
+ (priv->peersTreeView)),
+ gtk_label_new("Peers"));
+
+ return notebook;
+}
+
+void on_session_set(JsonObject * response, int status, gpointer data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ gdk_threads_enter();
+ trg_dialog_error_handler(TRG_MAIN_WINDOW(data), response, status);
+ gdk_threads_leave();
+
+ response_unref(response);
+
+ if (status == CURLE_OK || status == FAIL_RESPONSE_UNSUCCESSFUL)
+ dispatch_async(priv->client, session_get(), on_session_get, data);
+}
+
+static void on_session_get(JsonObject * response, int status,
+ gpointer data)
+{
+ TrgMainWindow *win = TRG_MAIN_WINDOW(data);
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+ trg_client *client = priv->client;
+ JsonObject *newSession;
+
+ gdk_threads_enter();
+
+ if (trg_dialog_error_handler(win, response, status) == TRUE) {
+ response_unref(response);
+ gdk_threads_leave();
+ return;
+ }
+
+ newSession = get_arguments(response);
+
+ if (client->session != NULL) {
+ json_object_unref(client->session);
+ } else {
+ trg_status_bar_connect(priv->statusBar, newSession);
+ trg_main_window_conn_changed(win, TRUE);
+
+ dispatch_async(client, torrent_get(), on_torrent_get_first, data);
+ }
+
+ client->session = newSession;
+
+ gdk_threads_leave();
+ json_object_ref(newSession);
+ response_unref(response);
+}
+
+static void
+on_torrent_get_first(JsonObject * response, int status, gpointer data)
+{
+ on_torrent_get_multipurpose(response, TRUE, status, data);
+}
+
+static void on_torrent_get(JsonObject * response, int status,
+ gpointer data)
+{
+ on_torrent_get_multipurpose(response, FALSE, status, data);
+}
+
+static gboolean trg_update_torrents_timerfunc(gpointer data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ if (priv->client->session != NULL)
+ dispatch_async(priv->client, torrent_get(), on_torrent_get, data);
+
+ return FALSE;
+}
+
+static void
+trg_main_window_update_notebook_displays(TrgMainWindow * win,
+ JsonObject * t,
+ GtkTreeIter * iter,
+ gboolean first)
+{
+ TrgMainWindowPrivate *priv;
+ trg_client *client;
+
+ priv = TRG_MAIN_WINDOW_GET_PRIVATE(win);
+ client = priv->client;
+
+ trg_general_panel_update(priv->genDetails, t, iter);
+
+ if (first == TRUE)
+ trg_trackers_model_update(priv->trackersModel, t);
+
+ trg_files_model_update(priv->filesModel, client->updateSerial,
+ t, first);
+ trg_peers_model_update(priv->peersModel, client->updateSerial,
+ t, first);
+}
+
+static
+void open_about_cb(GtkWidget * w, GtkWindow * parent)
+{
+ GtkWidget *aboutDialog = trg_about_window_new(parent);
+
+ gtk_dialog_run(GTK_DIALOG(aboutDialog));
+ gtk_widget_destroy(aboutDialog);
+}
+
+static gboolean
+trg_torrent_tree_view_visible_func(GtkTreeModel * model,
+ GtkTreeIter * iter, gpointer data)
+{
+ guint flags;
+ gchar *name = NULL;
+ gboolean visible = TRUE;
+
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ guint32 criteria = trg_state_selector_get_flag(priv->stateSelector);
+
+ gtk_tree_model_get(model, iter,
+ TORRENT_COLUMN_FLAGS, &flags,
+ TORRENT_COLUMN_NAME, &name, -1);
+
+ if (criteria != 0 && !(flags & criteria)) {
+ visible = FALSE;
+ } else if (name != NULL) {
+ const gchar *filterText =
+ gtk_entry_get_text(GTK_ENTRY(priv->filterEntry));
+ if (strlen(filterText) > 0 && strstr(name, filterText) == NULL)
+ visible = FALSE;
+ }
+
+ g_free(name);
+
+ return visible;
+}
+
+static
+TrgTorrentTreeView *trg_main_window_torrent_tree_view_new(TrgMainWindow *
+ win,
+ GtkTreeModel *
+ model,
+ TrgStateSelector
+ * selector)
+{
+ TrgTorrentTreeView *torrentTreeView = trg_torrent_tree_view_new(model);
+
+ GtkTreeSelection *selection =
+ gtk_tree_view_get_selection(GTK_TREE_VIEW(torrentTreeView));
+
+ g_signal_connect(G_OBJECT(selection), "changed",
+ G_CALLBACK(torrent_selection_changed), win);
+
+ return torrentTreeView;
+}
+
+static gboolean
+trg_dialog_error_handler(TrgMainWindow * win, JsonObject * response,
+ int status)
+{
+ TrgMainWindowPrivate *priv;
+
+ priv = TRG_MAIN_WINDOW_GET_PRIVATE(win);
+
+ if (status != CURLE_OK) {
+ GtkWidget *dialog;
+ const gchar *msg;
+
+ msg = make_error_message(response, status);
+ trg_status_bar_push_connection_msg(priv->statusBar, msg);
+ dialog = gtk_message_dialog_new(GTK_WINDOW(win),
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK, msg);
+ gtk_window_set_title(GTK_WINDOW(dialog), "Error");
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ g_free((gpointer) msg);
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+static gboolean torrent_selection_changed(GtkWidget * w, gpointer data)
+{
+ TrgMainWindow *win;
+ TrgMainWindowPrivate *priv;
+ gboolean isSelected;
+
+ priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+ win = TRG_MAIN_WINDOW(data);
+
+ isSelected = update_selected_torrent_notebook(win, TRUE);
+
+ trg_toolbar_torrent_actions_sensitive(priv->toolBar, isSelected);
+ trg_menu_bar_torrent_actions_sensitive(priv->menuBar, isSelected);
+
+ return TRUE;
+}
+
+void
+on_generic_interactive_action(JsonObject * response, int status,
+ gpointer data)
+{
+ gdk_threads_enter();
+ trg_dialog_error_handler(TRG_MAIN_WINDOW(data), response, status);
+ gdk_threads_leave();
+
+ response_unref(response);
+}
+
+static
+void trg_main_window_torrent_scrub(TrgMainWindow * win)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(win);
+
+ gtk_list_store_clear(GTK_LIST_STORE(priv->filesModel));
+ gtk_list_store_clear(GTK_LIST_STORE(priv->trackersModel));
+ gtk_list_store_clear(GTK_LIST_STORE(priv->peersModel));
+ trg_general_panel_clear(priv->genDetails);
+}
+
+static void
+on_torrent_get_multipurpose(JsonObject * response, gboolean first,
+ int status, gpointer data)
+{
+ TrgTorrentModelClassUpdateStats stats;
+ TrgMainWindowPrivate *priv;
+ trg_client *client;
+
+ priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+ client = priv->client;
+
+ g_mutex_lock(client->updateMutex);
+ gdk_threads_enter();
+
+ /* Disconnected between request and response callback */
+ if (client->session == NULL) {
+ gdk_threads_leave();
+ g_mutex_unlock(client->updateMutex);
+ response_unref(response);
+ return;
+ }
+
+ if (status != CURLE_OK) {
+ client->failCount++;
+ if (client->failCount >= 3) {
+ trg_main_window_conn_changed(TRG_MAIN_WINDOW(data), FALSE);
+ trg_dialog_error_handler(TRG_MAIN_WINDOW(data),
+ response, status);
+ } else {
+ const gchar *msg;
+ gchar *statusBarMsg;
+
+ msg = make_error_message(response, status);
+ statusBarMsg =
+ g_strdup_printf("Request %d/%d failed: %s",
+ client->failCount, 3, msg);
+ trg_status_bar_push_connection_msg(priv->statusBar,
+ statusBarMsg);
+ g_free((gpointer) msg);
+ g_free(statusBarMsg);
+ g_timeout_add_seconds(3, trg_update_torrents_timerfunc, data);
+ }
+ gdk_threads_leave();
+ g_mutex_unlock(client->updateMutex);
+ response_unref(response);
+ return;
+ }
+
+ client->failCount = 0;
+ stats.downRateTotal = 0;
+ stats.upRateTotal = 0;
+ stats.seeding = 0;
+ stats.down = 0;
+ stats.paused = 0;
+
+ trg_torrent_model_update(priv->torrentModel, priv->client,
+ response, &stats, first);
+
+ trg_status_bar_update(priv->statusBar, &stats);
+
+ update_selected_torrent_notebook(TRG_MAIN_WINDOW(data), first);
+
+ g_timeout_add_seconds(3, trg_update_torrents_timerfunc, data);
+
+ client->updateSerial++;
+
+ gdk_threads_leave();
+ g_mutex_unlock(client->updateMutex);
+ response_unref(response);
+}
+
+static void entry_filter_changed_cb(GtkWidget * w, gpointer data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+ gboolean clearSensitive = gtk_entry_get_text_length(GTK_ENTRY(w)) > 0;
+
+ gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER
+ (priv->filteredTorrentModel));
+
+#if GTK_CHECK_VERSION( 2,16,0 )
+ g_object_set(priv->filterEntryClearButton,
+ "secondary-icon-sensitive", clearSensitive, NULL);
+#else
+ gtk_widget_set_sensitive(priv->filterEntryClearButton, clearSensitive);
+#endif
+}
+
+static void torrent_state_selection_changed(TrgStateSelector * selector,
+ guint flag, gpointer data)
+{
+ gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(data));
+}
+
+static
+void trg_main_window_conn_changed(TrgMainWindow * win, gboolean connected)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(win);
+ trg_client *tc = priv->client;
+
+ trg_toolbar_connected_change(priv->toolBar, connected);
+ trg_menu_bar_connected_change(priv->menuBar, connected);
+
+ gtk_widget_set_sensitive(GTK_WIDGET(priv->torrentTreeView), connected);
+ gtk_widget_set_sensitive(GTK_WIDGET(priv->peersTreeView), connected);
+ gtk_widget_set_sensitive(GTK_WIDGET(priv->filesTreeView), connected);
+ gtk_widget_set_sensitive(GTK_WIDGET(priv->trackersTreeView),
+ connected);
+ gtk_widget_set_sensitive(GTK_WIDGET(priv->genDetails), connected);
+
+ if (connected == FALSE) {
+ json_object_unref(tc->session);
+ tc->session = NULL;
+
+ gtk_list_store_clear(GTK_LIST_STORE(priv->torrentModel));
+ trg_main_window_torrent_scrub(win);
+ }
+}
+
+static void
+trg_main_window_get_property(GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(object);
+ switch (property_id) {
+ case PROP_CLIENT:
+ g_value_set_pointer(value, priv->client);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_main_window_set_property(GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(object);
+ switch (property_id) {
+ case PROP_CLIENT:
+ priv->client = g_value_get_pointer(value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void quit_cb(GtkWidget * w, gpointer data)
+{
+ gtk_widget_destroy(GTK_WIDGET(data));
+}
+
+static TrgMenuBar *trg_main_window_menu_bar_new(TrgMainWindow * win)
+{
+ GObject *b_connect, *b_disconnect, *b_add, *b_resume, *b_pause,
+ *b_verify, *b_remove, *b_delete, *b_props, *b_local_prefs,
+ *b_remote_prefs, *b_about, *b_view_states, *b_view_notebook,
+ *b_add_url, *b_quit;
+ TrgMenuBar *menuBar;
+
+ menuBar = trg_menu_bar_new(win);
+ g_object_get(menuBar,
+ "connect-button", &b_connect,
+ "disconnect-button", &b_disconnect,
+ "add-button", &b_add,
+ "add-url-button", &b_add_url,
+ "resume-button", &b_resume,
+ "pause-button", &b_pause,
+ "delete-button", &b_delete,
+ "remove-button", &b_remove,
+ "verify-button", &b_verify,
+ "props-button", &b_props,
+ "remote-prefs-button", &b_remote_prefs,
+ "local-prefs-button", &b_local_prefs,
+ "view-notebook-button", &b_view_notebook,
+ "view-states-button", &b_view_states,
+ "about-button", &b_about, "quit-button", &b_quit, NULL);
+
+ g_signal_connect(b_connect, "activate", G_CALLBACK(connect_cb), win);
+ g_signal_connect(b_disconnect, "activate",
+ G_CALLBACK(disconnect_cb), win);
+ g_signal_connect(b_add, "activate", G_CALLBACK(add_cb), win);
+ g_signal_connect(b_add_url, "activate", G_CALLBACK(add_url_cb), win);
+ g_signal_connect(b_resume, "activate", G_CALLBACK(resume_cb), win);
+ g_signal_connect(b_pause, "activate", G_CALLBACK(pause_cb), win);
+ g_signal_connect(b_verify, "activate", G_CALLBACK(verify_cb), win);
+ g_signal_connect(b_delete, "activate", G_CALLBACK(delete_cb), win);
+ g_signal_connect(b_remove, "activate", G_CALLBACK(remove_cb), win);
+ g_signal_connect(b_about, "activate", G_CALLBACK(open_about_cb), win);
+ g_signal_connect(b_local_prefs, "activate",
+ G_CALLBACK(open_local_prefs_cb), win);
+ g_signal_connect(b_remote_prefs, "activate",
+ G_CALLBACK(open_remote_prefs_cb), win);
+ g_signal_connect(b_view_notebook, "toggled",
+ G_CALLBACK(view_notebook_toggled_cb), win);
+ g_signal_connect(b_view_states, "toggled",
+ G_CALLBACK(view_states_toggled_cb), win);
+ g_signal_connect(b_props, "activate", G_CALLBACK(open_props_cb), win);
+ g_signal_connect(b_quit, "activate", G_CALLBACK(quit_cb), win);
+
+ return menuBar;
+}
+
+static void status_icon_activated(GtkStatusIcon * icon, gpointer data)
+{
+ gtk_window_present(GTK_WINDOW(data));
+}
+
+static void clear_filter_entry_cb(GtkWidget * w, gpointer data)
+{
+ gtk_entry_set_text(GTK_ENTRY(w), "");
+}
+
+static gboolean torrent_tv_key_press_event(GtkWidget * w,
+ GdkEventKey * key,
+ gpointer data)
+{
+ if (key->keyval == GDK_KEY_Delete) {
+ if (key->state & GDK_SHIFT_MASK)
+ delete_cb(w, data);
+ else
+ remove_cb(w, data);
+ }
+ return FALSE;
+}
+
+static
+GtkWidget *trg_imagemenuitem_new(GtkMenuShell * shell, char *text,
+ char *stock_id, gboolean sensitive,
+ GCallback cb, gpointer cbdata)
+{
+ GtkWidget *item = gtk_image_menu_item_new_with_label(stock_id);
+
+ gtk_image_menu_item_set_use_stock(GTK_IMAGE_MENU_ITEM(item), TRUE);
+ gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM
+ (item), TRUE);
+ gtk_menu_item_set_label(GTK_MENU_ITEM(item), text);
+ g_signal_connect(item, "activate", cb, cbdata);
+ gtk_widget_set_sensitive(item, sensitive);
+ gtk_menu_shell_append(shell, item);
+
+ return item;
+}
+
+static void
+trg_torrent_tv_view_menu(GtkWidget * treeview, GdkEventButton * event,
+ gpointer data)
+{
+ GtkWidget *menu;
+
+ menu = gtk_menu_new();
+
+ trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Properties",
+ GTK_STOCK_PROPERTIES, TRUE,
+ G_CALLBACK(open_props_cb), data);
+ trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Resume",
+ GTK_STOCK_MEDIA_PLAY, TRUE,
+ G_CALLBACK(resume_cb), data);
+ trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Pause",
+ GTK_STOCK_MEDIA_PAUSE, TRUE,
+ G_CALLBACK(pause_cb), data);
+ trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Verify",
+ GTK_STOCK_REFRESH, TRUE, G_CALLBACK(verify_cb),
+ data);
+ trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Remove", GTK_STOCK_REMOVE,
+ TRUE, G_CALLBACK(remove_cb), data);
+ trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Remove & Delete",
+ GTK_STOCK_DELETE, TRUE, G_CALLBACK(delete_cb),
+ data);
+
+ gtk_widget_show_all(menu);
+
+ gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
+ (event != NULL) ? event->button : 0,
+ gdk_event_get_time((GdkEvent *) event));
+}
+
+static void
+trg_status_icon_view_menu(GtkStatusIcon * icon, GdkEventButton * event,
+ gpointer data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+ gboolean connected = priv->client->session != NULL;
+ GtkWidget *menu;
+
+ menu = gtk_menu_new();
+
+ trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Connect",
+ GTK_STOCK_CONNECT, !connected,
+ G_CALLBACK(connect_cb), data);
+ trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Disconnect",
+ GTK_STOCK_DISCONNECT, connected,
+ G_CALLBACK(disconnect_cb), data);
+ trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Add", GTK_STOCK_ADD,
+ connected, G_CALLBACK(add_cb), data);
+ trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Add from URL",
+ GTK_STOCK_ADD, connected, G_CALLBACK(add_url_cb),
+ data);
+ trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Quit", GTK_STOCK_QUIT,
+ TRUE, G_CALLBACK(quit_cb), data);
+
+ gtk_widget_show_all(menu);
+
+ gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
+ (event != NULL) ? event->button : 0,
+ gdk_event_get_time((GdkEvent *) event));
+}
+
+static gboolean trg_status_icon_popup_menu_cb(GtkStatusIcon * icon,
+ gpointer userdata)
+{
+ trg_status_icon_view_menu(icon, NULL, userdata);
+ return TRUE;
+}
+
+static gboolean status_icon_button_press_event(GtkStatusIcon * icon,
+ GdkEventButton * event,
+ gpointer data)
+{
+ if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
+ trg_status_icon_view_menu(icon, event, data);
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+static gboolean
+torrent_tv_button_pressed_cb(GtkWidget * treeview, GdkEventButton * event,
+ gpointer userdata)
+{
+ GtkTreeSelection *selection;
+ GtkTreePath *path;
+
+ if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
+
+ if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview),
+ (gint) event->x,
+ (gint) event->y, &path,
+ NULL, NULL, NULL)) {
+ if (!gtk_tree_selection_path_is_selected(selection, path)) {
+ gtk_tree_selection_unselect_all(selection);
+ gtk_tree_selection_select_path(selection, path);
+ }
+
+ gtk_tree_path_free(path);
+
+ trg_torrent_tv_view_menu(treeview, event, userdata);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean torrent_tv_popup_menu_cb(GtkWidget * treeview,
+ gpointer userdata)
+{
+ trg_torrent_tv_view_menu(treeview, NULL, userdata);
+ return TRUE;
+}
+
+static GObject *trg_main_window_constructor(GType type,
+ guint
+ n_construct_properties,
+ GObjectConstructParam
+ * construct_params)
+{
+ TrgMainWindow *self;
+ TrgMainWindowPrivate *priv;
+ GtkWidget *w;
+ GtkWidget *outerVbox;
+ GError *iconError = NULL;
+ GtkWidget *toolbarHbox;
+ gint width, height;
+
+ self = TRG_MAIN_WINDOW(G_OBJECT_CLASS
+ (trg_main_window_parent_class)->constructor
+ (type, n_construct_properties,
+ construct_params));
+ priv = TRG_MAIN_WINDOW_GET_PRIVATE(self);
+
+ gtk_window_set_title(GTK_WINDOW(self), PACKAGE_NAME);
+ gtk_container_set_border_width(GTK_CONTAINER(self), 5);
+ gtk_window_set_default_size(GTK_WINDOW(self), 1000, 600);
+ g_signal_connect(G_OBJECT(self), "delete-event",
+ G_CALLBACK(delete_event), NULL);
+ g_signal_connect(G_OBJECT(self), "destroy", G_CALLBACK(destroy_window),
+ NULL);
+
+ gtk_window_set_icon_from_file
+ (GTK_WINDOW(self), WINDOW_ICON_FILE, &iconError);
+
+ if (iconError != NULL) {
+ g_printf("setting icon failed: %s\n", iconError->message);
+ g_error_free(iconError);
+ }
+
+ priv->torrentModel = trg_torrent_model_new();
+ g_signal_connect(priv->torrentModel, "torrent-completed",
+ G_CALLBACK(on_torrent_completed), self);
+
+ priv->filteredTorrentModel =
+ gtk_tree_model_filter_new(GTK_TREE_MODEL(priv->torrentModel),
+ NULL);
+ gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER
+ (priv->filteredTorrentModel),
+ trg_torrent_tree_view_visible_func,
+ self, NULL);
+
+ priv->sortedTorrentModel =
+ gtk_tree_model_sort_new_with_model(priv->filteredTorrentModel);
+
+ priv->torrentTreeView =
+ trg_main_window_torrent_tree_view_new(self,
+ priv->sortedTorrentModel,
+ priv->stateSelector);
+ g_signal_connect(priv->torrentTreeView, "key-press-event",
+ G_CALLBACK(torrent_tv_key_press_event), self);
+ g_signal_connect(priv->torrentTreeView, "popup-menu",
+ G_CALLBACK(torrent_tv_popup_menu_cb), self);
+ g_signal_connect(priv->torrentTreeView, "button-press-event",
+ G_CALLBACK(torrent_tv_button_pressed_cb), self);
+
+
+ outerVbox = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(self), outerVbox);
+
+ priv->menuBar = trg_main_window_menu_bar_new(self);
+ gtk_box_pack_start(GTK_BOX(outerVbox), GTK_WIDGET(priv->menuBar),
+ FALSE, FALSE, 0);
+
+ toolbarHbox = gtk_hbox_new(FALSE, 0);
+ priv->toolBar = trg_main_window_toolbar_new(self);
+ gtk_box_pack_start(GTK_BOX(toolbarHbox), GTK_WIDGET(priv->toolBar),
+ TRUE, TRUE, 0);
+
+#if GTK_CHECK_VERSION( 2,16,0 )
+ w = gtk_entry_new();
+ gtk_entry_set_icon_from_stock(GTK_ENTRY(w),
+ GTK_ENTRY_ICON_SECONDARY,
+ GTK_STOCK_CLEAR);
+ g_signal_connect(w, "icon-release",
+ G_CALLBACK(clear_filter_entry_cb), w);
+ gtk_box_pack_start(GTK_BOX(toolbarHbox), w, FALSE, FALSE, 0);
+ g_object_set(w, "secondary-icon-sensitive", FALSE, NULL);
+ priv->filterEntryClearButton = priv->filterEntry = w;
+#else
+ priv->filterEntry = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(toolbarHbox), priv->filterEntry, FALSE,
+ FALSE, 0);
+ w = gtk_button_new();
+ gtk_button_set_relief(GTK_BUTTON(w), GTK_RELIEF_NONE);
+ gtk_button_set_image(GTK_BUTTON(w),
+ gtk_image_new_from_stock(GTK_STOCK_CLEAR,
+ GTK_ICON_SIZE_MENU));
+ gtk_box_pack_start(GTK_BOX(toolbarHbox), w, FALSE, FALSE, 0);
+ g_signal_connect_swapped(w, "clicked",
+ G_CALLBACK(clear_filter_entry_cb),
+ priv->filterEntry);
+ priv->filterEntryClearButton = w;
+#endif
+
+ g_signal_connect(G_OBJECT(priv->filterEntry), "changed",
+ G_CALLBACK(entry_filter_changed_cb), self);
+
+ gtk_box_pack_start(GTK_BOX(outerVbox), GTK_WIDGET(toolbarHbox),
+ FALSE, FALSE, 0);
+
+ priv->vpaned = gtk_vpaned_new();
+ priv->hpaned = gtk_hpaned_new();
+ gtk_box_pack_start(GTK_BOX(outerVbox), priv->vpaned, TRUE, TRUE, 0);
+ gtk_paned_pack1(GTK_PANED(priv->vpaned), priv->hpaned, TRUE, TRUE);
+
+ priv->stateSelector = trg_state_selector_new();
+ gtk_paned_pack1(GTK_PANED(priv->hpaned),
+ GTK_WIDGET(priv->stateSelector), FALSE, FALSE);
+
+ gtk_paned_pack2(GTK_PANED(priv->hpaned),
+ my_scrolledwin_new(GTK_WIDGET
+ (priv->torrentTreeView)), TRUE,
+ TRUE);
+
+ g_signal_connect(G_OBJECT(priv->stateSelector),
+ "torrent-state-changed",
+ G_CALLBACK(torrent_state_selection_changed),
+ priv->filteredTorrentModel);
+
+ priv->notebook = trg_main_window_notebook_new(self);
+ gtk_paned_pack2(GTK_PANED(priv->vpaned), priv->notebook, FALSE, FALSE);
+
+ priv->statusIcon = gtk_status_icon_new_from_file(WINDOW_ICON_FILE);
+ gtk_status_icon_set_screen(priv->statusIcon,
+ gtk_window_get_screen(GTK_WINDOW(self)));
+ g_signal_connect(priv->statusIcon, "activate",
+ G_CALLBACK(status_icon_activated), self);
+ g_signal_connect(priv->statusIcon, "button-press-event",
+ G_CALLBACK(status_icon_button_press_event), self);
+ g_signal_connect(priv->statusIcon, "popup-menu",
+ G_CALLBACK(trg_status_icon_popup_menu_cb), self);
+
+ priv->statusBar = trg_status_bar_new(priv->statusIcon);
+ gtk_box_pack_start(GTK_BOX(outerVbox), GTK_WIDGET(priv->statusBar),
+ FALSE, FALSE, 2);
+
+ width =
+ gconf_client_get_int(priv->client->gconf,
+ TRG_GCONF_KEY_WINDOW_WIDTH, NULL);
+ height =
+ gconf_client_get_int(priv->client->gconf,
+ TRG_GCONF_KEY_WINDOW_HEIGHT, NULL);
+
+ if (width > 0 && height > 0)
+ gtk_window_set_default_size(GTK_WINDOW(self), width, height);
+
+ return G_OBJECT(self);
+}
+
+static void trg_main_window_class_init(TrgMainWindowClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ g_type_class_add_private(klass, sizeof(TrgMainWindowPrivate));
+
+ object_class->constructor = trg_main_window_constructor;
+ object_class->get_property = trg_main_window_get_property;
+ object_class->set_property = trg_main_window_set_property;
+
+ g_object_class_install_property(object_class,
+ PROP_CLIENT,
+ g_param_spec_pointer
+ ("trg-client", "TClient",
+ "Client",
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+}
+
+void auto_connect_if_required(TrgMainWindow * win, trg_client * tc)
+{
+ gchar *host =
+ gconf_client_get_string(tc->gconf, TRG_GCONF_KEY_HOSTNAME,
+ NULL);
+ if (host != NULL) {
+ gint len = strlen(host);
+ g_free(host);
+ if (len > 0
+ && gconf_client_get_bool(tc->gconf,
+ TRG_GCONF_KEY_AUTO_CONNECT,
+ NULL) == TRUE)
+ connect_cb(NULL, win);
+ }
+}
+
+TrgMainWindow *trg_main_window_new(trg_client * tc)
+{
+ return g_object_new(TRG_TYPE_MAIN_WINDOW, "trg-client", tc, NULL);
+}
diff --git a/src/trg-main-window.h b/src/trg-main-window.h
new file mode 100644
index 0000000..24cac9f
--- /dev/null
+++ b/src/trg-main-window.h
@@ -0,0 +1,71 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MAIN_WINDOW_H_
+#define MAIN_WINDOW_H_
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+
+#include "trg-torrent-model.h"
+#include "trg-peers-model.h"
+#include "trg-files-model.h"
+#include "trg-trackers-model.h"
+#include "trg-general-panel.h"
+#include "trg-torrent-tree-view.h"
+#include "trg-client.h"
+
+G_BEGIN_DECLS
+#define TRG_TYPE_MAIN_WINDOW trg_main_window_get_type()
+#define TRG_MAIN_WINDOW(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_MAIN_WINDOW, TrgMainWindow))
+#define TRG_MAIN_WINDOW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_MAIN_WINDOW, TrgMainWindowClass))
+#define TRG_IS_MAIN_WINDOW(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_MAIN_WINDOW))
+#define TRG_IS_MAIN_WINDOW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_MAIN_WINDOW))
+#define TRG_MAIN_WINDOW_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_MAIN_WINDOW, TrgMainWindowClass))
+ typedef struct {
+ GtkWindow parent;
+} TrgMainWindow;
+
+typedef struct {
+ GtkWindowClass parent_class;
+} TrgMainWindowClass;
+
+GType trg_main_window_get_type(void);
+
+TrgMainWindow *trg_main_window_new(trg_client * tc);
+gboolean trg_add_from_filename(TrgMainWindow * win, gchar * fileName);
+void on_generic_interactive_action(JsonObject * response,
+ int status, gpointer data);
+void auto_connect_if_required(TrgMainWindow * win, trg_client * tc);
+void on_session_set(JsonObject * response, int status, gpointer data);
+
+G_END_DECLS struct add_torrent_threadfunc_args {
+ GSList *list;
+ trg_client *client;
+ gpointer cb_data;
+};
+
+#endif /* MAIN_WINDOW_H_ */
diff --git a/src/trg-menu-bar.c b/src/trg-menu-bar.c
new file mode 100644
index 0000000..23354b3
--- /dev/null
+++ b/src/trg-menu-bar.c
@@ -0,0 +1,374 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <gtk/gtk.h>
+#include "trg-main-window.h"
+#include "trg-menu-bar.h"
+
+enum {
+ PROP_0,
+ PROP_CONNECT_BUTTON,
+ PROP_DISCONNECT_BUTTON,
+ PROP_ADD_BUTTON,
+ PROP_ADD_URL_BUTTON,
+ PROP_REMOVE_BUTTON,
+ PROP_DELETE_BUTTON,
+ PROP_RESUME_BUTTON,
+ PROP_PAUSE_BUTTON,
+ PROP_VERIFY_BUTTON,
+ PROP_PROPS_BUTTON,
+ PROP_REMOTE_PREFS_BUTTON,
+ PROP_LOCAL_PREFS_BUTTON,
+ PROP_ABOUT_BUTTON,
+ PROP_VIEW_STATES_BUTTON,
+ PROP_VIEW_NOTEBOOK_BUTTON,
+ PROP_QUIT
+};
+
+G_DEFINE_TYPE(TrgMenuBar, trg_menu_bar, GTK_TYPE_MENU_BAR)
+#define TRG_MENU_BAR_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_MENU_BAR, TrgMenuBarPrivate))
+typedef struct _TrgMenuBarPrivate TrgMenuBarPrivate;
+
+struct _TrgMenuBarPrivate {
+ GtkWidget *mb_connect;
+ GtkWidget *mb_disconnect;
+ GtkWidget *mb_add;
+ GtkWidget *mb_add_url;
+ GtkWidget *mb_remove;
+ GtkWidget *mb_delete;
+ GtkWidget *mb_resume;
+ GtkWidget *mb_pause;
+ GtkWidget *mb_verify;
+ GtkWidget *mb_props;
+ GtkWidget *mb_local_prefs;
+ GtkWidget *mb_remote_prefs;
+ GtkWidget *mb_view_states;
+ GtkWidget *mb_view_notebook;
+ GtkWidget *mb_about;
+ GtkWidget *mb_quit;
+};
+
+void trg_menu_bar_connected_change(TrgMenuBar * mb, gboolean connected)
+{
+ TrgMenuBarPrivate *priv = TRG_MENU_BAR_GET_PRIVATE(mb);
+
+ gtk_widget_set_sensitive(priv->mb_add, connected);
+ gtk_widget_set_sensitive(priv->mb_add_url, connected);
+ gtk_widget_set_sensitive(priv->mb_connect, !connected);
+ gtk_widget_set_sensitive(priv->mb_disconnect, connected);
+ gtk_widget_set_sensitive(priv->mb_remote_prefs, connected);
+}
+
+void trg_menu_bar_torrent_actions_sensitive(TrgMenuBar * mb,
+ gboolean sensitive)
+{
+ TrgMenuBarPrivate *priv = TRG_MENU_BAR_GET_PRIVATE(mb);
+
+ gtk_widget_set_sensitive(priv->mb_props, sensitive);
+ gtk_widget_set_sensitive(priv->mb_remove, sensitive);
+ gtk_widget_set_sensitive(priv->mb_delete, sensitive);
+ gtk_widget_set_sensitive(priv->mb_resume, sensitive);
+ gtk_widget_set_sensitive(priv->mb_pause, sensitive);
+ gtk_widget_set_sensitive(priv->mb_verify, sensitive);
+}
+
+static void
+trg_menu_bar_get_property(GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ TrgMenuBarPrivate *priv = TRG_MENU_BAR_GET_PRIVATE(object);
+ switch (property_id) {
+ case PROP_CONNECT_BUTTON:
+ g_value_set_object(value, priv->mb_connect);
+ break;
+ case PROP_DISCONNECT_BUTTON:
+ g_value_set_object(value, priv->mb_disconnect);
+ break;
+ case PROP_ADD_BUTTON:
+ g_value_set_object(value, priv->mb_add);
+ break;
+ case PROP_ADD_URL_BUTTON:
+ g_value_set_object(value, priv->mb_add_url);
+ break;
+ case PROP_REMOVE_BUTTON:
+ g_value_set_object(value, priv->mb_remove);
+ break;
+ case PROP_DELETE_BUTTON:
+ g_value_set_object(value, priv->mb_delete);
+ break;
+ case PROP_RESUME_BUTTON:
+ g_value_set_object(value, priv->mb_resume);
+ break;
+ case PROP_PAUSE_BUTTON:
+ g_value_set_object(value, priv->mb_pause);
+ break;
+ case PROP_VERIFY_BUTTON:
+ g_value_set_object(value, priv->mb_verify);
+ break;
+ case PROP_PROPS_BUTTON:
+ g_value_set_object(value, priv->mb_props);
+ break;
+ case PROP_REMOTE_PREFS_BUTTON:
+ g_value_set_object(value, priv->mb_remote_prefs);
+ break;
+ case PROP_LOCAL_PREFS_BUTTON:
+ g_value_set_object(value, priv->mb_local_prefs);
+ break;
+ case PROP_ABOUT_BUTTON:
+ g_value_set_object(value, priv->mb_about);
+ break;
+ case PROP_VIEW_STATES_BUTTON:
+ g_value_set_object(value, priv->mb_view_states);
+ break;
+ case PROP_VIEW_NOTEBOOK_BUTTON:
+ g_value_set_object(value, priv->mb_view_notebook);
+ break;
+ case PROP_QUIT:
+ g_value_set_object(value, priv->mb_quit);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_menu_bar_install_widget_prop(GObjectClass * class, guint propId,
+ const gchar * name, const gchar * nick)
+{
+ g_object_class_install_property(class,
+ propId,
+ g_param_spec_object(name,
+ nick,
+ nick,
+ GTK_TYPE_WIDGET,
+ G_PARAM_READABLE
+ |
+ G_PARAM_STATIC_NAME
+ |
+ G_PARAM_STATIC_NICK
+ |
+ G_PARAM_STATIC_BLURB));
+}
+
+static
+GtkWidget *trg_menu_bar_item_new(GtkMenuShell * shell, char *text,
+ char *stock_id, gboolean sensitive)
+{
+ GtkWidget *item = gtk_image_menu_item_new_with_label(stock_id);
+ gtk_image_menu_item_set_use_stock(GTK_IMAGE_MENU_ITEM(item), TRUE);
+
+ gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM
+ (item), TRUE);
+ gtk_menu_item_set_label(GTK_MENU_ITEM(item), text);
+ gtk_widget_set_sensitive(item, sensitive);
+
+ gtk_menu_shell_append(shell, item);
+
+ return item;
+}
+
+static GtkWidget *trg_menu_bar_view_menu_new(TrgMenuBarPrivate * priv)
+{
+ GtkWidget *view = gtk_menu_item_new_with_label("View");
+ GtkWidget *viewMenu = gtk_menu_new();
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(view), viewMenu);
+
+ priv->mb_view_states =
+ gtk_check_menu_item_new_with_label("State selector");
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM
+ (priv->mb_view_states), TRUE);
+
+ priv->mb_view_notebook =
+ gtk_check_menu_item_new_with_label("Torrent details");
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM
+ (priv->mb_view_notebook), TRUE);
+
+ gtk_menu_shell_append(GTK_MENU_SHELL(viewMenu), priv->mb_view_states);
+ gtk_menu_shell_append(GTK_MENU_SHELL(viewMenu),
+ priv->mb_view_notebook);
+
+ return view;
+}
+
+static
+GtkWidget *trg_menu_bar_options_menu_new(TrgMenuBarPrivate * priv)
+{
+ GtkWidget *opts = gtk_menu_item_new_with_label("Options");
+ GtkWidget *optsMenu = gtk_menu_new();
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(opts), optsMenu);
+
+ priv->mb_local_prefs =
+ trg_menu_bar_item_new(GTK_MENU_SHELL(optsMenu),
+ "Local Preferences",
+ GTK_STOCK_PREFERENCES, TRUE);
+
+ priv->mb_remote_prefs =
+ trg_menu_bar_item_new(GTK_MENU_SHELL(optsMenu),
+ "Remote Preferences",
+ GTK_STOCK_NETWORK, FALSE);
+
+ return opts;
+}
+
+static
+GtkWidget *trg_menu_bar_file_file_menu_new(TrgMenuBarPrivate * priv)
+{
+ GtkWidget *file = gtk_menu_item_new_with_label("File");
+ GtkWidget *fileMenu = gtk_menu_new();
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), fileMenu);
+
+ priv->mb_connect =
+ trg_menu_bar_item_new(GTK_MENU_SHELL(fileMenu), "Connect",
+ GTK_STOCK_CONNECT, TRUE);
+ priv->mb_disconnect =
+ trg_menu_bar_item_new(GTK_MENU_SHELL(fileMenu), "Disconnect",
+ GTK_STOCK_DISCONNECT, FALSE);
+ priv->mb_add =
+ trg_menu_bar_item_new(GTK_MENU_SHELL(fileMenu), "Add",
+ GTK_STOCK_ADD, FALSE);
+ priv->mb_add_url =
+ trg_menu_bar_item_new(GTK_MENU_SHELL(fileMenu), "Add from URL",
+ GTK_STOCK_ADD, FALSE);
+
+ priv->mb_quit = trg_menu_bar_item_new(GTK_MENU_SHELL(fileMenu), "Quit",
+ GTK_STOCK_QUIT, TRUE);
+
+ return file;
+}
+
+static
+GtkWidget *trg_menu_bar_torrent_menu_new(TrgMenuBarPrivate * priv)
+{
+ GtkWidget *torrent = gtk_menu_item_new_with_label("Torrent");
+ GtkWidget *torrentMenu = gtk_menu_new();
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(torrent), torrentMenu);
+
+ priv->mb_props =
+ trg_menu_bar_item_new(GTK_MENU_SHELL(torrentMenu),
+ "Properties", GTK_STOCK_PROPERTIES, FALSE);
+ priv->mb_resume =
+ trg_menu_bar_item_new(GTK_MENU_SHELL(torrentMenu), "Resume",
+ GTK_STOCK_MEDIA_PLAY, FALSE);
+ priv->mb_pause =
+ trg_menu_bar_item_new(GTK_MENU_SHELL(torrentMenu), "Pause",
+ GTK_STOCK_MEDIA_PAUSE, FALSE);
+ priv->mb_verify =
+ trg_menu_bar_item_new(GTK_MENU_SHELL(torrentMenu), "Verify",
+ GTK_STOCK_REFRESH, FALSE);
+ priv->mb_remove =
+ trg_menu_bar_item_new(GTK_MENU_SHELL(torrentMenu), "Remove",
+ GTK_STOCK_REMOVE, FALSE);
+ priv->mb_delete =
+ trg_menu_bar_item_new(GTK_MENU_SHELL(torrentMenu),
+ "Remove and Delete", GTK_STOCK_DELETE,
+ FALSE);
+
+ return torrent;
+}
+
+static
+GtkWidget *trg_menu_bar_help_menu_new(TrgMenuBar * menuBar)
+{
+ TrgMenuBarPrivate *priv = TRG_MENU_BAR_GET_PRIVATE(menuBar);
+
+ GtkWidget *help = gtk_menu_item_new_with_label("Help");
+ GtkWidget *helpMenu = gtk_menu_new();
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(help), helpMenu);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menuBar), help);
+
+ priv->mb_about =
+ trg_menu_bar_item_new(GTK_MENU_SHELL(helpMenu), "About",
+ GTK_STOCK_ABOUT, TRUE);
+
+ return helpMenu;
+}
+
+static void trg_menu_bar_class_init(TrgMenuBarClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+ object_class->get_property = trg_menu_bar_get_property;
+
+ g_type_class_add_private(klass, sizeof(TrgMenuBarPrivate));
+
+ trg_menu_bar_install_widget_prop(object_class, PROP_CONNECT_BUTTON,
+ "connect-button", "Connect Button");
+ trg_menu_bar_install_widget_prop(object_class,
+ PROP_DISCONNECT_BUTTON,
+ "disconnect-button",
+ "Disconnect Button");
+ trg_menu_bar_install_widget_prop(object_class, PROP_ADD_BUTTON,
+ "add-button", "Add Button");
+ trg_menu_bar_install_widget_prop(object_class, PROP_ADD_URL_BUTTON,
+ "add-url-button", "Add URL Button");
+ trg_menu_bar_install_widget_prop(object_class, PROP_REMOVE_BUTTON,
+ "remove-button", "Remove Button");
+ trg_menu_bar_install_widget_prop(object_class, PROP_DELETE_BUTTON,
+ "delete-button", "Delete Button");
+ trg_menu_bar_install_widget_prop(object_class, PROP_RESUME_BUTTON,
+ "resume-button", "Resume Button");
+ trg_menu_bar_install_widget_prop(object_class, PROP_VERIFY_BUTTON,
+ "verify-button", "Verify Button");
+ trg_menu_bar_install_widget_prop(object_class, PROP_PAUSE_BUTTON,
+ "pause-button", "Pause Button");
+ trg_menu_bar_install_widget_prop(object_class, PROP_PROPS_BUTTON,
+ "props-button", "Props Button");
+ trg_menu_bar_install_widget_prop(object_class, PROP_ABOUT_BUTTON,
+ "about-button", "About Button");
+ trg_menu_bar_install_widget_prop(object_class,
+ PROP_VIEW_STATES_BUTTON,
+ "view-states-button",
+ "View states Button");
+ trg_menu_bar_install_widget_prop(object_class,
+ PROP_VIEW_NOTEBOOK_BUTTON,
+ "view-notebook-button",
+ "View notebook Button");
+ trg_menu_bar_install_widget_prop(object_class,
+ PROP_REMOTE_PREFS_BUTTON,
+ "remote-prefs-button",
+ "Remote Prefs Button");
+ trg_menu_bar_install_widget_prop(object_class,
+ PROP_LOCAL_PREFS_BUTTON,
+ "local-prefs-button",
+ "Local Prefs Button");
+ trg_menu_bar_install_widget_prop(object_class,
+ PROP_QUIT,
+ "quit-button", "Quit Button");
+}
+
+static void trg_menu_bar_init(TrgMenuBar * self)
+{
+ TrgMenuBarPrivate *priv = TRG_MENU_BAR_GET_PRIVATE(self);
+
+ gtk_menu_shell_append(GTK_MENU_SHELL(self),
+ trg_menu_bar_file_file_menu_new(priv));
+ gtk_menu_shell_append(GTK_MENU_SHELL(self),
+ trg_menu_bar_torrent_menu_new(priv));
+ gtk_menu_shell_append(GTK_MENU_SHELL(self),
+ trg_menu_bar_options_menu_new(priv));
+ gtk_menu_shell_append(GTK_MENU_SHELL(self),
+ trg_menu_bar_view_menu_new(priv));
+ trg_menu_bar_help_menu_new(TRG_MENU_BAR(self));
+}
+
+TrgMenuBar *trg_menu_bar_new(TrgMainWindow * win)
+{
+ GObject *obj = g_object_new(TRG_TYPE_MENU_BAR, NULL);
+ return TRG_MENU_BAR(obj);
+}
diff --git a/src/trg-menu-bar.h b/src/trg-menu-bar.h
new file mode 100644
index 0000000..43c5f45
--- /dev/null
+++ b/src/trg-menu-bar.h
@@ -0,0 +1,57 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TRG_MENU_BAR_H_
+#define TRG_MENU_BAR_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "trg-main-window.h"
+
+G_BEGIN_DECLS
+#define TRG_TYPE_MENU_BAR trg_menu_bar_get_type()
+#define TRG_MENU_BAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_MENU_BAR, TrgMenuBar))
+#define TRG_MENU_BAR_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_MENU_BAR, TrgMenuBarClass))
+#define TRG_IS_MENU_BAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_MENU_BAR))
+#define TRG_IS_MENU_BAR_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_MENU_BAR))
+#define TRG_MENU_BAR_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_MENU_BAR, TrgMenuBarClass))
+ typedef struct {
+ GtkMenuBar parent;
+} TrgMenuBar;
+
+typedef struct {
+ GtkMenuBarClass parent_class;
+} TrgMenuBarClass;
+
+GType trg_menu_bar_get_type(void);
+
+TrgMenuBar *trg_menu_bar_new(TrgMainWindow * win);
+
+G_END_DECLS
+ void trg_menu_bar_torrent_actions_sensitive(TrgMenuBar * mb,
+ gboolean sensitive);
+void trg_menu_bar_connected_change(TrgMenuBar * mb, gboolean connected);
+
+#endif /* TRG_MENU_BAR_H_ */
diff --git a/src/trg-model.c b/src/trg-model.c
new file mode 100644
index 0000000..585c796
--- /dev/null
+++ b/src/trg-model.c
@@ -0,0 +1,65 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+struct trg_model_remove_removed_foreachfunc_args {
+ gint64 currentSerial;
+ gint serial_column;
+ GList *toRemove;
+};
+
+gboolean
+trg_model_remove_removed_foreachfunc(GtkTreeModel * model,
+ GtkTreePath * path,
+ GtkTreeIter * iter, gpointer data)
+{
+ struct trg_model_remove_removed_foreachfunc_args *args =
+ (struct trg_model_remove_removed_foreachfunc_args *) data;
+ gint64 rowSerial;
+ gtk_tree_model_get(model, iter, args->serial_column, &rowSerial, -1);
+ if (rowSerial != args->currentSerial)
+ args->toRemove =
+ g_list_append(args->toRemove, gtk_tree_iter_copy(iter));
+
+ return FALSE;
+}
+
+void
+trg_model_remove_removed(GtkListStore * model, gint serial_column,
+ gint64 currentSerial)
+{
+ struct trg_model_remove_removed_foreachfunc_args args;
+ GList *li;
+
+ args.toRemove = NULL;
+ args.currentSerial = currentSerial;
+ args.serial_column = serial_column;
+ gtk_tree_model_foreach(GTK_TREE_MODEL(model),
+ trg_model_remove_removed_foreachfunc, &args);
+ if (args.toRemove != NULL) {
+ for (li = g_list_last(args.toRemove); li != NULL;
+ li = g_list_previous(li)) {
+ gtk_list_store_remove(model, (GtkTreeIter *) li->data);
+ gtk_tree_iter_free((GtkTreeIter *) li->data);
+ }
+ g_list_free(args.toRemove);
+ }
+}
diff --git a/src/trg-model.h b/src/trg-model.h
new file mode 100644
index 0000000..db03996
--- /dev/null
+++ b/src/trg-model.h
@@ -0,0 +1,29 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef TRG_MODEL_H_
+#define TRG_MODEL_H_
+
+#include <gtk/gtk.h>
+
+void trg_model_remove_removed(GtkListStore * model, gint serial_column,
+ gint64 currentSerial);
+
+#endif /* TRG_MODEL_H_ */
diff --git a/src/trg-peers-model.c b/src/trg-peers-model.c
new file mode 100644
index 0000000..b45ac20
--- /dev/null
+++ b/src/trg-peers-model.c
@@ -0,0 +1,225 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+#include <gio/gio.h>
+#include <glib/gstdio.h>
+#if HAVE_GEOIP
+#include <GeoIP.h>
+#endif
+
+#include "torrent.h"
+#include "tpeer.h"
+#include "trg-peers-model.h"
+#include "trg-model.h"
+#include "util.h"
+
+G_DEFINE_TYPE(TrgPeersModel, trg_peers_model, GTK_TYPE_LIST_STORE)
+
+static void trg_peers_model_class_init(TrgPeersModelClass * klass)
+{
+}
+
+gboolean
+find_existing_peer_item_foreachfunc(GtkTreeModel * model,
+ GtkTreePath * path, GtkTreeIter * iter,
+ gpointer data)
+{
+ struct peerAndIter *pi;
+ gchar *ip;
+
+ pi = (struct peerAndIter *) data;
+
+ gtk_tree_model_get(model, iter, PEERSCOL_IP, &ip, -1);
+ if (g_strcmp0(ip, pi->ip) == 0) {
+ pi->iter = *iter;
+ pi->found = TRUE;
+ }
+ g_free(ip);
+ return pi->found;
+}
+
+gboolean
+find_existing_peer_item(TrgPeersModel * model, JsonObject * p,
+ GtkTreeIter * iter)
+{
+ struct peerAndIter pi;
+ pi.ip = peer_get_address(p);
+ pi.found = FALSE;
+
+ gtk_tree_model_foreach(GTK_TREE_MODEL(model),
+ find_existing_peer_item_foreachfunc, &pi);
+
+ if (pi.found == TRUE)
+ *iter = pi.iter;
+
+ return pi.found;
+}
+
+static void resolved_dns_cb(GObject * source_object,
+ GAsyncResult * res, gpointer data)
+{
+ GtkTreeRowReference *treeRef;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+
+ treeRef = (GtkTreeRowReference *) data;
+ model = gtk_tree_row_reference_get_model(treeRef);
+ path = gtk_tree_row_reference_get_path(treeRef);
+
+ if (path != NULL) {
+ gchar *rdns =
+ g_resolver_lookup_by_address_finish(G_RESOLVER(source_object),
+ res, NULL);
+ if (rdns != NULL) {
+ GtkTreeIter iter;
+ if (gtk_tree_model_get_iter(model, &iter, path) == TRUE) {
+ gdk_threads_enter();
+ gtk_list_store_set(GTK_LIST_STORE(model),
+ &iter, PEERSCOL_HOST, rdns, -1);
+ gdk_threads_leave();
+ }
+ g_free(rdns);
+ }
+ gtk_tree_path_free(path);
+ }
+
+ gtk_tree_row_reference_free(treeRef);
+}
+
+void trg_peers_model_update(TrgPeersModel * model, gint64 updateSerial,
+ JsonObject * t, gboolean first)
+{
+ JsonArray *peers;
+ GtkTreeIter peerIter;
+ int j;
+ gboolean isNew;
+
+ peers = torrent_get_peers(t);
+
+ if (first == TRUE)
+ gtk_list_store_clear(GTK_LIST_STORE(model));
+
+ for (j = 0; j < json_array_get_length(peers); j++) {
+ JsonObject *peer;
+ const gchar *address, *flagStr;
+#if HAVE_GEOIP
+ GeoIP *gi;
+ const gchar *country = NULL;
+#endif
+ peer = json_node_get_object(json_array_get_element(peers, j));
+
+ if (first == TRUE
+ || find_existing_peer_item(model, peer, &peerIter) == FALSE) {
+ gtk_list_store_append(GTK_LIST_STORE(model), &peerIter);
+ isNew = TRUE;
+ } else {
+ isNew = FALSE;
+ }
+
+ address = peer_get_address(peer);
+ flagStr = peer_get_flagstr(peer);
+#if HAVE_GEOIP
+ if ((gi = g_object_get_data(G_OBJECT(model), "geoip")) != NULL)
+ country = GeoIP_country_name_by_addr(gi, address);
+#endif
+ gtk_list_store_set(GTK_LIST_STORE(model), &peerIter,
+ PEERSCOL_ICON,
+ GTK_STOCK_NETWORK, PEERSCOL_IP, address,
+#if HAVE_GEOIP
+ PEERSCOL_COUNTRY,
+ country != NULL ? country : "",
+#endif
+ PEERSCOL_FLAGS, flagStr,
+ PEERSCOL_PROGRESS,
+ peer_get_progress(peer),
+ PEERSCOL_DOWNSPEED,
+ peer_get_rate_to_client(peer),
+ PEERSCOL_UPSPEED,
+ peer_get_rate_to_peer(peer),
+ PEERSCOL_UPDATESERIAL, updateSerial, -1);
+
+ if (isNew == TRUE) {
+ GtkTreePath *path;
+ GtkTreeRowReference *treeRef;
+ GInetAddress *inetAddr;
+ GResolver *resolver;
+
+ path =
+ gtk_tree_model_get_path(GTK_TREE_MODEL(model), &peerIter);
+ treeRef =
+ gtk_tree_row_reference_new(GTK_TREE_MODEL(model), path);
+ gtk_tree_path_free(path);
+
+ inetAddr = g_inet_address_new_from_string(address);
+ resolver = g_resolver_get_default();
+ g_resolver_lookup_by_address_async(resolver,
+ inetAddr, NULL,
+ resolved_dns_cb, treeRef);
+ g_object_unref(resolver);
+ g_object_unref(inetAddr);
+ }
+ }
+
+ if (first == FALSE)
+ trg_model_remove_removed(GTK_LIST_STORE(model),
+ PEERSCOL_UPDATESERIAL, updateSerial);
+}
+
+static void trg_peers_model_init(TrgPeersModel * self)
+{
+ GType column_types[PEERSCOL_COLUMNS];
+
+ column_types[PEERSCOL_ICON] = G_TYPE_STRING;
+ column_types[PEERSCOL_IP] = G_TYPE_STRING;
+#if HAVE_GEOIP
+ column_types[PEERSCOL_COUNTRY] = G_TYPE_STRING;
+#endif
+ column_types[PEERSCOL_HOST] = G_TYPE_STRING;
+ column_types[PEERSCOL_FLAGS] = G_TYPE_STRING;
+ column_types[PEERSCOL_PROGRESS] = G_TYPE_DOUBLE;
+ column_types[PEERSCOL_DOWNSPEED] = G_TYPE_INT64;
+ column_types[PEERSCOL_UPSPEED] = G_TYPE_INT64;
+ column_types[PEERSCOL_UPDATESERIAL] = G_TYPE_INT64;
+
+ gtk_list_store_set_column_types(GTK_LIST_STORE(self),
+ PEERSCOL_COLUMNS, column_types);
+
+#if HAVE_GEOIP
+ if (g_file_test(TRG_GEOIP_DATABASE, G_FILE_TEST_EXISTS) == TRUE) {
+ GeoIP *gi = GeoIP_open(TRG_GEOIP_DATABASE,
+ GEOIP_STANDARD | GEOIP_CHECK_CACHE);
+ g_object_set_data(G_OBJECT(self), "geoip", gi);
+ }
+#endif
+}
+
+
+TrgPeersModel *trg_peers_model_new()
+{
+ GObject *obj = g_object_new(TRG_TYPE_PEERS_MODEL, NULL);
+
+ return TRG_PEERS_MODEL(obj);
+}
diff --git a/src/trg-peers-model.h b/src/trg-peers-model.h
new file mode 100644
index 0000000..0e18723
--- /dev/null
+++ b/src/trg-peers-model.h
@@ -0,0 +1,85 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef TRG_PEERS_MODEL_H_
+#define TRG_PEERS_MODEL_H_
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+#if HAVE_GEOIP
+#include <GeoIP.h>
+#endif
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+#define TRG_TYPE_PEERS_MODEL trg_peers_model_get_type()
+#define TRG_PEERS_MODEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_PEERS_MODEL, TrgPeersModel))
+#define TRG_PEERS_MODEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_PEERS_MODEL, TrgPeersModelClass))
+#define TRG_IS_PEERS_MODEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_PEERS_MODEL))
+#define TRG_IS_PEERS_MODEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_PEERS_MODEL))
+#define TRG_PEERS_MODEL_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_PEERS_MODEL, TrgPeersModelClass))
+ typedef struct {
+ GtkListStore parent;
+} TrgPeersModel;
+
+typedef struct {
+ GtkListStoreClass parent_class;
+} TrgPeersModelClass;
+
+GType trg_peers_model_get_type(void);
+
+TrgPeersModel *trg_peers_model_new();
+
+G_END_DECLS struct peerAndIter {
+ const gchar *ip;
+ GtkTreeIter iter;
+ gboolean found;
+};
+
+enum {
+ PEERSCOL_ICON,
+ PEERSCOL_IP,
+#if HAVE_GEOIP
+ PEERSCOL_COUNTRY,
+#endif
+ PEERSCOL_HOST,
+ PEERSCOL_FLAGS,
+ PEERSCOL_PROGRESS,
+ PEERSCOL_DOWNSPEED,
+ PEERSCOL_UPSPEED,
+ PEERSCOL_UPDATESERIAL,
+ PEERSCOL_COLUMNS
+};
+
+void trg_peers_model_update(TrgPeersModel * model, gint64 updateSerial,
+ JsonObject * t, gboolean first);
+
+#endif /* TRG_PEERS_MODEL_H_ */
+
+#define TRG_GEOIP_DATABASE "/usr/share/GeoIP/GeoIP.dat"
diff --git a/src/trg-peers-tree-view.c b/src/trg-peers-tree-view.c
new file mode 100644
index 0000000..bd109a5
--- /dev/null
+++ b/src/trg-peers-tree-view.c
@@ -0,0 +1,69 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if HAVE_GEOIP
+#include <GeoIP.h>
+#endif
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "trg-tree-view.h"
+#include "trg-peers-model.h"
+#include "trg-peers-tree-view.h"
+
+G_DEFINE_TYPE(TrgPeersTreeView, trg_peers_tree_view, TRG_TYPE_TREE_VIEW)
+
+static void trg_peers_tree_view_class_init(TrgPeersTreeViewClass * klass)
+{
+}
+
+static void trg_peers_tree_view_init(TrgPeersTreeView * self)
+{
+ trg_tree_view_add_pixbuf_text_column(TRG_TREE_VIEW
+ (self),
+ PEERSCOL_ICON,
+ PEERSCOL_IP, "IP", 180);
+#if HAVE_GEOIP
+ trg_tree_view_add_column(TRG_TREE_VIEW(self), "Country",
+ PEERSCOL_COUNTRY);
+#endif
+ trg_tree_view_add_column_fixed_width(TRG_TREE_VIEW(self), "Host",
+ PEERSCOL_HOST, 300);
+ trg_tree_view_add_column(TRG_TREE_VIEW(self), "Flags", PEERSCOL_FLAGS);
+ trg_tree_view_add_prog_column(TRG_TREE_VIEW(self), "Progress",
+ PEERSCOL_PROGRESS, -1);
+ trg_tree_view_add_speed_column(TRG_TREE_VIEW(self), "Down Speed",
+ PEERSCOL_DOWNSPEED, -1);
+ trg_tree_view_add_speed_column(TRG_TREE_VIEW(self), "Up Speed",
+ PEERSCOL_UPSPEED, -1);
+}
+
+TrgPeersTreeView *trg_peers_tree_view_new(TrgPeersModel * model)
+{
+ GObject *obj = g_object_new(TRG_TYPE_PEERS_TREE_VIEW, NULL);
+
+ gtk_tree_view_set_model(GTK_TREE_VIEW(obj), GTK_TREE_MODEL(model));
+
+ return TRG_PEERS_TREE_VIEW(obj);
+}
diff --git a/src/trg-peers-tree-view.h b/src/trg-peers-tree-view.h
new file mode 100644
index 0000000..ce3e5ab
--- /dev/null
+++ b/src/trg-peers-tree-view.h
@@ -0,0 +1,54 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef TRG_PEERS_TREE_VIEW_H_
+#define TRG_PEERS_TREE_VIEW_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "trg-peers-model.h"
+
+G_BEGIN_DECLS
+#define TRG_TYPE_PEERS_TREE_VIEW trg_peers_tree_view_get_type()
+#define TRG_PEERS_TREE_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_PEERS_TREE_VIEW, TrgPeersTreeView))
+#define TRG_PEERS_TREE_VIEW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_PEERS_TREE_VIEW, TrgPeersTreeViewClass))
+#define TRG_IS_PEERS_TREE_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_PEERS_TREE_VIEW))
+#define TRG_IS_PEERS_TREE_VIEW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_PEERS_TREE_VIEW))
+#define TRG_PEERS_TREE_VIEW_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_PEERS_TREE_VIEW, TrgPeersTreeViewClass))
+ typedef struct {
+ GtkTreeView parent;
+} TrgPeersTreeView;
+
+typedef struct {
+ GtkTreeViewClass parent_class;
+} TrgPeersTreeViewClass;
+
+GType trg_peers_tree_view_get_type(void);
+
+TrgPeersTreeView *trg_peers_tree_view_new(TrgPeersModel * model);
+
+G_END_DECLS
+#endif /* TRG_PEERS_TREE_VIEW_H_ */
diff --git a/src/trg-preferences-dialog.c b/src/trg-preferences-dialog.c
new file mode 100644
index 0000000..971ff80
--- /dev/null
+++ b/src/trg-preferences-dialog.c
@@ -0,0 +1,327 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <gconf/gconf-client.h>
+
+#include "hig.h"
+#include "trg-preferences-dialog.h"
+#include "trg-preferences.h"
+
+#define TRG_PREFERENCES_DIALOG_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((object), TRG_TYPE_PREFERENCES_DIALOG, TrgPreferencesDialogPrivate))
+
+G_DEFINE_TYPE(TrgPreferencesDialog, trg_preferences_dialog,
+ GTK_TYPE_DIALOG)
+
+enum {
+ PROP_0,
+ PROP_GCONF_CLIENT,
+ PROP_PARENT_WINDOW
+};
+
+#define GCONF_OBJECT_KEY "gconf-key"
+
+struct _TrgPreferencesDialogPrivate {
+ GConfClient *gconf;
+ GtkWindow *parent;
+};
+
+static GObject *instance = NULL;
+
+static void
+trg_preferences_dialog_set_property(GObject * object,
+ guint prop_id,
+ const GValue * value,
+ GParamSpec * pspec)
+{
+ TrgPreferencesDialog *pref_dlg = TRG_PREFERENCES_DIALOG(object);
+
+ switch (prop_id) {
+ case PROP_GCONF_CLIENT:
+ pref_dlg->priv->gconf = g_value_get_object(value);
+ break;
+ case PROP_PARENT_WINDOW:
+ pref_dlg->priv->parent = g_value_get_object(value);
+ break;
+ }
+}
+
+static void
+trg_preferences_response_cb(GtkDialog * dlg, gint res_id, gpointer data)
+{
+ switch (res_id) {
+ default:
+ gtk_widget_destroy(GTK_WIDGET(dlg));
+ instance = NULL;
+ }
+}
+
+static void
+trg_preferences_dialog_get_property(GObject * object,
+ guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ TrgPreferencesDialog *pref_dlg = TRG_PREFERENCES_DIALOG(object);
+
+ switch (prop_id) {
+ case PROP_GCONF_CLIENT:
+ g_value_set_object(value, pref_dlg->priv->gconf);
+ break;
+ case PROP_PARENT_WINDOW:
+ g_value_set_object(value, pref_dlg->priv->parent);
+ break;
+ }
+}
+
+static void toggled_cb(GtkToggleButton * w, gpointer gconf)
+{
+ const char *key;
+ gboolean flag;
+
+ key = g_object_get_data(G_OBJECT(w), GCONF_OBJECT_KEY);
+ flag = gtk_toggle_button_get_active(w);
+
+ gconf_client_set_bool(GCONF_CLIENT(gconf), key, flag, NULL);
+}
+
+static GtkWidget *new_check_button(GConfClient * gconf,
+ const char *mnemonic, const char *key)
+{
+ GtkWidget *w = gtk_check_button_new_with_mnemonic(mnemonic);
+
+ g_object_set_data_full(G_OBJECT(w), GCONF_OBJECT_KEY,
+ g_strdup(key), g_free);
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w),
+ gconf_client_get_bool(gconf, key, NULL));
+ g_signal_connect(w, "toggled", G_CALLBACK(toggled_cb), gconf);
+ return w;
+}
+
+static void spun_cb_int(GtkWidget * widget, gpointer gconf)
+{
+ gchar *key;
+
+ key = g_object_get_data(G_OBJECT(widget), GCONF_OBJECT_KEY);
+
+ gconf_client_set_int(GCONF_CLIENT(gconf),
+ key,
+ gtk_spin_button_get_value_as_int
+ (GTK_SPIN_BUTTON(widget)), NULL);
+}
+
+static GtkWidget *new_spin_button(GConfClient * gconf,
+ const char *key,
+ int low, int high, int step)
+{
+ GtkWidget *w;
+ gint currentValue;
+ GError *error = NULL;
+
+ w = gtk_spin_button_new_with_range(low, high, step);
+ g_object_set_data_full(G_OBJECT(w), GCONF_OBJECT_KEY,
+ g_strdup(key), g_free);
+
+ gtk_spin_button_set_digits(GTK_SPIN_BUTTON(w), 0);
+
+ currentValue = gconf_client_get_int(gconf, key, &error);
+
+ if (error != NULL) {
+ g_error_free(error);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), 9091);
+ } else {
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), currentValue);
+ }
+
+ g_signal_connect(w, "value-changed", G_CALLBACK(spun_cb_int), gconf);
+
+ return w;
+}
+
+static void entry_changed_cb(GtkEntry * w, gpointer gconf)
+{
+ const char *key, *value;
+
+ key = g_object_get_data(G_OBJECT(w), GCONF_OBJECT_KEY);
+ value = gtk_entry_get_text(w);
+
+ gconf_client_set_string(GCONF_CLIENT(gconf), key, value, NULL);
+}
+
+static GtkWidget *new_entry(GConfClient * gconf, const char *key)
+{
+ GtkWidget *w;
+ const char *value;
+
+ w = gtk_entry_new();
+ value = gconf_client_get_string(gconf, key, NULL);
+
+ if (value != NULL) {
+ gtk_entry_set_text(GTK_ENTRY(w), value);
+ g_free((gpointer) value);
+ }
+
+ g_object_set_data_full(G_OBJECT(w), GCONF_OBJECT_KEY,
+ g_strdup(key), g_free);
+
+ g_signal_connect(w, "changed", G_CALLBACK(entry_changed_cb), gconf);
+ return w;
+}
+
+static GtkWidget *trg_prefs_serverPage(GConfClient * gconf)
+{
+ GtkWidget *w, *t;
+ gint row = 0;
+
+ t = hig_workarea_create();
+
+ hig_workarea_add_section_title(t, &row, "Server");
+
+ w = new_entry(gconf, TRG_GCONF_KEY_HOSTNAME);
+ hig_workarea_add_row(t, &row, "Host:", w, NULL);
+
+ w = new_spin_button(gconf, TRG_GCONF_KEY_PORT, 1, 65535, 1);
+ hig_workarea_add_row(t, &row, "Port:", w, NULL);
+
+ w = new_check_button(gconf, "Automatically connect",
+ TRG_GCONF_KEY_AUTO_CONNECT);
+ hig_workarea_add_wide_control(t, &row, w);
+
+ hig_workarea_add_section_divider(t, &row);
+ hig_workarea_add_section_title(t, &row, "Authentication");
+
+ w = new_entry(gconf, TRG_GCONF_KEY_USERNAME);
+ hig_workarea_add_row(t, &row, "Username:", w, NULL);
+
+ w = new_entry(gconf, TRG_GCONF_KEY_PASSWORD);
+ gtk_entry_set_visibility(GTK_ENTRY(w), FALSE);
+ hig_workarea_add_row(t, &row, "Password:", w, NULL);
+
+ return t;
+}
+
+static GObject *trg_preferences_dialog_constructor(GType type,
+ guint
+ n_construct_properties,
+ GObjectConstructParam *
+ construct_params)
+{
+ GObject *object;
+ TrgPreferencesDialogPrivate *priv;
+ GtkWidget *notebook;
+
+ object =
+ G_OBJECT_CLASS
+ (trg_preferences_dialog_parent_class)->constructor(type,
+ n_construct_properties,
+ construct_params);
+ priv = TRG_PREFERENCES_DIALOG_GET_PRIVATE(object);
+
+ gtk_window_set_transient_for(GTK_WINDOW(object), priv->parent);
+ gtk_window_set_destroy_with_parent(GTK_WINDOW(object), TRUE);
+ gtk_dialog_add_button(GTK_DIALOG(object), GTK_STOCK_CLOSE,
+ GTK_RESPONSE_CLOSE);
+ gtk_window_set_title(GTK_WINDOW(object), "Local Preferences");
+ gtk_container_set_border_width(GTK_CONTAINER(object), GUI_PAD);
+
+ g_signal_connect(G_OBJECT(object),
+ "response",
+ G_CALLBACK(trg_preferences_response_cb), NULL);
+
+ notebook = gtk_notebook_new();
+
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
+ trg_prefs_serverPage(priv->gconf),
+ gtk_label_new("Connection"));
+
+ gtk_container_set_border_width(GTK_CONTAINER(notebook), GUI_PAD);
+
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(object)->vbox), notebook,
+ TRUE, TRUE, 0);
+
+ gtk_widget_show_all(GTK_DIALOG(object)->vbox);
+
+ return object;
+}
+
+static void
+trg_preferences_dialog_class_init(TrgPreferencesDialogClass * class)
+{
+ GObjectClass *g_object_class = (GObjectClass *) class;
+
+ g_object_class->constructor = trg_preferences_dialog_constructor;
+ g_object_class->set_property = trg_preferences_dialog_set_property;
+ g_object_class->get_property = trg_preferences_dialog_get_property;
+
+ g_object_class_install_property(g_object_class,
+ PROP_GCONF_CLIENT,
+ g_param_spec_object("gconf-client",
+ "GConf Client",
+ "GConf Client",
+ GCONF_TYPE_CLIENT,
+ G_PARAM_READWRITE
+ |
+ G_PARAM_CONSTRUCT_ONLY
+ |
+ G_PARAM_STATIC_NAME
+ |
+ G_PARAM_STATIC_NICK
+ |
+ G_PARAM_STATIC_BLURB));
+
+ g_object_class_install_property(g_object_class,
+ PROP_PARENT_WINDOW,
+ g_param_spec_object
+ ("parent-window", "Parent window",
+ "Parent window", GTK_TYPE_WINDOW,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+
+ g_type_class_add_private(g_object_class,
+ sizeof(TrgPreferencesDialogPrivate));
+}
+
+static void trg_preferences_dialog_init(TrgPreferencesDialog * pref_dlg)
+{
+ pref_dlg->priv = TRG_PREFERENCES_DIALOG_GET_PRIVATE(pref_dlg);
+
+ pref_dlg->priv->gconf = NULL;
+}
+
+GtkWidget *trg_preferences_dialog_get_instance(GtkWindow * parent,
+ GConfClient * client)
+{
+ if (instance == NULL) {
+ instance = g_object_new(TRG_TYPE_PREFERENCES_DIALOG,
+ "parent-window", parent,
+ "gconf-client", client, NULL);
+ }
+
+ return GTK_WIDGET(instance);
+}
diff --git a/src/trg-preferences-dialog.h b/src/trg-preferences-dialog.h
new file mode 100644
index 0000000..1ddacb2
--- /dev/null
+++ b/src/trg-preferences-dialog.h
@@ -0,0 +1,56 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TRG_PREFERENCES_WINDOW_H_
+#define TRG_PREFERENCES_WINDOW_H_
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <gconf/gconf-client.h>
+
+G_BEGIN_DECLS
+
+typedef struct _TrgPreferencesDialog TrgPreferencesDialog;
+typedef struct _TrgPreferencesDialogClass TrgPreferencesDialogClass;
+typedef struct _TrgPreferencesDialogPrivate TrgPreferencesDialogPrivate;
+
+#define TRG_TYPE_PREFERENCES_DIALOG (trg_preferences_dialog_get_type ())
+#define TRG_PREFERENCES_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TRG_TYPE_PREFERENCES_DIALOG, TrgPreferencesDialog))
+#define TRG_PREFERENCES_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TRG_TYPE_PREFERENCES_DIALOG, TrgPreferencesDialogClass))
+#define TRG_IS_PREFERENCES_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TRG_TYPE_PREFERENCES_DIALOG))
+#define TRG_IS_PREFERENCES_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TRG_TYPE_PREFERENCES_DIALOG))
+#define TRG_PREFERENCES_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TRG_TYPE_PREFERENCES_DIALOG, TrgPreferencesDialogClass))
+
+struct _TrgPreferencesDialog {
+ GtkDialog dialog;
+ TrgPreferencesDialogPrivate *priv;
+};
+
+struct _TrgPreferencesDialogClass {
+ GtkDialogClass parent_class;
+};
+
+GType trg_preferences_dialog_get_type(void);
+
+GtkWidget * trg_preferences_dialog_get_instance(GtkWindow * parent,
+ GConfClient * client);
+G_END_DECLS
+
+#endif /* TRG_PREFERENCES_WINDOW_H_ */
diff --git a/src/trg-preferences.h b/src/trg-preferences.h
new file mode 100644
index 0000000..8a2db59
--- /dev/null
+++ b/src/trg-preferences.h
@@ -0,0 +1,32 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TRG_PREFERENCES_H_
+#define TRG_PREFERENCES_H_
+
+#define TRG_GCONF_KEY_HOSTNAME "/apps/trg/hostname"
+#define TRG_GCONF_KEY_PORT "/apps/trg/port"
+#define TRG_GCONF_KEY_USERNAME "/apps/trg/username"
+#define TRG_GCONF_KEY_PASSWORD "/apps/trg/password"
+#define TRG_GCONF_KEY_AUTO_CONNECT "/apps/trg/auto-connect"
+#define TRG_GCONF_KEY_WINDOW_WIDTH "/schemas/apps/trg/window_width"
+#define TRG_GCONF_KEY_WINDOW_HEIGHT "/schemas/apps/trg/window_height"
+
+
+#endif /* TRG_PREFERENCES_H_ */
diff --git a/src/trg-remote-prefs-dialog.c b/src/trg-remote-prefs-dialog.c
new file mode 100644
index 0000000..dbd6068
--- /dev/null
+++ b/src/trg-remote-prefs-dialog.c
@@ -0,0 +1,541 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+
+#include "trg-main-window.h"
+#include "trg-remote-prefs-dialog.h"
+#include "hig.h"
+#include "dispatch.h"
+#include "requests.h"
+#include "json.h"
+#include "trg-json-widgets.h"
+#include "session-get.h"
+
+G_DEFINE_TYPE(TrgRemotePrefsDialog, trg_remote_prefs_dialog,
+ GTK_TYPE_DIALOG)
+#define TRG_REMOTE_PREFS_DIALOG_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_REMOTE_PREFS_DIALOG, TrgRemotePrefsDialogPrivate))
+enum {
+ PROP_0,
+ PROP_PARENT,
+ PROP_CLIENT
+};
+
+typedef struct _TrgRemotePrefsDialogPrivate TrgRemotePrefsDialogPrivate;
+
+struct _TrgRemotePrefsDialogPrivate {
+ trg_client *client;
+ TrgMainWindow *parent;
+
+ GtkWidget *done_script_entry;
+ GtkWidget *done_script_enabled_check;
+ GtkWidget *pex_enabled_check;
+ GtkWidget *lpd_enabled_check;
+ GtkWidget *download_dir_entry;
+ GtkWidget *peer_port_random_check;
+ GtkWidget *peer_port_spin;
+ GtkWidget *peer_limit_global_spin;
+ GtkWidget *peer_limit_per_torrent_spin;
+ GtkWidget *peer_port_forwarding_check;
+ GtkWidget *blocklist_url_entry;
+ GtkWidget *blocklist_check;
+ GtkWidget *rename_partial_files_check;
+ GtkWidget *encryption_combo;
+ GtkWidget *incomplete_dir_entry;
+ GtkWidget *incomplete_dir_check;
+ GtkWidget *seed_ratio_limit_check;
+ GtkWidget *seed_ratio_limit_spin;
+ GtkWidget *cache_size_mb_spin;
+ GtkWidget *start_added_torrent_check;
+ GtkWidget *trash_original_torrent_files_check;
+ GtkWidget *speed_limit_down_check;
+ GtkWidget *speed_limit_down_spin;
+ GtkWidget *speed_limit_up_check;
+ GtkWidget *speed_limit_up_spin;
+};
+
+static GObject *instance = NULL;
+
+static void update_session(GtkDialog * dlg)
+{
+ TrgRemotePrefsDialogPrivate *priv =
+ TRG_REMOTE_PREFS_DIALOG_GET_PRIVATE(dlg);
+ JsonNode *request = session_set();
+ JsonObject *args = node_get_arguments(request);
+ gchar *encryption;
+
+ /* General */
+
+ gtk_entry_json_output(GTK_ENTRY(priv->download_dir_entry), args);
+ gtk_entry_json_output(GTK_ENTRY(priv->incomplete_dir_entry), args);
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->done_script_enabled_check), args);
+ gtk_entry_json_output(GTK_ENTRY(priv->done_script_entry), args);
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->rename_partial_files_check), args);
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->incomplete_dir_check), args);
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->trash_original_torrent_files_check),
+ args);
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->start_added_torrent_check), args);
+ gtk_spin_button_json_int_out(GTK_SPIN_BUTTON(priv->cache_size_mb_spin),
+ args);
+
+ /* Connection */
+
+ encryption =
+ g_ascii_strdown(gtk_combo_box_get_active_text
+ (GTK_COMBO_BOX(priv->encryption_combo)), -1);
+ json_object_set_string_member(args, SGET_ENCRYPTION, encryption);
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->peer_port_random_check), args);
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->peer_port_forwarding_check), args);
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->pex_enabled_check), args);
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->lpd_enabled_check), args);
+ gtk_spin_button_json_int_out(GTK_SPIN_BUTTON(priv->peer_port_spin),
+ args);
+
+ /* Limits */
+
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->seed_ratio_limit_check), args);
+ gtk_spin_button_json_double_out(GTK_SPIN_BUTTON
+ (priv->seed_ratio_limit_spin), args);
+
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->speed_limit_down_check), args);
+ gtk_spin_button_json_int_out(GTK_SPIN_BUTTON
+ (priv->speed_limit_down_spin), args);
+
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->speed_limit_up_check), args);
+ gtk_spin_button_json_int_out(GTK_SPIN_BUTTON
+ (priv->speed_limit_up_spin), args);
+
+ gtk_spin_button_json_int_out(GTK_SPIN_BUTTON
+ (priv->peer_limit_global_spin), args);
+ gtk_spin_button_json_int_out(GTK_SPIN_BUTTON
+ (priv->peer_limit_per_torrent_spin),
+ args);
+
+ g_free(encryption);
+
+ dispatch_async(priv->client, request, on_session_set, priv->parent);
+}
+
+static void
+trg_remote_prefs_response_cb(GtkDialog * dlg, gint res_id, gpointer data)
+{
+ if (res_id == GTK_RESPONSE_ACCEPT) {
+ update_session(dlg);
+ }
+ gtk_widget_destroy(GTK_WIDGET(dlg));
+ instance = NULL;
+}
+
+static void
+trg_remote_prefs_dialog_get_property(GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ TrgRemotePrefsDialogPrivate *priv =
+ TRG_REMOTE_PREFS_DIALOG_GET_PRIVATE(object);
+ switch (property_id) {
+ case PROP_PARENT:
+ g_value_set_object(value, priv->parent);
+ break;
+ case PROP_CLIENT:
+ g_value_set_pointer(value, priv->client);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_remote_prefs_dialog_set_property(GObject * object, guint property_id,
+ const GValue * value,
+ GParamSpec * pspec)
+{
+ TrgRemotePrefsDialogPrivate *priv =
+ TRG_REMOTE_PREFS_DIALOG_GET_PRIVATE(object);
+ switch (property_id) {
+ case PROP_PARENT:
+ priv->parent = g_value_get_object(value);
+ break;
+ case PROP_CLIENT:
+ priv->client = g_value_get_pointer(value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static GtkWidget *trg_rprefs_limitsPage(TrgRemotePrefsDialog * win,
+ JsonObject * json)
+{
+ TrgRemotePrefsDialogPrivate *priv =
+ TRG_REMOTE_PREFS_DIALOG_GET_PRIVATE(win);
+ GtkWidget *w, *tb, *t;
+ gint row = 0;
+
+ t = hig_workarea_create();
+
+ hig_workarea_add_section_title(t, &row, "Bandwidth");
+
+ tb = priv->speed_limit_down_check = gtk_check_button_new_with_mnemonic
+ ("Limit download speed (KB/s)");
+ widget_set_json_key(tb, SGET_SPEED_LIMIT_DOWN_ENABLED);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb),
+ session_get_speed_limit_down_enabled
+ (json));
+
+ w = priv->speed_limit_down_spin =
+ gtk_spin_button_new_with_range(0, INT_MAX, 5);
+ widget_set_json_key(w, SGET_SPEED_LIMIT_DOWN);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w),
+ session_get_speed_limit_down(json));
+ g_signal_connect(G_OBJECT(tb), "toggled",
+ G_CALLBACK(toggle_active_arg_is_sensitive), w);
+ gtk_widget_set_sensitive(w,
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
+ (tb)));
+ hig_workarea_add_row_w(t, &row, tb, w, NULL);
+
+ tb = priv->speed_limit_up_check = gtk_check_button_new_with_mnemonic
+ ("Limit upload speed (KB/s)");
+ widget_set_json_key(tb, SGET_SPEED_LIMIT_UP_ENABLED);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb),
+ session_get_speed_limit_up_enabled(json));
+
+ w = priv->speed_limit_up_spin =
+ gtk_spin_button_new_with_range(0, INT_MAX, 5);
+ widget_set_json_key(w, SGET_SPEED_LIMIT_UP);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w),
+ session_get_speed_limit_up(json));
+ g_signal_connect(G_OBJECT(tb), "toggled",
+ G_CALLBACK(toggle_active_arg_is_sensitive), w);
+ gtk_widget_set_sensitive(w,
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
+ (tb)));
+ hig_workarea_add_row_w(t, &row, tb, w, NULL);
+
+ hig_workarea_add_section_title(t, &row, "Seeding");
+
+ tb = priv->seed_ratio_limit_check = gtk_check_button_new_with_mnemonic
+ ("Seed ratio limit");
+ widget_set_json_key(tb, SGET_SEED_RATIO_LIMITED);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb),
+ session_get_seed_ratio_limited(json));
+
+ w = priv->seed_ratio_limit_spin =
+ gtk_spin_button_new_with_range(0, INT_MAX, 0.1);
+ widget_set_json_key(w, SGET_SEED_RATIO_LIMIT);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w),
+ session_get_seed_ratio_limit(json));
+ gtk_widget_set_sensitive(w,
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
+ (tb)));
+ g_signal_connect(G_OBJECT(tb), "toggled",
+ G_CALLBACK(toggle_active_arg_is_sensitive), w);
+ hig_workarea_add_row_w(t, &row, tb, w, NULL);
+
+ hig_workarea_add_section_title(t, &row, "Peers");
+
+ w = priv->peer_limit_global_spin =
+ gtk_spin_button_new_with_range(0, INT_MAX, 5);
+ widget_set_json_key(GTK_WIDGET(w), SGET_PEER_LIMIT_GLOBAL);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w),
+ session_get_peer_limit_global(json));
+ hig_workarea_add_row(t, &row, "Global peer limit", w, w);
+
+ w = priv->peer_limit_per_torrent_spin =
+ gtk_spin_button_new_with_range(0, INT_MAX, 5);
+ widget_set_json_key(GTK_WIDGET(w), SGET_PEER_LIMIT_PER_TORRENT);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w),
+ session_get_peer_limit_per_torrent(json));
+ hig_workarea_add_row(t, &row, "Per torrent peer limit", w, w);
+
+ return t;
+}
+
+static GtkWidget *trg_rprefs_connPage(TrgRemotePrefsDialog * win,
+ JsonObject * s)
+{
+ TrgRemotePrefsDialogPrivate *priv =
+ TRG_REMOTE_PREFS_DIALOG_GET_PRIVATE(win);
+
+ GtkWidget *w, *t;
+ const gchar *encryption;
+ gint row = 0;
+
+ t = hig_workarea_create();
+
+ w = priv->encryption_combo = gtk_combo_box_new_text();
+ widget_set_json_key(GTK_WIDGET(w), SGET_ENCRYPTION);
+ gtk_combo_box_append_text(GTK_COMBO_BOX(w), "Required");
+ gtk_combo_box_append_text(GTK_COMBO_BOX(w), "Preferred");
+ gtk_combo_box_append_text(GTK_COMBO_BOX(w), "Tolerated");
+ encryption = session_get_encryption(s);
+ if (g_strcmp0(encryption, "required") == 0) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(w), 0);
+ } else if (g_strcmp0(encryption, "tolerated") == 0) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(w), 2);
+ } else {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(w), 1);
+ }
+ hig_workarea_add_row(t, &row, "Encryption", w, NULL);
+
+ w = priv->peer_port_spin = gtk_spin_button_new_with_range(0, 65535, 1);
+ widget_set_json_key(w, SGET_PEER_PORT);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w),
+ session_get_peer_port(s));
+ hig_workarea_add_row(t, &row, "Peer port", w, w);
+
+ w = priv->peer_port_random_check =
+ hig_workarea_add_wide_checkbutton(t, &row,
+ "Random peer port on start",
+ session_get_peer_port_random(s));
+ widget_set_json_key(w, SGET_PEER_PORT_RANDOM_ON_START);
+
+ w = priv->peer_port_forwarding_check =
+ hig_workarea_add_wide_checkbutton(t, &row,
+ "Peer port forwarding",
+ session_get_port_forwarding_enabled
+ (s));
+ widget_set_json_key(w, SGET_PORT_FORWARDING_ENABLED);
+
+ w = priv->pex_enabled_check =
+ hig_workarea_add_wide_checkbutton(t, &row,
+ "Peer exchange (PEX)",
+ session_get_pex_enabled(s));
+ widget_set_json_key(w, SGET_PEX_ENABLED);
+
+ w = priv->lpd_enabled_check =
+ hig_workarea_add_wide_checkbutton(t, &row,
+ "Local peer discovery",
+ session_get_lpd_enabled(s));
+ widget_set_json_key(w, SGET_LPD_ENABLED);
+
+ return t;
+}
+
+static GtkWidget *trg_rprefs_generalPage(TrgRemotePrefsDialog * win,
+ JsonObject * s)
+{
+ TrgRemotePrefsDialogPrivate *priv =
+ TRG_REMOTE_PREFS_DIALOG_GET_PRIVATE(win);
+
+ GtkWidget *w, *tb, *t;
+ gint row = 0;
+
+ t = hig_workarea_create();
+
+ w = priv->download_dir_entry = gtk_entry_new();
+ widget_set_json_key(w, SGET_DOWNLOAD_DIR);
+ gtk_entry_set_text(GTK_ENTRY(w), session_get_download_dir(s));
+ hig_workarea_add_row(t, &row, "Download directory", w, NULL);
+
+ tb = priv->incomplete_dir_check =
+ gtk_check_button_new_with_mnemonic("Incomplete download dir");
+ widget_set_json_key(tb, SGET_INCOMPLETE_DIR_ENABLED);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb),
+ session_get_incomplete_dir_enabled(s));
+
+ w = priv->incomplete_dir_entry = gtk_entry_new();
+ widget_set_json_key(w, SGET_INCOMPLETE_DIR);
+ gtk_entry_set_text(GTK_ENTRY(w), session_get_incomplete_dir(s));
+ gtk_widget_set_sensitive(w,
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
+ (tb)));
+ g_signal_connect(G_OBJECT(tb), "toggled",
+ G_CALLBACK(toggle_active_arg_is_sensitive), w);
+ hig_workarea_add_row_w(t, &row, tb, w, NULL);
+
+ tb = priv->done_script_enabled_check =
+ gtk_check_button_new_with_mnemonic("Torrent done script");
+ widget_set_json_key(tb, SGET_SCRIPT_TORRENT_DONE_ENABLED);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb),
+ session_get_torrent_done_enabled(s));
+
+ w = priv->done_script_entry = gtk_entry_new();
+ widget_set_json_key(w, SGET_SCRIPT_TORRENT_DONE_FILENAME);
+ gtk_entry_set_text(GTK_ENTRY(w), session_get_torrent_done_filename(s));
+ gtk_widget_set_sensitive(w,
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
+ (tb)));
+ g_signal_connect(G_OBJECT(tb), "toggled",
+ G_CALLBACK(toggle_active_arg_is_sensitive), w);
+ hig_workarea_add_row_w(t, &row, tb, w, NULL);
+
+ w = priv->cache_size_mb_spin =
+ gtk_spin_button_new_with_range(0, INT_MAX, 1);
+ widget_set_json_key(w, SGET_CACHE_SIZE_MB);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w),
+ session_get_cache_size_mb(s));
+ hig_workarea_add_row(t, &row, "Cache size (MB)", w, w);
+
+ w = priv->rename_partial_files_check =
+ hig_workarea_add_wide_checkbutton(t, &row,
+ "Rename partial files",
+ session_get_rename_partial_files
+ (s));
+ widget_set_json_key(w, SGET_RENAME_PARTIAL_FILES);
+
+ w = priv->trash_original_torrent_files_check =
+ hig_workarea_add_wide_checkbutton(t, &row,
+ "Trash original torrent files",
+ session_get_trash_original_torrent_files
+ (s));
+ widget_set_json_key(w, SGET_TRASH_ORIGINAL_TORRENT_FILES);
+
+ w = priv->start_added_torrent_check =
+ hig_workarea_add_wide_checkbutton(t, &row,
+ "Start added torrents",
+ session_get_start_added_torrents
+ (s));
+ widget_set_json_key(w, SGET_START_ADDED_TORRENTS);
+
+ return t;
+}
+
+static GObject *trg_remote_prefs_dialog_constructor(GType type,
+ guint
+ n_construct_properties,
+ GObjectConstructParam
+ * construct_params)
+{
+ GObject *object;
+ TrgRemotePrefsDialogPrivate *priv;
+ JsonObject *session;
+ GtkWidget *notebook;
+
+ object = G_OBJECT_CLASS
+ (trg_remote_prefs_dialog_parent_class)->constructor(type,
+ n_construct_properties,
+ construct_params);
+ priv = TRG_REMOTE_PREFS_DIALOG_GET_PRIVATE(object);
+ session = priv->client->session;
+
+ gtk_window_set_title(GTK_WINDOW(object), "Remote Preferences");
+ gtk_window_set_transient_for(GTK_WINDOW(object),
+ GTK_WINDOW(priv->parent));
+ gtk_window_set_destroy_with_parent(GTK_WINDOW(object), TRUE);
+
+ gtk_dialog_add_button(GTK_DIALOG(object), GTK_STOCK_CLOSE,
+ GTK_RESPONSE_CLOSE);
+ gtk_dialog_add_button(GTK_DIALOG(object), GTK_STOCK_APPLY,
+ GTK_RESPONSE_ACCEPT);
+
+ gtk_container_set_border_width(GTK_CONTAINER(object), GUI_PAD);
+
+ gtk_dialog_set_default_response(GTK_DIALOG(object),
+ GTK_RESPONSE_CLOSE);
+
+ gtk_dialog_set_alternative_button_order(GTK_DIALOG(object),
+ GTK_RESPONSE_ACCEPT,
+ GTK_RESPONSE_CLOSE, -1);
+
+ g_signal_connect(G_OBJECT(object),
+ "response",
+ G_CALLBACK(trg_remote_prefs_response_cb), NULL);
+
+ notebook = gtk_notebook_new();
+
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
+ trg_rprefs_generalPage
+ (TRG_REMOTE_PREFS_DIALOG(object),
+ session), gtk_label_new("General"));
+
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
+ trg_rprefs_connPage
+ (TRG_REMOTE_PREFS_DIALOG(object),
+ session), gtk_label_new("Connections"));
+
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
+ trg_rprefs_limitsPage
+ (TRG_REMOTE_PREFS_DIALOG(object),
+ session), gtk_label_new("Limits"));
+
+ gtk_container_set_border_width(GTK_CONTAINER(notebook), GUI_PAD);
+
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(object)->vbox), notebook,
+ TRUE, TRUE, 0);
+
+ return object;
+}
+
+static void
+trg_remote_prefs_dialog_class_init(TrgRemotePrefsDialogClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ g_type_class_add_private(klass, sizeof(TrgRemotePrefsDialogPrivate));
+
+ object_class->constructor = trg_remote_prefs_dialog_constructor;
+ object_class->get_property = trg_remote_prefs_dialog_get_property;
+ object_class->set_property = trg_remote_prefs_dialog_set_property;
+
+ g_object_class_install_property(object_class,
+ PROP_CLIENT,
+ g_param_spec_pointer
+ ("trg-client", "TClient",
+ "Client",
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+
+ g_object_class_install_property(object_class,
+ PROP_PARENT,
+ g_param_spec_object
+ ("parent-window", "Parent window",
+ "Parent window",
+ TRG_TYPE_MAIN_WINDOW,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+}
+
+static void trg_remote_prefs_dialog_init(TrgRemotePrefsDialog * self)
+{
+}
+
+TrgRemotePrefsDialog *trg_remote_prefs_dialog_get_instance(TrgMainWindow *
+ parent,
+ trg_client *
+ client)
+{
+ if (instance == NULL) {
+ instance = g_object_new(TRG_TYPE_REMOTE_PREFS_DIALOG,
+ "parent-window", parent,
+ "trg-client", client, NULL);
+ }
+
+ return TRG_REMOTE_PREFS_DIALOG(instance);
+}
diff --git a/src/trg-remote-prefs-dialog.h b/src/trg-remote-prefs-dialog.h
new file mode 100644
index 0000000..661ca24
--- /dev/null
+++ b/src/trg-remote-prefs-dialog.h
@@ -0,0 +1,57 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef TRG_REMOTE_PREFS_DIALOG_H_
+#define TRG_REMOTE_PREFS_DIALOG_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "trg-main-window.h"
+
+G_BEGIN_DECLS
+#define TRG_TYPE_REMOTE_PREFS_DIALOG trg_remote_prefs_dialog_get_type()
+#define TRG_REMOTE_PREFS_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_REMOTE_PREFS_DIALOG, TrgRemotePrefsDialog))
+#define TRG_REMOTE_PREFS_DIALOG_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_REMOTE_PREFS_DIALOG, TrgRemotePrefsDialogClass))
+#define TRG_IS_REMOTE_PREFS_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_REMOTE_PREFS_DIALOG))
+#define TRG_IS_REMOTE_PREFS_DIALOG_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_REMOTE_PREFS_DIALOG))
+#define TRG_REMOTE_PREFS_DIALOG_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_REMOTE_PREFS_DIALOG, TrgRemotePrefsDialogClass))
+ typedef struct {
+ GtkDialog parent;
+} TrgRemotePrefsDialog;
+
+typedef struct {
+ GtkDialogClass parent_class;
+} TrgRemotePrefsDialogClass;
+
+GType trg_remote_prefs_dialog_get_type(void);
+
+TrgRemotePrefsDialog *trg_remote_prefs_dialog_get_instance(TrgMainWindow *
+ parent,
+ trg_client *
+ client);
+
+G_END_DECLS
+#endif /* TRG_REMOTE_PREFS_DIALOG_H_ */
diff --git a/src/trg-state-selector.c b/src/trg-state-selector.c
new file mode 100644
index 0000000..325b363
--- /dev/null
+++ b/src/trg-state-selector.c
@@ -0,0 +1,161 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "torrent.h"
+#include "trg-state-selector.h"
+
+enum {
+ SELECTOR_STATE_CHANGED,
+ SELECTOR_SIGNAL_COUNT
+};
+
+static guint signals[SELECTOR_SIGNAL_COUNT] = { 0 };
+
+G_DEFINE_TYPE(TrgStateSelector, trg_state_selector, GTK_TYPE_TREE_VIEW)
+#define TRG_STATE_SELECTOR_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_STATE_SELECTOR, TrgStateSelectorPrivate))
+typedef struct _TrgStateSelectorPrivate TrgStateSelectorPrivate;
+
+struct _TrgStateSelectorPrivate {
+ guint flag;
+};
+
+guint32 trg_state_selector_get_flag(TrgStateSelector * s)
+{
+ TrgStateSelectorPrivate *priv = TRG_STATE_SELECTOR_GET_PRIVATE(s);
+ return priv->flag;
+}
+
+static void trg_state_selector_class_init(TrgStateSelectorClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ signals[SELECTOR_STATE_CHANGED] =
+ g_signal_new("torrent-state-changed",
+ G_TYPE_FROM_CLASS(object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET(TrgStateSelectorClass,
+ torrent_state_changed), NULL,
+ NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE,
+ 1, G_TYPE_UINT);
+
+ g_type_class_add_private(klass, sizeof(TrgStateSelectorPrivate));
+}
+
+static void state_selection_changed(GtkTreeSelection * selection,
+ gpointer data)
+{
+ TrgStateSelectorPrivate *priv;
+ GtkTreeIter iter;
+ GtkTreeView *tv;
+ GtkTreeModel *stateModel;
+
+ priv = TRG_STATE_SELECTOR_GET_PRIVATE(data);
+
+ tv = gtk_tree_selection_get_tree_view(selection);
+ stateModel = gtk_tree_view_get_model(tv);
+
+ if (gtk_tree_selection_get_selected(selection, &stateModel, &iter))
+ gtk_tree_model_get(stateModel, &iter, STATE_SELECTOR_BIT,
+ &(priv->flag), -1);
+ else
+ priv->flag = 0;
+
+ g_signal_emit(TRG_STATE_SELECTOR(data),
+ signals[SELECTOR_STATE_CHANGED], 0, priv->flag);
+}
+
+static void trg_state_selector_add_state(GtkListStore * model,
+ GtkTreeIter * iter, gchar * icon,
+ gchar * name, guint32 flag)
+{
+ gtk_list_store_append(model, iter);
+ gtk_list_store_set(model, iter,
+ STATE_SELECTOR_ICON, icon,
+ STATE_SELECTOR_NAME, name,
+ STATE_SELECTOR_BIT, flag, -1);
+}
+
+static void trg_state_selector_init(TrgStateSelector * self)
+{
+ TrgStateSelectorPrivate *priv;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+
+ priv = TRG_STATE_SELECTOR_GET_PRIVATE(self);
+ priv->flag = 0;
+
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(self), FALSE);
+
+ column = gtk_tree_view_column_new();
+
+ renderer = gtk_cell_renderer_pixbuf_new();
+ gtk_tree_view_column_pack_start(column, renderer, FALSE);
+ g_object_set(renderer, "stock-size", 4, NULL);
+ gtk_tree_view_column_set_attributes(column, renderer, "stock-id",
+ 0, NULL);
+
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_column_pack_start(column, renderer, TRUE);
+ gtk_tree_view_column_set_attributes(column, renderer, "text", 1, NULL);
+
+ gtk_tree_view_append_column(GTK_TREE_VIEW(self), column);
+
+ store =
+ gtk_list_store_new(STATE_SELECTOR_COLUMNS, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_UINT);
+
+ trg_state_selector_add_state(store, &iter, GTK_STOCK_ABOUT, "All", 0);
+ trg_state_selector_add_state(store, &iter, GTK_STOCK_GO_DOWN,
+ "Downloading", TORRENT_FLAG_DOWNLOADING);
+ trg_state_selector_add_state(store, &iter, GTK_STOCK_MEDIA_PAUSE,
+ "Paused", TORRENT_FLAG_PAUSED);
+ trg_state_selector_add_state(store, &iter, GTK_STOCK_REFRESH,
+ "Checking", TORRENT_FLAG_CHECKING);
+ trg_state_selector_add_state(store, &iter, GTK_STOCK_APPLY,
+ "Complete", TORRENT_FLAG_COMPLETE);
+ trg_state_selector_add_state(store, &iter, GTK_STOCK_SELECT_ALL,
+ "Incomplete", TORRENT_FLAG_INCOMPLETE);
+ trg_state_selector_add_state(store, &iter, GTK_STOCK_GO_UP,
+ "Seeding", TORRENT_FLAG_SEEDING);
+ trg_state_selector_add_state(store, &iter,
+ GTK_STOCK_DIALOG_WARNING, "Error",
+ TORRENT_FLAG_ERROR);
+
+ gtk_tree_view_set_model(GTK_TREE_VIEW(self), GTK_TREE_MODEL(store));
+ gtk_tree_view_set_rubber_banding(GTK_TREE_VIEW(self), TRUE);
+
+ gtk_widget_set_size_request(GTK_WIDGET(self), 120, -1);
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
+
+ g_signal_connect(G_OBJECT(selection), "changed",
+ G_CALLBACK(state_selection_changed), self);
+}
+
+TrgStateSelector *trg_state_selector_new(void)
+{
+ return g_object_new(TRG_TYPE_STATE_SELECTOR, NULL);
+}
diff --git a/src/trg-state-selector.h b/src/trg-state-selector.h
new file mode 100644
index 0000000..03d1070
--- /dev/null
+++ b/src/trg-state-selector.h
@@ -0,0 +1,63 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef TRG_STATE_LIST_H_
+#define TRG_STATE_LIST_H_
+
+#include <glib-object.h>
+
+enum {
+ STATE_SELECTOR_ICON,
+ STATE_SELECTOR_NAME,
+ STATE_SELECTOR_BIT,
+ STATE_SELECTOR_COLUMNS
+};
+
+G_BEGIN_DECLS
+#define TRG_TYPE_STATE_SELECTOR trg_state_selector_get_type()
+#define TRG_STATE_SELECTOR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_STATE_SELECTOR, TrgStateSelector))
+#define TRG_STATE_SELECTOR_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_STATE_SELECTOR, TrgStateSelectorClass))
+#define TRG_IS_STATE_SELECTOR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_STATE_SELECTOR))
+#define TRG_IS_STATE_SELECTOR_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_STATE_SELECTOR))
+#define TRG_STATE_SELECTOR_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_STATE_SELECTOR, TrgStateSelectorClass))
+ typedef struct {
+ GtkTreeView parent;
+} TrgStateSelector;
+
+typedef struct {
+ GtkTreeViewClass parent_class;
+
+ /* SIGNALS */
+
+ void (*torrent_state_changed) (TrgStateSelector * selector,
+ guint flag, gpointer data);
+} TrgStateSelectorClass;
+
+GType trg_state_selector_get_type(void);
+TrgStateSelector *trg_state_selector_new(void);
+
+G_END_DECLS guint32 trg_state_selector_get_flag(TrgStateSelector * s);
+
+#endif /* TRG_STATE_LIST_H_ */
diff --git a/src/trg-status-bar.c b/src/trg-status-bar.c
new file mode 100644
index 0000000..f7c5bd5
--- /dev/null
+++ b/src/trg-status-bar.c
@@ -0,0 +1,130 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <glib/gprintf.h>
+#include <gtk/gtk.h>
+
+#include "trg-status-bar.h"
+#include "trg-torrent-model.h"
+#include "session-get.h"
+#include "util.h"
+
+G_DEFINE_TYPE(TrgStatusBar, trg_status_bar, GTK_TYPE_STATUSBAR)
+#define TRG_STATUS_BAR_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_STATUS_BAR, TrgStatusBarPrivate))
+typedef struct _TrgStatusBarPrivate TrgStatusBarPrivate;
+
+struct _TrgStatusBarPrivate {
+ guint connectionCtx;
+ guint countSpeedsCtx;
+ GtkStatusIcon *icon;
+};
+
+static void trg_status_bar_class_init(TrgStatusBarClass * klass)
+{
+ g_type_class_add_private(klass, sizeof(TrgStatusBarPrivate));
+}
+
+static void trg_status_bar_init(TrgStatusBar * self)
+{
+ TrgStatusBarPrivate *priv = TRG_STATUS_BAR_GET_PRIVATE(self);
+
+ priv->connectionCtx =
+ gtk_statusbar_get_context_id(GTK_STATUSBAR(self), "connection");
+ priv->countSpeedsCtx =
+ gtk_statusbar_get_context_id(GTK_STATUSBAR(self),
+ "counts and speeds");
+}
+
+void trg_status_bar_push_connection_msg(TrgStatusBar * sb,
+ const gchar * msg)
+{
+ TrgStatusBarPrivate *priv;
+
+ priv = TRG_STATUS_BAR_GET_PRIVATE(sb);
+
+ gtk_status_icon_set_tooltip(priv->icon, msg);
+ gtk_statusbar_pop(GTK_STATUSBAR(sb), priv->connectionCtx);
+ gtk_statusbar_push(GTK_STATUSBAR(sb), priv->connectionCtx, msg);
+}
+
+void trg_status_bar_connect(TrgStatusBar * sb, JsonObject * session)
+{
+ gchar *statusMsg;
+ float version;
+ int revision;
+
+ session_get_version(session, &version, &revision);
+ statusMsg =
+ g_strdup_printf
+ ("Connected to Transmission %g (r%d), getting torrents...",
+ version, revision);
+ g_printf("%s\n", statusMsg);
+ trg_status_bar_push_connection_msg(sb, statusMsg);
+ g_free(statusMsg);
+
+}
+
+void trg_status_bar_update(TrgStatusBar * sb,
+ TrgTorrentModelClassUpdateStats * stats)
+{
+ TrgStatusBarPrivate *priv;
+ gchar *statusBarUpdate;
+ gchar downRateTotalString[32], upRateTotalString[32];
+
+ priv = TRG_STATUS_BAR_GET_PRIVATE(sb);
+
+ trg_strlspeed(downRateTotalString,
+ stats->downRateTotal / KILOBYTE_FACTOR);
+ trg_strlspeed(upRateTotalString, stats->upRateTotal / KILOBYTE_FACTOR);
+
+ statusBarUpdate =
+ g_strdup_printf
+ ("%d torrents .. Down %s, Up %s .. %d seeding, %d downloading, %d paused",
+ stats->count,
+ downRateTotalString, upRateTotalString,
+ stats->seeding, stats->down, stats->paused);
+ gtk_statusbar_pop(GTK_STATUSBAR(sb), priv->countSpeedsCtx);
+ gtk_statusbar_push(GTK_STATUSBAR(sb),
+ priv->countSpeedsCtx, statusBarUpdate);
+ g_free(statusBarUpdate);
+
+ if (priv->icon != NULL) {
+ gchar *toolTipUpdate =
+ g_strdup_printf("%d torrents: %s down/%s up",
+ stats->count, downRateTotalString,
+ upRateTotalString);
+ gtk_status_icon_set_tooltip(priv->icon, toolTipUpdate);
+ g_free(toolTipUpdate);
+ }
+}
+
+
+TrgStatusBar *trg_status_bar_new(GtkStatusIcon * icon)
+{
+ TrgStatusBarPrivate *priv;
+ GObject *obj;
+
+ obj = g_object_new(TRG_TYPE_STATUS_BAR, NULL);
+
+ priv = TRG_STATUS_BAR_GET_PRIVATE(obj);
+ priv->icon = icon;
+
+ return TRG_STATUS_BAR(obj);
+}
diff --git a/src/trg-status-bar.h b/src/trg-status-bar.h
new file mode 100644
index 0000000..ed96505
--- /dev/null
+++ b/src/trg-status-bar.h
@@ -0,0 +1,60 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef TRG_STATUS_BAR_H_
+#define TRG_STATUS_BAR_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "trg-torrent-model.h"
+
+G_BEGIN_DECLS
+#define TRG_TYPE_STATUS_BAR trg_status_bar_get_type()
+#define TRG_STATUS_BAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_STATUS_BAR, TrgStatusBar))
+#define TRG_STATUS_BAR_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_STATUS_BAR, TrgStatusBarClass))
+#define TRG_IS_STATUS_BAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_STATUS_BAR))
+#define TRG_IS_STATUS_BAR_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_STATUS_BAR))
+#define TRG_STATUS_BAR_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_STATUS_BAR, TrgStatusBarClass))
+ typedef struct {
+ GtkStatusbar parent;
+} TrgStatusBar;
+
+typedef struct {
+ GtkStatusbarClass parent_class;
+} TrgStatusBarClass;
+
+GType trg_status_bar_get_type(void);
+
+TrgStatusBar *trg_status_bar_new(GtkStatusIcon * icon);
+
+G_END_DECLS
+ void trg_status_bar_update(TrgStatusBar * sb,
+ TrgTorrentModelClassUpdateStats * stats);
+void trg_status_bar_connect(TrgStatusBar * sb, JsonObject * session);
+void trg_status_bar_push_connection_msg(TrgStatusBar * sb,
+ const gchar * msg);
+
+#endif /* TRG_STATUS_BAR_H_ */
diff --git a/src/trg-toolbar.c b/src/trg-toolbar.c
new file mode 100644
index 0000000..672b30f
--- /dev/null
+++ b/src/trg-toolbar.c
@@ -0,0 +1,267 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <gtk/gtk.h>
+#include "trg-toolbar.h"
+
+enum {
+ PROP_0,
+ PROP_CONNECT_BUTTON,
+ PROP_DISCONNECT_BUTTON,
+ PROP_ADD_BUTTON,
+ PROP_ADD_URL_BUTTON,
+ PROP_REMOVE_BUTTON,
+ PROP_DELETE_BUTTON,
+ PROP_RESUME_BUTTON,
+ PROP_PAUSE_BUTTON,
+ /*PROP_VERIFY_BUTTON, */
+ PROP_PROPS_BUTTON,
+ PROP_REMOTE_PREFS_BUTTON,
+ PROP_LOCAL_PREFS_BUTTON
+};
+
+G_DEFINE_TYPE(TrgToolbar, trg_toolbar, GTK_TYPE_TOOLBAR)
+#define TRG_TOOLBAR_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_TOOLBAR, TrgToolbarPrivate))
+typedef struct _TrgToolbarPrivate TrgToolbarPrivate;
+
+struct _TrgToolbarPrivate {
+ GtkWidget *tb_connect;
+ GtkWidget *tb_disconnect;
+ GtkWidget *tb_add;
+ /*GtkWidget *tb_add_url; */
+ GtkWidget *tb_remove;
+ GtkWidget *tb_delete;
+ GtkWidget *tb_resume;
+ GtkWidget *tb_pause;
+ /*GtkWidget *tb_verify; */
+ GtkWidget *tb_props;
+ GtkWidget *tb_remote_prefs;
+ GtkWidget *tb_local_prefs;
+};
+
+static void
+trg_toolbar_get_property(GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ TrgToolbarPrivate *priv = TRG_TOOLBAR_GET_PRIVATE(object);
+
+ switch (property_id) {
+ case PROP_CONNECT_BUTTON:
+ g_value_set_object(value, priv->tb_connect);
+ break;
+ case PROP_DISCONNECT_BUTTON:
+ g_value_set_object(value, priv->tb_disconnect);
+ break;
+ case PROP_ADD_BUTTON:
+ g_value_set_object(value, priv->tb_add);
+ break;
+/* case PROP_ADD_URL_BUTTON:
+ g_value_set_object(value, priv->tb_add_url);
+ break;*/
+ case PROP_REMOVE_BUTTON:
+ g_value_set_object(value, priv->tb_remove);
+ break;
+ case PROP_DELETE_BUTTON:
+ g_value_set_object(value, priv->tb_delete);
+ break;
+ case PROP_RESUME_BUTTON:
+ g_value_set_object(value, priv->tb_resume);
+ break;
+ case PROP_PAUSE_BUTTON:
+ g_value_set_object(value, priv->tb_pause);
+ break;
+ /*case PROP_VERIFY_BUTTON:
+ g_value_set_object(value, priv->tb_verify);
+ break; */
+ case PROP_PROPS_BUTTON:
+ g_value_set_object(value, priv->tb_props);
+ break;
+ case PROP_REMOTE_PREFS_BUTTON:
+ g_value_set_object(value, priv->tb_remote_prefs);
+ break;
+ case PROP_LOCAL_PREFS_BUTTON:
+ g_value_set_object(value, priv->tb_local_prefs);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_toolbar_install_widget_prop(GObjectClass * class, guint propId,
+ const gchar * name, const gchar * nick)
+{
+ g_object_class_install_property(class,
+ propId,
+ g_param_spec_object(name,
+ nick,
+ nick,
+ GTK_TYPE_WIDGET,
+ G_PARAM_READABLE
+ |
+ G_PARAM_STATIC_NAME
+ |
+ G_PARAM_STATIC_NICK
+ |
+ G_PARAM_STATIC_BLURB));
+}
+
+static void trg_toolbar_class_init(TrgToolbarClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+ object_class->get_property = trg_toolbar_get_property;
+
+ trg_toolbar_install_widget_prop(object_class, PROP_CONNECT_BUTTON,
+ "connect-button", "Connect Button");
+ trg_toolbar_install_widget_prop(object_class,
+ PROP_DISCONNECT_BUTTON,
+ "disconnect-button",
+ "Disconnect Button");
+ trg_toolbar_install_widget_prop(object_class, PROP_ADD_BUTTON,
+ "add-button", "Add Button");
+ trg_toolbar_install_widget_prop(object_class, PROP_ADD_URL_BUTTON,
+ "add-url-button", "Add URL Button");
+ trg_toolbar_install_widget_prop(object_class, PROP_REMOVE_BUTTON,
+ "remove-button", "Remove Button");
+ trg_toolbar_install_widget_prop(object_class, PROP_DELETE_BUTTON,
+ "delete-button", "Delete Button");
+ trg_toolbar_install_widget_prop(object_class, PROP_RESUME_BUTTON,
+ "resume-button", "Resume Button");
+ trg_toolbar_install_widget_prop(object_class, PROP_PAUSE_BUTTON,
+ "pause-button", "Pause Button");
+ /*trg_toolbar_install_widget_prop(object_class, PROP_VERIFY_BUTTON,
+ "verify-button", "Verify Button"); */
+ trg_toolbar_install_widget_prop(object_class, PROP_PROPS_BUTTON,
+ "props-button", "Props Button");
+ trg_toolbar_install_widget_prop(object_class,
+ PROP_REMOTE_PREFS_BUTTON,
+ "remote-prefs-button",
+ "Remote Prefs Button");
+ trg_toolbar_install_widget_prop(object_class,
+ PROP_LOCAL_PREFS_BUTTON,
+ "local-prefs-button",
+ "Local Prefs Button");
+
+ g_type_class_add_private(klass, sizeof(TrgToolbarPrivate));
+}
+
+static
+GtkWidget *trg_toolbar_item_new(TrgToolbar * toolbar, gchar * text,
+ int *index, gchar * icon,
+ gboolean sensitive)
+{
+ GtkToolItem *w = gtk_tool_button_new_from_stock(icon);
+ gtk_widget_set_sensitive(GTK_WIDGET(w), sensitive);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), w, (*index)++);
+ return GTK_WIDGET(w);
+}
+
+void trg_toolbar_connected_change(TrgToolbar * tb, gboolean connected)
+{
+ TrgToolbarPrivate *priv = TRG_TOOLBAR_GET_PRIVATE(tb);
+
+ gtk_widget_set_sensitive(priv->tb_add, connected);
+ /*gtk_widget_set_sensitive(priv->tb_add_url, connected); */
+ gtk_widget_set_sensitive(priv->tb_connect, !connected);
+ gtk_widget_set_sensitive(priv->tb_disconnect, connected);
+ gtk_widget_set_sensitive(priv->tb_remote_prefs, connected);
+}
+
+void trg_toolbar_torrent_actions_sensitive(TrgToolbar * tb,
+ gboolean sensitive)
+{
+ TrgToolbarPrivate *priv = TRG_TOOLBAR_GET_PRIVATE(tb);
+
+ gtk_widget_set_sensitive(priv->tb_props, sensitive);
+ gtk_widget_set_sensitive(priv->tb_remove, sensitive);
+ gtk_widget_set_sensitive(priv->tb_delete, sensitive);
+ gtk_widget_set_sensitive(priv->tb_resume, sensitive);
+ gtk_widget_set_sensitive(priv->tb_pause, sensitive);
+ /*gtk_widget_set_sensitive(priv->tb_verify, sensitive); */
+}
+
+static void trg_toolbar_init(TrgToolbar * self)
+{
+ GtkToolItem *separator;
+ TrgToolbarPrivate *priv;
+ int position = 0;
+
+ priv = TRG_TOOLBAR_GET_PRIVATE(self);
+
+ gtk_orientable_set_orientation(GTK_ORIENTABLE(self),
+ GTK_ORIENTATION_HORIZONTAL);
+ gtk_toolbar_set_icon_size(GTK_TOOLBAR(self),
+ GTK_ICON_SIZE_LARGE_TOOLBAR);
+ gtk_toolbar_set_style(GTK_TOOLBAR(self), GTK_TOOLBAR_ICONS);
+
+ priv->tb_connect =
+ trg_toolbar_item_new(self, "Connect", &position,
+ GTK_STOCK_CONNECT, TRUE);
+ priv->tb_disconnect =
+ trg_toolbar_item_new(self, "Disconnect", &position,
+ GTK_STOCK_DISCONNECT, FALSE);
+ priv->tb_add =
+ trg_toolbar_item_new(self, "Add", &position, GTK_STOCK_ADD, FALSE);
+
+ /*priv->tb_add_url =
+ trg_toolbar_item_new(self, "Add URL", 3, GTK_STOCK_ADD, FALSE); */
+
+ separator = gtk_separator_tool_item_new();
+ gtk_toolbar_insert(GTK_TOOLBAR(self), separator, position++);
+
+ priv->tb_resume =
+ trg_toolbar_item_new(self, "Resume", &position,
+ GTK_STOCK_MEDIA_PLAY, FALSE);
+ priv->tb_pause =
+ trg_toolbar_item_new(self, "Pause", &position,
+ GTK_STOCK_MEDIA_PAUSE, FALSE);
+
+ priv->tb_props =
+ trg_toolbar_item_new(self, "Properties", &position,
+ GTK_STOCK_PROPERTIES, FALSE);
+
+ priv->tb_remove =
+ trg_toolbar_item_new(self, "Remove", &position,
+ GTK_STOCK_REMOVE, FALSE);
+
+ priv->tb_delete =
+ trg_toolbar_item_new(self, "Remove with data",
+ &position, GTK_STOCK_DELETE, FALSE);
+
+ /*priv->tb_verify =
+ trg_toolbar_item_new(self, "Verify", 11,
+ GTK_STOCK_REFRESH, FALSE); */
+
+ separator = gtk_separator_tool_item_new();
+ gtk_toolbar_insert(GTK_TOOLBAR(self), separator, position++);
+
+ priv->tb_local_prefs =
+ trg_toolbar_item_new(self, "Local Preferences", &position,
+ GTK_STOCK_PREFERENCES, TRUE);
+
+ priv->tb_remote_prefs =
+ trg_toolbar_item_new(self, "Remote Preferences",
+ &position, GTK_STOCK_NETWORK, FALSE);
+}
+
+TrgToolbar *trg_toolbar_new(void)
+{
+ return g_object_new(TRG_TYPE_TOOLBAR, NULL);
+}
diff --git a/src/trg-toolbar.h b/src/trg-toolbar.h
new file mode 100644
index 0000000..624f33e
--- /dev/null
+++ b/src/trg-toolbar.h
@@ -0,0 +1,56 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef TRG_TOOLBAR_H_
+#define TRG_TOOLBAR_H_
+
+#include <gtk/gtk.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+#define TRG_TYPE_TOOLBAR trg_toolbar_get_type()
+#define TRG_TOOLBAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_TOOLBAR, TrgToolbar))
+#define TRG_TOOLBAR_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_TOOLBAR, TrgToolbarClass))
+#define TRG_IS_TOOLBAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_TOOLBAR))
+#define TRG_IS_TOOLBAR_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_TOOLBAR))
+#define TRG_TOOLBAR_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_TOOLBAR, TrgToolbarClass))
+ typedef struct {
+ GtkToolbar parent;
+} TrgToolbar;
+
+typedef struct {
+ GtkToolbarClass parent_class;
+} TrgToolbarClass;
+
+GType trg_toolbar_get_type(void);
+
+TrgToolbar *trg_toolbar_new(void);
+
+G_END_DECLS
+ void trg_toolbar_torrent_actions_sensitive(TrgToolbar * mb,
+ gboolean sensitive);
+void trg_toolbar_connected_change(TrgToolbar * mb, gboolean connected);
+
+#endif /* TRG_TOOLBAR_H_ */
diff --git a/src/trg-torrent-add-url-dialog.c b/src/trg-torrent-add-url-dialog.c
new file mode 100644
index 0000000..b416cec
--- /dev/null
+++ b/src/trg-torrent-add-url-dialog.c
@@ -0,0 +1,129 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+
+#include "trg-client.h"
+#include "trg-main-window.h"
+#include "trg-torrent-add-url-dialog.h"
+#include "hig.h"
+#include "requests.h"
+#include "dispatch.h"
+
+G_DEFINE_TYPE(TrgTorrentAddUrlDialog, trg_torrent_add_url_dialog,
+ GTK_TYPE_DIALOG)
+#define TRG_TORRENT_ADD_URL_DIALOG_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_TORRENT_ADD_URL_DIALOG, TrgTorrentAddUrlDialogPrivate))
+typedef struct _TrgTorrentAddUrlDialogPrivate
+ TrgTorrentAddUrlDialogPrivate;
+
+struct _TrgTorrentAddUrlDialogPrivate {
+ trg_client *client;
+ TrgMainWindow *win;
+ GtkWidget *urlEntry, *startCheck, *addButton;
+};
+
+static void
+trg_torrent_add_url_dialog_class_init(TrgTorrentAddUrlDialogClass * klass)
+{
+ g_type_class_add_private(klass, sizeof(TrgTorrentAddUrlDialogPrivate));
+}
+
+static void
+trg_torrent_add_url_response_cb(GtkDialog * dlg, gint res_id,
+ gpointer data)
+{
+ TrgTorrentAddUrlDialogPrivate *priv =
+ TRG_TORRENT_ADD_URL_DIALOG_GET_PRIVATE(dlg);
+ if (res_id == GTK_RESPONSE_ACCEPT) {
+ JsonNode *request =
+ torrent_add_url(gtk_entry_get_text(GTK_ENTRY(priv->urlEntry)),
+ gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(priv->startCheck)));
+ dispatch_async(priv->client, request,
+ on_generic_interactive_action, data);
+ }
+ gtk_widget_destroy(GTK_WIDGET(dlg));
+}
+
+static void url_entry_changed(GtkWidget * w, gpointer data)
+{
+ TrgTorrentAddUrlDialogPrivate *priv =
+ TRG_TORRENT_ADD_URL_DIALOG_GET_PRIVATE(data);
+ gtk_widget_set_sensitive(priv->addButton,
+ gtk_entry_get_text_length(GTK_ENTRY(w)) > 0);
+}
+
+static void trg_torrent_add_url_dialog_init(TrgTorrentAddUrlDialog * self)
+{
+ TrgTorrentAddUrlDialogPrivate *priv =
+ TRG_TORRENT_ADD_URL_DIALOG_GET_PRIVATE(self);
+ GtkWidget *w, *t;
+ gint row = 0;
+
+ t = hig_workarea_create();
+
+ w = priv->urlEntry = gtk_entry_new();
+ g_signal_connect(w, "changed", G_CALLBACK(url_entry_changed), self);
+ w = hig_workarea_add_row(t, &row, "URL:", w, NULL);
+
+ priv->startCheck =
+ hig_workarea_add_wide_checkbutton(t, &row, "Start Paused", FALSE);
+
+ gtk_window_set_title(GTK_WINDOW(self), "Add torrent from URL");
+ gtk_window_set_destroy_with_parent(GTK_WINDOW(self), TRUE);
+
+ gtk_dialog_add_button(GTK_DIALOG(self), GTK_STOCK_CLOSE,
+ GTK_RESPONSE_CANCEL);
+ priv->addButton =
+ gtk_dialog_add_button(GTK_DIALOG(self), GTK_STOCK_ADD,
+ GTK_RESPONSE_ACCEPT);
+ gtk_widget_set_sensitive(priv->addButton, FALSE);
+
+ gtk_container_set_border_width(GTK_CONTAINER(self), GUI_PAD);
+
+ gtk_dialog_set_default_response(GTK_DIALOG(self), GTK_RESPONSE_ACCEPT);
+
+ gtk_dialog_set_alternative_button_order(GTK_DIALOG(self),
+ GTK_RESPONSE_ACCEPT,
+ GTK_RESPONSE_CANCEL, -1);
+
+ gtk_container_set_border_width(GTK_CONTAINER(t), GUI_PAD);
+
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(self)->vbox), t, TRUE, TRUE, 0);
+}
+
+TrgTorrentAddUrlDialog *trg_torrent_add_url_dialog_new(TrgMainWindow * win,
+ trg_client * client)
+{
+ GObject *obj = g_object_new(TRG_TYPE_TORRENT_ADD_URL_DIALOG, NULL);
+ TrgTorrentAddUrlDialogPrivate *priv =
+ TRG_TORRENT_ADD_URL_DIALOG_GET_PRIVATE(obj);
+
+ priv->client = client;
+ priv->win = win;
+
+ gtk_window_set_transient_for(GTK_WINDOW(obj), GTK_WINDOW(win));
+ g_signal_connect(G_OBJECT(obj),
+ "response",
+ G_CALLBACK(trg_torrent_add_url_response_cb), win);
+
+ return TRG_TORRENT_ADD_URL_DIALOG(obj);
+}
diff --git a/src/trg-torrent-add-url-dialog.h b/src/trg-torrent-add-url-dialog.h
new file mode 100644
index 0000000..b2005ef
--- /dev/null
+++ b/src/trg-torrent-add-url-dialog.h
@@ -0,0 +1,57 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef TRG_TORRENT_ADD_URL_DIALOG_H_
+#define TRG_TORRENT_ADD_URL_DIALOG_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "trg-client.h"
+#include "trg-main-window.h"
+
+G_BEGIN_DECLS
+#define TRG_TYPE_TORRENT_ADD_URL_DIALOG trg_torrent_add_url_dialog_get_type()
+#define TRG_TORRENT_ADD_URL_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_TORRENT_ADD_URL_DIALOG, TrgTorrentAddUrlDialog))
+#define TRG_TORRENT_ADD_URL_DIALOG_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_TORRENT_ADD_URL_DIALOG, TrgTorrentAddUrlDialogClass))
+#define TRG_IS_TORRENT_ADD_URL_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_TORRENT_ADD_URL_DIALOG))
+#define TRG_IS_TORRENT_ADD_URL_DIALOG_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_TORRENT_ADD_URL_DIALOG))
+#define TRG_TORRENT_ADD_URL_DIALOG_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_TORRENT_ADD_URL_DIALOG, TrgTorrentAddUrlDialogClass))
+ typedef struct {
+ GtkDialog parent;
+} TrgTorrentAddUrlDialog;
+
+typedef struct {
+ GtkDialogClass parent_class;
+} TrgTorrentAddUrlDialogClass;
+
+GType trg_torrent_add_url_dialog_get_type(void);
+
+TrgTorrentAddUrlDialog *trg_torrent_add_url_dialog_new(TrgMainWindow * win,
+ trg_client *
+ client);
+
+G_END_DECLS
+#endif /* TRG_TORRENT_ADD_URL_DIALOG_H_ */
diff --git a/src/trg-torrent-model.c b/src/trg-torrent-model.c
new file mode 100644
index 0000000..3bf8229
--- /dev/null
+++ b/src/trg-torrent-model.c
@@ -0,0 +1,297 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <string.h>
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+
+#include "torrent.h"
+#include "tpeer.h"
+#include "json.h"
+#include "trg-torrent-model.h"
+#include "protocol-constants.h"
+#include "trg-model.h"
+#include "util.h"
+
+enum {
+ TMODEL_TORRENT_COMPLETED,
+ TMODEL_SIGNAL_COUNT
+};
+
+static guint signals[TMODEL_SIGNAL_COUNT] = { 0 };
+
+G_DEFINE_TYPE(TrgTorrentModel, trg_torrent_model, GTK_TYPE_LIST_STORE)
+
+static guint32 torrent_get_flags(JsonObject * t, gint64 status,
+ TrgTorrentModelClassUpdateStats * stats);
+
+static void
+update_torrent_iter(gint64 serial, TrgTorrentModel * model,
+ GtkTreeIter * iter, JsonObject * t,
+ TrgTorrentModelClassUpdateStats * stats);
+
+static gboolean
+find_existing_torrent_item_foreachfunc(GtkTreeModel * model,
+ GtkTreePath * path,
+ GtkTreeIter * iter, gpointer data);
+
+static gboolean
+find_existing_torrent_item(TrgTorrentModel * model, JsonObject * t,
+ GtkTreeIter * iter);
+
+static void trg_torrent_model_class_init(TrgTorrentModelClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ signals[TMODEL_TORRENT_COMPLETED] =
+ g_signal_new("torrent-completed",
+ G_TYPE_FROM_CLASS(object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET(TrgTorrentModelClass,
+ torrent_completed), NULL,
+ NULL, g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+}
+
+static void trg_torrent_module_count_peers(TrgTorrentModel * model,
+ GtkTreeIter * iter,
+ JsonObject * t)
+{
+ JsonArray *peers;
+ gint seeders, leechers, j;
+
+ peers = torrent_get_peers(t);
+
+ seeders = 0;
+ leechers = 0;
+
+ for (j = 0; j < json_array_get_length(peers); j++) {
+ JsonObject *peer;
+
+ peer = json_node_get_object(json_array_get_element(peers, j));
+
+ if (peer_get_is_downloading_from(peer))
+ seeders++;
+
+ if (peer_get_is_uploading_to(peer))
+ leechers++;
+ }
+
+ gtk_list_store_set(GTK_LIST_STORE(model), iter,
+ TORRENT_COLUMN_SEEDS, seeders,
+ TORRENT_COLUMN_LEECHERS, leechers, -1);
+}
+
+static void trg_torrent_model_init(TrgTorrentModel * self)
+{
+ GType column_types[TORRENT_COLUMN_COLUMNS];
+
+ column_types[TORRENT_COLUMN_ICON] = G_TYPE_STRING;
+ column_types[TORRENT_COLUMN_NAME] = G_TYPE_STRING;
+ column_types[TORRENT_COLUMN_SIZE] = G_TYPE_INT64;
+ column_types[TORRENT_COLUMN_DONE] = G_TYPE_DOUBLE;
+ column_types[TORRENT_COLUMN_STATUS] = G_TYPE_STRING;
+ column_types[TORRENT_COLUMN_SEEDS] = G_TYPE_INT;
+ column_types[TORRENT_COLUMN_LEECHERS] = G_TYPE_INT;
+ column_types[TORRENT_COLUMN_DOWNSPEED] = G_TYPE_INT64;
+ column_types[TORRENT_COLUMN_UPSPEED] = G_TYPE_INT64;
+ column_types[TORRENT_COLUMN_ETA] = G_TYPE_INT64;
+ column_types[TORRENT_COLUMN_UPLOADED] = G_TYPE_INT64;
+ column_types[TORRENT_COLUMN_DOWNLOADED] = G_TYPE_INT64;
+ column_types[TORRENT_COLUMN_RATIO] = G_TYPE_DOUBLE;
+ column_types[TORRENT_COLUMN_ID] = G_TYPE_INT;
+ column_types[TORRENT_COLUMN_JSON] = G_TYPE_POINTER;
+ column_types[TORRENT_COLUMN_UPDATESERIAL] = G_TYPE_INT64;
+ column_types[TORRENT_COLUMN_FLAGS] = G_TYPE_INT;
+
+ gtk_list_store_set_column_types(GTK_LIST_STORE(self),
+ TORRENT_COLUMN_COLUMNS, column_types);
+}
+
+static guint32 torrent_get_flags(JsonObject * t, gint64 status,
+ TrgTorrentModelClassUpdateStats * stats)
+{
+ guint32 flags = 0;
+ switch (status) {
+ case STATUS_DOWNLOADING:
+ flags |= TORRENT_FLAG_DOWNLOADING;
+ stats->down++;
+ break;
+ case STATUS_PAUSED:
+ flags |= TORRENT_FLAG_PAUSED;
+ stats->paused++;
+ break;
+ case STATUS_SEEDING:
+ flags |= TORRENT_FLAG_SEEDING;
+ stats->seeding++;
+ break;
+ case STATUS_CHECKING:
+ flags |= TORRENT_FLAG_CHECKING;
+ break;
+ case STATUS_WAITING_TO_CHECK:
+ flags |= TORRENT_FLAG_WAITING_CHECK;
+ flags |= TORRENT_FLAG_CHECKING;
+ break;
+ }
+
+ if (torrent_get_is_finished(t) == TRUE)
+ flags |= TORRENT_FLAG_COMPLETE;
+ else
+ flags |= TORRENT_FLAG_INCOMPLETE;
+
+ if (strlen(torrent_get_errorstr(t)) > 0)
+ flags |= TORRENT_FLAG_ERROR;
+
+ return flags;
+}
+
+static void
+update_torrent_iter(gint64 serial, TrgTorrentModel * model,
+ GtkTreeIter * iter, JsonObject * t,
+ TrgTorrentModelClassUpdateStats * stats)
+{
+ guint lastFlags, newFlags;
+ gchar *statusString, *statusIcon;
+ gint64 downRate, upRate, downloaded, uploaded, id, status;
+
+ downRate = torrent_get_rate_down(t);
+ stats->downRateTotal += downRate;
+
+ upRate = torrent_get_rate_up(t);
+ stats->upRateTotal += upRate;
+
+ uploaded = torrent_get_uploaded(t);
+ downloaded = torrent_get_downloaded(t);
+
+ id = torrent_get_id(t);
+
+ status = torrent_get_status(t);
+ statusString = torrent_get_status_string(status);
+ newFlags = torrent_get_flags(t, status, stats);
+ statusIcon = torrent_get_status_icon(newFlags);
+
+ gtk_tree_model_get(GTK_TREE_MODEL(model), iter,
+ TORRENT_COLUMN_FLAGS, &lastFlags, -1);
+
+ gtk_list_store_set(GTK_LIST_STORE(model), iter,
+ TORRENT_COLUMN_ICON, statusIcon,
+ TORRENT_COLUMN_NAME, torrent_get_name(t),
+ TORRENT_COLUMN_SIZE, torrent_get_size(t),
+ TORRENT_COLUMN_DONE,
+ torrent_get_percent_done(t),
+ TORRENT_COLUMN_STATUS, statusString,
+ TORRENT_COLUMN_DOWNSPEED, downRate,
+ TORRENT_COLUMN_FLAGS, newFlags,
+ TORRENT_COLUMN_UPSPEED, upRate,
+ TORRENT_COLUMN_ETA, torrent_get_eta(t),
+ TORRENT_COLUMN_UPLOADED, uploaded,
+ TORRENT_COLUMN_DOWNLOADED, downloaded,
+ TORRENT_COLUMN_RATIO,
+ uploaded >
+ 0 ? (double) uploaded / (double) downloaded : 0,
+ TORRENT_COLUMN_ID, id, TORRENT_COLUMN_JSON, t,
+ TORRENT_COLUMN_UPDATESERIAL, serial, -1);
+
+
+ if ((lastFlags & TORRENT_FLAG_DOWNLOADING) ==
+ TORRENT_FLAG_DOWNLOADING
+ && (newFlags & TORRENT_FLAG_COMPLETE) == TORRENT_FLAG_COMPLETE)
+ g_signal_emit(model, signals[TMODEL_TORRENT_COMPLETED], 0, iter);
+
+ trg_torrent_module_count_peers(model, iter, t);
+
+ g_free(statusString);
+ g_free(statusIcon);
+}
+
+TrgTorrentModel *trg_torrent_model_new(void)
+{
+ return g_object_new(TRG_TYPE_TORRENT_MODEL, NULL);
+}
+
+static gboolean
+find_existing_torrent_item_foreachfunc(GtkTreeModel * model,
+ GtkTreePath * path,
+ GtkTreeIter * iter, gpointer data)
+{
+ struct idAndIter *ii;
+ gint currentId;
+
+ ii = (struct idAndIter *) data;
+
+ gtk_tree_model_get(model, iter, TORRENT_COLUMN_ID, &currentId, -1);
+ if (currentId == ii->id) {
+ ii->iter = iter;
+ return ii->found = TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+find_existing_torrent_item(TrgTorrentModel * model, JsonObject * t,
+ GtkTreeIter * iter)
+{
+ struct idAndIter ii;
+ ii.id = torrent_get_id(t);
+ ii.found = FALSE;
+ gtk_tree_model_foreach(GTK_TREE_MODEL(model),
+ find_existing_torrent_item_foreachfunc, &ii);
+ if (ii.found == TRUE)
+ *iter = *(ii.iter);
+ return ii.found;
+}
+
+void trg_torrent_model_update(TrgTorrentModel * model, trg_client * tc,
+ JsonObject * response,
+ TrgTorrentModelClassUpdateStats * stats,
+ gboolean first)
+{
+ int i;
+ JsonArray *newTorrents;
+
+ newTorrents = get_torrents(get_arguments(response));
+
+ stats->count = json_array_get_length(newTorrents);
+
+ for (i = 0; i < stats->count; i++) {
+ GtkTreeIter iter;
+ JsonObject *t;
+
+ t = json_array_get_object_element(newTorrents, i);
+
+ if (first == TRUE
+ || find_existing_torrent_item(model, t, &iter) == FALSE)
+ gtk_list_store_append(GTK_LIST_STORE(model), &iter);
+
+ update_torrent_iter(tc->updateSerial, model, &iter, t, stats);
+ }
+
+ json_array_ref(newTorrents);
+
+ if (tc->torrents != NULL)
+ json_array_unref(tc->torrents);
+
+ tc->torrents = newTorrents;
+
+ trg_model_remove_removed(GTK_LIST_STORE(model),
+ TORRENT_COLUMN_UPDATESERIAL,
+ tc->updateSerial);
+}
diff --git a/src/trg-torrent-model.h b/src/trg-torrent-model.h
new file mode 100644
index 0000000..1157474
--- /dev/null
+++ b/src/trg-torrent-model.h
@@ -0,0 +1,102 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef TRG_TORRENT_MODEL_H_
+#define TRG_TORRENT_MODEL_H_
+
+#include <glib-object.h>
+#include <json-glib/json-glib.h>
+
+#include "trg-client.h"
+
+G_BEGIN_DECLS
+#define TRG_TYPE_TORRENT_MODEL trg_torrent_model_get_type()
+#define TRG_TORRENT_MODEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_TORRENT_MODEL, TrgTorrentModel))
+#define TRG_TORRENT_MODEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_TORRENT_MODEL, TrgTorrentModelClass))
+#define TRG_IS_TORRENT_MODEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_TORRENT_MODEL))
+#define TRG_IS_TORRENT_MODEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_TORRENT_MODEL))
+#define TRG_TORRENT_MODEL_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_TORRENT_MODEL, TrgTorrentModelClass))
+ typedef struct {
+ GtkListStore parent;
+} TrgTorrentModel;
+
+typedef struct {
+ GtkListStoreClass parent_class;
+ void (*torrent_completed) (TrgTorrentModel * model,
+ GtkTreeIter * iter, JsonObject * t,
+ gpointer data);
+} TrgTorrentModelClass;
+
+typedef struct {
+ gint64 downRateTotal;
+ gint64 upRateTotal;
+ gint seeding;
+ gint down;
+ gint paused;
+ gint count;
+} TrgTorrentModelClassUpdateStats;
+
+GType trg_torrent_model_get_type(void);
+
+TrgTorrentModel *trg_torrent_model_new();
+
+G_END_DECLS
+ gboolean
+find_existing_peer_item(GtkListStore * model, JsonObject * p,
+ GtkTreeIter * iter);
+
+void trg_torrent_model_update(TrgTorrentModel * model, trg_client * tc,
+ JsonObject * response,
+ TrgTorrentModelClassUpdateStats * stats,
+ gboolean first);
+
+enum {
+ TORRENT_COLUMN_ICON,
+ TORRENT_COLUMN_NAME,
+ TORRENT_COLUMN_SIZE,
+ TORRENT_COLUMN_DONE,
+ TORRENT_COLUMN_STATUS,
+ TORRENT_COLUMN_SEEDS,
+ TORRENT_COLUMN_LEECHERS,
+ TORRENT_COLUMN_DOWNSPEED,
+ TORRENT_COLUMN_UPSPEED,
+ TORRENT_COLUMN_ETA,
+ TORRENT_COLUMN_UPLOADED,
+ TORRENT_COLUMN_DOWNLOADED,
+ TORRENT_COLUMN_RATIO,
+ TORRENT_COLUMN_ID,
+ TORRENT_COLUMN_JSON,
+ TORRENT_COLUMN_UPDATESERIAL,
+ TORRENT_COLUMN_FLAGS,
+ TORRENT_COLUMN_COLUMNS
+};
+
+struct idAndIter {
+ gint id;
+ GtkTreeIter *iter;
+ gboolean found;
+};
+
+#endif /* TRG_TORRENT_MODEL_H_ */
diff --git a/src/trg-torrent-props-dialog.c b/src/trg-torrent-props-dialog.c
new file mode 100644
index 0000000..1740502
--- /dev/null
+++ b/src/trg-torrent-props-dialog.c
@@ -0,0 +1,372 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+
+#include "torrent.h"
+#include "json.h"
+#include "dispatch.h"
+#include "trg-client.h"
+#include "requests.h"
+#include "protocol-constants.h"
+#include "trg-torrent-model.h"
+#include "trg-torrent-tree-view.h"
+#include "trg-torrent-props-dialog.h"
+#include "trg-main-window.h"
+#include "trg-json-widgets.h"
+#include "hig.h"
+
+G_DEFINE_TYPE(TrgTorrentPropsDialog, trg_torrent_props_dialog,
+ GTK_TYPE_DIALOG)
+
+enum {
+ PROP_0,
+ PROP_TREEVIEW,
+ PROP_PARENT_WINDOW
+};
+
+#define TRG_TORRENT_PROPS_DIALOG_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_TORRENT_PROPS_DIALOG, TrgTorrentPropsDialogPrivate))
+typedef struct _TrgTorrentPropsDialogPrivate TrgTorrentPropsDialogPrivate;
+
+struct _TrgTorrentPropsDialogPrivate {
+ TrgTorrentTreeView *tv;
+ trg_client *client;
+ GtkWindow *parent;
+ JsonArray *targetIds;
+
+ GtkWidget *bandwidthPriorityCombo;
+ GtkWidget *down_limited_check, *down_limit_spin;
+ GtkWidget *up_limited_check, *up_limit_spin;
+ GtkWidget *seedRatioMode, *seedRatioLimit;
+ GtkWidget *honor_limits_check;
+ GtkWidget *peer_limit_spin;
+};
+
+static void
+trg_torrent_props_dialog_set_property(GObject * object,
+ guint prop_id,
+ const GValue * value,
+ GParamSpec * pspec)
+{
+ TrgTorrentPropsDialogPrivate *priv =
+ TRG_TORRENT_PROPS_DIALOG_GET_PRIVATE(object);
+
+ switch (prop_id) {
+ case PROP_PARENT_WINDOW:
+ priv->parent = g_value_get_object(value);
+ break;
+ case PROP_TREEVIEW:
+ priv->tv = g_value_get_object(value);
+ break;
+ }
+}
+
+static void
+trg_torrent_props_dialog_get_property(GObject * object,
+ guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ TrgTorrentPropsDialogPrivate *priv =
+ TRG_TORRENT_PROPS_DIALOG_GET_PRIVATE(object);
+
+ switch (prop_id) {
+ case PROP_TREEVIEW:
+ g_value_set_object(value, priv->tv);
+ break;
+ case PROP_PARENT_WINDOW:
+ g_value_set_object(value, priv->parent);
+ break;
+ }
+}
+
+static void
+trg_torrent_props_response_cb(GtkDialog * dlg, gint res_id, gpointer data)
+{
+ TrgTorrentPropsDialogPrivate *priv;
+ JsonNode *request;
+ JsonObject *args;
+
+ priv = TRG_TORRENT_PROPS_DIALOG_GET_PRIVATE(dlg);
+
+ if (res_id != GTK_RESPONSE_ACCEPT) {
+ gtk_widget_destroy(GTK_WIDGET(dlg));
+ return;
+ }
+
+ request = torrent_set(priv->targetIds);
+ args = node_get_arguments(request);
+
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->honor_limits_check), args);
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->down_limited_check), args);
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->up_limited_check), args);
+ gtk_spin_button_json_int_out(GTK_SPIN_BUTTON
+ (priv->down_limit_spin), args);
+ gtk_spin_button_json_int_out(GTK_SPIN_BUTTON(priv->up_limit_spin),
+ args);
+ gtk_spin_button_json_double_out(GTK_SPIN_BUTTON
+ (priv->seedRatioLimit), args);
+ json_object_set_int_member(args, FIELD_SEED_RATIO_MODE,
+ gtk_combo_box_get_active(GTK_COMBO_BOX
+ (priv->
+ seedRatioMode)));
+ json_object_set_int_member(args, FIELD_BANDWIDTH_PRIORITY,
+ gtk_combo_box_get_active(GTK_COMBO_BOX
+ (priv->
+ bandwidthPriorityCombo))
+ + 1);
+
+ gtk_spin_button_json_int_out(GTK_SPIN_BUTTON
+ (priv->peer_limit_spin), args);
+
+ dispatch_async(priv->client, request,
+ on_generic_interactive_action, priv->parent);
+
+ gtk_widget_destroy(GTK_WIDGET(dlg));
+}
+
+static void seed_ratio_mode_changed_cb(GtkWidget * w, gpointer data)
+{
+ gtk_widget_set_sensitive(GTK_WIDGET(data),
+ gtk_combo_box_get_active(GTK_COMBO_BOX(w))
+ == 1 ? TRUE : FALSE);
+}
+
+static GtkWidget *trg_props_limitsPage(TrgTorrentPropsDialog * win,
+ JsonObject * json)
+{
+ TrgTorrentPropsDialogPrivate *priv =
+ TRG_TORRENT_PROPS_DIALOG_GET_PRIVATE(win);
+ GtkWidget *w, *tb, *t;
+ gint row = 0;
+
+ t = hig_workarea_create();
+
+ hig_workarea_add_section_title(t, &row, "Bandwidth");
+
+ w = priv->honor_limits_check =
+ hig_workarea_add_wide_checkbutton(t, &row,
+ "Honor global limits",
+ torrent_get_honors_session_limits
+ (json));
+ widget_set_json_key(w, FIELD_HONORS_SESSION_LIMITS);
+
+ w = priv->bandwidthPriorityCombo = gtk_combo_box_new_text();
+ widget_set_json_key(w, FIELD_BANDWIDTH_PRIORITY);
+ gtk_combo_box_append_text(GTK_COMBO_BOX(w), "Low");
+ gtk_combo_box_append_text(GTK_COMBO_BOX(w), "Normal");
+ gtk_combo_box_append_text(GTK_COMBO_BOX(w), "High");
+ gtk_combo_box_set_active(GTK_COMBO_BOX(w),
+ torrent_get_bandwidth_priority(json) + 1);
+ hig_workarea_add_row(t, &row, "Torrent priority:", w, NULL);
+
+ tb = priv->down_limited_check = gtk_check_button_new_with_mnemonic
+ ("Limit download speed (Kbps)");
+ widget_set_json_key(tb, FIELD_DOWNLOAD_LIMITED);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb),
+ torrent_get_download_limited(json));
+
+ w = priv->down_limit_spin =
+ gtk_spin_button_new_with_range(0, INT_MAX, 5);
+ widget_set_json_key(w, FIELD_DOWNLOAD_LIMIT);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w),
+ torrent_get_download_limit(json));
+ hig_workarea_add_row_w(t, &row, tb, w, NULL);
+
+ tb = priv->up_limited_check = gtk_check_button_new_with_mnemonic
+ ("Limit upload speed (Kbps)");
+ widget_set_json_key(tb, FIELD_UPLOAD_LIMITED);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb),
+ torrent_get_upload_limited(json));
+
+ w = priv->up_limit_spin =
+ gtk_spin_button_new_with_range(0, INT_MAX, 5);
+ widget_set_json_key(w, FIELD_UPLOAD_LIMIT);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w),
+ torrent_get_upload_limit(json));
+ hig_workarea_add_row_w(t, &row, tb, w, NULL);
+
+ hig_workarea_add_section_title(t, &row, "Seeding");
+
+ w = priv->seedRatioMode = gtk_combo_box_new_text();
+ widget_set_json_key(GTK_WIDGET(w), FIELD_SEED_RATIO_MODE);
+ gtk_combo_box_append_text(GTK_COMBO_BOX(w), "Use global settings");
+ gtk_combo_box_append_text(GTK_COMBO_BOX(w), "Stop seeding at ratio");
+ gtk_combo_box_append_text(GTK_COMBO_BOX(w),
+ "Seed regardless of ratio");
+ gtk_combo_box_set_active(GTK_COMBO_BOX(w),
+ torrent_get_seed_ratio_mode(json));
+ hig_workarea_add_row(t, &row, "Seed ratio mode:", w, NULL);
+
+ w = priv->seedRatioLimit =
+ gtk_spin_button_new_with_range(0, INT_MAX, 0.2);
+ gtk_widget_set_sensitive(GTK_WIDGET(w), FALSE);
+ widget_set_json_key(GTK_WIDGET(w), FIELD_SEED_RATIO_LIMIT);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w),
+ torrent_get_seed_ratio_limit(json));
+ hig_workarea_add_row(t, &row, "Seed ratio limit:", w, w);
+ g_signal_connect(G_OBJECT(priv->seedRatioMode), "changed",
+ G_CALLBACK(seed_ratio_mode_changed_cb), w);
+
+ hig_workarea_add_section_title(t, &row, "Peers");
+
+ w = priv->peer_limit_spin =
+ gtk_spin_button_new_with_range(0, INT_MAX, 5);
+ widget_set_json_key(GTK_WIDGET(w), FIELD_PEER_LIMIT);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w),
+ torrent_get_peer_limit(json));
+ hig_workarea_add_row(t, &row, "Peer limit:", w, w);
+
+ return t;
+}
+
+static GObject *trg_torrent_props_dialog_constructor(GType type,
+ guint
+ n_construct_properties,
+ GObjectConstructParam
+ * construct_params)
+{
+ GObject *object;
+ TrgTorrentPropsDialogPrivate *priv;
+ JsonObject *json;
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+ gint rowCount;
+ GtkWidget *notebook;
+
+ object = G_OBJECT_CLASS
+ (trg_torrent_props_dialog_parent_class)->constructor(type,
+ n_construct_properties,
+ construct_params);
+
+ priv = TRG_TORRENT_PROPS_DIALOG_GET_PRIVATE(object);
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->tv));
+ rowCount = gtk_tree_selection_count_selected_rows(selection);
+ get_first_selected(priv->tv, &iter, &json);
+ priv->targetIds = build_json_id_array(priv->tv);
+
+ if (rowCount > 1) {
+ gchar *windowTitle =
+ g_strdup_printf("Multiple (%d) torrent properties",
+ rowCount);
+ gtk_window_set_title(GTK_WINDOW(object), windowTitle);
+ g_free(windowTitle);
+ } else if (rowCount == 1) {
+ gtk_window_set_title(GTK_WINDOW(object), torrent_get_name(json));
+ }
+
+ gtk_window_set_transient_for(GTK_WINDOW(object), priv->parent);
+ gtk_window_set_destroy_with_parent(GTK_WINDOW(object), TRUE);
+
+ gtk_dialog_add_button(GTK_DIALOG(object), GTK_STOCK_CLOSE,
+ GTK_RESPONSE_CLOSE);
+ gtk_dialog_add_button(GTK_DIALOG(object), GTK_STOCK_APPLY,
+ GTK_RESPONSE_ACCEPT);
+
+ gtk_container_set_border_width(GTK_CONTAINER(object), GUI_PAD);
+
+ gtk_dialog_set_default_response(GTK_DIALOG(object),
+ GTK_RESPONSE_CLOSE);
+
+ gtk_dialog_set_alternative_button_order(GTK_DIALOG(object),
+ GTK_RESPONSE_ACCEPT,
+ GTK_RESPONSE_CLOSE, -1);
+
+ g_signal_connect(G_OBJECT(object),
+ "response",
+ G_CALLBACK(trg_torrent_props_response_cb), NULL);
+
+ notebook = gtk_notebook_new();
+
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
+ trg_props_limitsPage
+ (TRG_TORRENT_PROPS_DIALOG(object), json),
+ gtk_label_new("Limits"));
+
+ gtk_container_set_border_width(GTK_CONTAINER(notebook), GUI_PAD);
+
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(object)->vbox), notebook,
+ TRUE, TRUE, 0);
+
+ return object;
+}
+
+static void
+trg_torrent_props_dialog_class_init(TrgTorrentPropsDialogClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ object_class->constructor = trg_torrent_props_dialog_constructor;
+ object_class->set_property = trg_torrent_props_dialog_set_property;
+ object_class->get_property = trg_torrent_props_dialog_get_property;
+
+ g_type_class_add_private(klass, sizeof(TrgTorrentPropsDialogPrivate));
+
+ g_object_class_install_property(object_class,
+ PROP_TREEVIEW,
+ g_param_spec_object
+ ("torrent-tree-view",
+ "TrgTorrentTreeView",
+ "TrgTorrentTreeView",
+ TRG_TYPE_TORRENT_TREE_VIEW,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+
+ g_object_class_install_property(object_class,
+ PROP_PARENT_WINDOW,
+ g_param_spec_object
+ ("parent-window", "Parent window",
+ "Parent window", GTK_TYPE_WINDOW,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+}
+
+static void trg_torrent_props_dialog_init(TrgTorrentPropsDialog * self)
+{
+
+}
+
+TrgTorrentPropsDialog *trg_torrent_props_dialog_new(GtkWindow * window,
+ TrgTorrentTreeView *
+ treeview,
+ trg_client * client)
+{
+ GObject *obj;
+ TrgTorrentPropsDialogPrivate *priv;
+
+ obj = g_object_new(TRG_TYPE_TORRENT_PROPS_DIALOG,
+ "parent-window", window,
+ "torrent-tree-view", treeview, NULL);
+
+ priv = TRG_TORRENT_PROPS_DIALOG_GET_PRIVATE(obj);
+ priv->client = client;
+
+ return TRG_TORRENT_PROPS_DIALOG(obj);
+}
diff --git a/src/trg-torrent-props-dialog.h b/src/trg-torrent-props-dialog.h
new file mode 100644
index 0000000..ff7d710
--- /dev/null
+++ b/src/trg-torrent-props-dialog.h
@@ -0,0 +1,57 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef TRG_TORRENT_PROPS_DIALOG_H_
+#define TRG_TORRENT_PROPS_DIALOG_H_
+
+#include <glib-object.h>
+
+#include "trg-torrent-tree-view.h"
+#include "trg-client.h"
+
+G_BEGIN_DECLS
+#define TRG_TYPE_TORRENT_PROPS_DIALOG trg_torrent_props_dialog_get_type()
+#define TRG_TORRENT_PROPS_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_TORRENT_PROPS_DIALOG, TrgTorrentPropsDialog))
+#define TRG_TORRENT_PROPS_DIALOG_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_TORRENT_PROPS_DIALOG, TrgTorrentPropsDialogClass))
+#define TRG_IS_TORRENT_PROPS_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_TORRENT_PROPS_DIALOG))
+#define TRG_IS_TORRENT_PROPS_DIALOG_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_TORRENT_PROPS_DIALOG))
+#define TRG_TORRENT_PROPS_DIALOG_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_TORRENT_PROPS_DIALOG, TrgTorrentPropsDialogClass))
+ typedef struct {
+ GtkDialog parent;
+} TrgTorrentPropsDialog;
+
+typedef struct {
+ GtkDialogClass parent_class;
+} TrgTorrentPropsDialogClass;
+
+GType trg_torrent_props_dialog_get_type(void);
+
+TrgTorrentPropsDialog *trg_torrent_props_dialog_new(GtkWindow * window,
+ TrgTorrentTreeView *
+ treeview,
+ trg_client * client);
+
+G_END_DECLS
+#endif /* TRG_TORRENT_PROPS_DIALOG_H_ */
diff --git a/src/trg-torrent-tree-view.c b/src/trg-torrent-tree-view.c
new file mode 100644
index 0000000..02010b0
--- /dev/null
+++ b/src/trg-torrent-tree-view.c
@@ -0,0 +1,122 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <gtk/gtk.h>
+
+#include "trg-tree-view.h"
+#include "trg-torrent-model.h"
+#include "trg-torrent-tree-view.h"
+
+G_DEFINE_TYPE(TrgTorrentTreeView, trg_torrent_tree_view,
+ TRG_TYPE_TREE_VIEW)
+
+static void
+trg_torrent_tree_view_class_init(TrgTorrentTreeViewClass * klass)
+{
+}
+
+static void trg_torrent_tree_view_init(TrgTorrentTreeView * tv)
+{
+ trg_tree_view_add_pixbuf_text_column(TRG_TREE_VIEW(tv),
+ TORRENT_COLUMN_ICON,
+ TORRENT_COLUMN_NAME, "Name", -1);
+ trg_tree_view_add_size_column(TRG_TREE_VIEW(tv), "Size",
+ TORRENT_COLUMN_SIZE, -1);
+ trg_tree_view_add_prog_column(TRG_TREE_VIEW(tv), "Done",
+ TORRENT_COLUMN_DONE, 70);
+ trg_tree_view_add_column(TRG_TREE_VIEW(tv), "Status",
+ TORRENT_COLUMN_STATUS);
+ trg_tree_view_add_column(TRG_TREE_VIEW(tv), "Seeds",
+ TORRENT_COLUMN_SEEDS);
+ trg_tree_view_add_column(TRG_TREE_VIEW(tv), "Leechers",
+ TORRENT_COLUMN_LEECHERS);
+ trg_tree_view_add_speed_column(TRG_TREE_VIEW(tv), "Down Speed",
+ TORRENT_COLUMN_DOWNSPEED, -1);
+ trg_tree_view_add_speed_column(TRG_TREE_VIEW(tv), "Up Speed",
+ TORRENT_COLUMN_UPSPEED, -1);
+ trg_tree_view_add_eta_column(TRG_TREE_VIEW(tv), "ETA",
+ TORRENT_COLUMN_ETA, -1);
+ trg_tree_view_add_size_column(TRG_TREE_VIEW(tv), "Uploaded",
+ TORRENT_COLUMN_UPLOADED, -1);
+ trg_tree_view_add_size_column(TRG_TREE_VIEW(tv), "Downloaded",
+ TORRENT_COLUMN_DOWNLOADED, -1);
+ trg_tree_view_add_ratio_column(TRG_TREE_VIEW(tv), "Ratio",
+ TORRENT_COLUMN_RATIO, -1);
+}
+
+gint get_first_selected(TrgTorrentTreeView * view, GtkTreeIter * iter,
+ JsonObject ** json)
+{
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GList *selectionList;
+ GList *firstNode;
+ gint id = -1;
+
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
+ selectionList = gtk_tree_selection_get_selected_rows(selection, NULL);
+
+ if ((firstNode = g_list_first(selectionList)) != NULL) {
+ if (gtk_tree_model_get_iter(model, iter, firstNode->data) == TRUE) {
+ gtk_tree_model_get(model, iter,
+ TORRENT_COLUMN_JSON, json,
+ TORRENT_COLUMN_ID, &id, -1);
+ }
+ }
+
+ g_list_free(selectionList);
+
+ return id;
+}
+
+void
+trg_torrent_model_get_json_id_array_foreach(GtkTreeModel * model,
+ GtkTreePath * path,
+ GtkTreeIter * iter,
+ gpointer data)
+{
+ JsonArray *output = (JsonArray *) data;
+ gint id;
+ gtk_tree_model_get(model, iter, TORRENT_COLUMN_ID, &id, -1);
+ json_array_add_int_element(output, id);
+}
+
+JsonArray *build_json_id_array(TrgTorrentTreeView * tv)
+{
+ GtkTreeSelection *selection =
+ gtk_tree_view_get_selection(GTK_TREE_VIEW(tv));
+
+ JsonArray *ids = json_array_new();
+ gtk_tree_selection_selected_foreach(selection,
+ (GtkTreeSelectionForeachFunc)
+ trg_torrent_model_get_json_id_array_foreach,
+ ids);
+ return ids;
+}
+
+TrgTorrentTreeView *trg_torrent_tree_view_new(GtkTreeModel * model)
+{
+ GObject *obj = g_object_new(TRG_TYPE_TORRENT_TREE_VIEW, NULL);
+
+ gtk_tree_view_set_model(GTK_TREE_VIEW(obj), model);
+
+ return TRG_TORRENT_TREE_VIEW(obj);
+}
diff --git a/src/trg-torrent-tree-view.h b/src/trg-torrent-tree-view.h
new file mode 100644
index 0000000..b900985
--- /dev/null
+++ b/src/trg-torrent-tree-view.h
@@ -0,0 +1,58 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _TRG_TORRENT_TREE_VIEW_H_
+#define _TRG_TORRENT_TREE_VIEW_H_
+
+#include <glib-object.h>
+#include <json-glib/json-glib.h>
+
+#include "trg-torrent-model.h"
+#include "trg-tree-view.h"
+#include "trg-state-selector.h"
+
+G_BEGIN_DECLS
+#define TRG_TYPE_TORRENT_TREE_VIEW trg_torrent_tree_view_get_type()
+#define TRG_TORRENT_TREE_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_TORRENT_TREE_VIEW, TrgTorrentTreeView))
+#define TRG_TORRENT_TREE_VIEW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_TORRENT_TREE_VIEW, TrgTorrentTreeViewClass))
+#define TRG_IS_TORRENT_TREE_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_TORRENT_TREE_VIEW))
+#define TRG_IS_TORRENT_TREE_VIEW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_TORRENT_TREE_VIEW))
+#define TRG_TORRENT_TREE_VIEW_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_TORRENT_TREE_VIEW, TrgTorrentTreeViewClass))
+ typedef struct {
+ TrgTreeView parent;
+} TrgTorrentTreeView;
+
+typedef struct {
+ TrgTreeViewClass parent_class;
+} TrgTorrentTreeViewClass;
+
+GType trg_torrent_tree_view_get_type(void);
+
+TrgTorrentTreeView *trg_torrent_tree_view_new(GtkTreeModel * model);
+JsonArray *build_json_id_array(TrgTorrentTreeView * tv);
+gint get_first_selected(TrgTorrentTreeView * view, GtkTreeIter * iter,
+ JsonObject ** json);
+
+G_END_DECLS
+#endif /* _TRG_TORRENT_TREE_VIEW_H_ */
diff --git a/src/trg-trackers-model.c b/src/trg-trackers-model.c
new file mode 100644
index 0000000..9a7a5e7
--- /dev/null
+++ b/src/trg-trackers-model.c
@@ -0,0 +1,72 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+
+#include "torrent.h"
+#include "trg-trackers-model.h"
+
+G_DEFINE_TYPE(TrgTrackersModel, trg_trackers_model, GTK_TYPE_LIST_STORE)
+
+void trg_trackers_model_update(TrgTrackersModel * model, JsonObject * t)
+{
+ int j;
+ JsonArray *trackers;
+
+ gtk_list_store_clear(GTK_LIST_STORE(model));
+
+ trackers = torrent_get_trackers(t);
+ for (j = 0; j < json_array_get_length(trackers); j++) {
+ JsonObject *tracker =
+ json_node_get_object(json_array_get_element(trackers, j));
+ GtkTreeIter trackIter;
+ gtk_list_store_append(GTK_LIST_STORE(model), &trackIter);
+ gtk_list_store_set(GTK_LIST_STORE(model), &trackIter,
+ TRACKERCOL_ICON, GTK_STOCK_NETWORK,
+ TRACKERCOL_TIER,
+ tracker_get_tier(tracker),
+ TRACKERCOL_ANNOUNCE,
+ tracker_get_announce(tracker),
+ TRACKERCOL_SCRAPE,
+ tracker_get_scrape(tracker), -1);
+ }
+}
+
+static void trg_trackers_model_class_init(TrgTrackersModelClass * klass)
+{
+}
+
+static void trg_trackers_model_init(TrgTrackersModel * self)
+{
+ GType column_types[TRACKERCOL_COLUMNS];
+
+ column_types[TRACKERCOL_ICON] = G_TYPE_STRING;
+ column_types[TRACKERCOL_TIER] = G_TYPE_INT;
+ column_types[TRACKERCOL_ANNOUNCE] = G_TYPE_STRING;
+ column_types[TRACKERCOL_SCRAPE] = G_TYPE_STRING;
+
+ gtk_list_store_set_column_types(GTK_LIST_STORE(self),
+ TRACKERCOL_COLUMNS, column_types);
+}
+
+TrgTrackersModel *trg_trackers_model_new(void)
+{
+ return g_object_new(TRG_TYPE_TRACKERS_MODEL, NULL);
+}
diff --git a/src/trg-trackers-model.h b/src/trg-trackers-model.h
new file mode 100644
index 0000000..2de1b78
--- /dev/null
+++ b/src/trg-trackers-model.h
@@ -0,0 +1,62 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TRG_TRACKERS_MODEL_H_
+#define TRG_TRACKERS_MODEL_H_
+
+#include <glib-object.h>
+#include <json-glib/json-glib.h>
+
+G_BEGIN_DECLS
+#define TRG_TYPE_TRACKERS_MODEL trg_trackers_model_get_type()
+#define TRG_TRACKERS_MODEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_TRACKERS_MODEL, TrgTrackersModel))
+#define TRG_TRACKERS_MODEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_TRACKERS_MODEL, TrgTrackersModelClass))
+#define TRG_IS_TRACKERS_MODEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_TRACKERS_MODEL))
+#define TRG_IS_TRACKERS_MODEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_TRACKERS_MODEL))
+#define TRG_TRACKERS_MODEL_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_TRACKERS_MODEL, TrgTrackersModelClass))
+ typedef struct {
+ GtkListStore parent;
+} TrgTrackersModel;
+
+typedef struct {
+ GtkListStoreClass parent_class;
+} TrgTrackersModelClass;
+
+GType trg_trackers_model_get_type(void);
+
+TrgTrackersModel *trg_trackers_model_new(void);
+
+G_END_DECLS
+ void
+trg_trackers_model_update(TrgTrackersModel * model, JsonObject * t);
+
+enum {
+ TRACKERCOL_ICON,
+ TRACKERCOL_TIER,
+ TRACKERCOL_ANNOUNCE,
+ TRACKERCOL_SCRAPE,
+ TRACKERCOL_COLUMNS
+};
+
+#endif /* TRG_TRACKERS_MODEL_H_ */
diff --git a/src/trg-trackers-tree-view.c b/src/trg-trackers-tree-view.c
new file mode 100644
index 0000000..6f8275f
--- /dev/null
+++ b/src/trg-trackers-tree-view.c
@@ -0,0 +1,51 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <gtk/gtk.h>
+#include "trg-trackers-tree-view.h"
+
+#include "trg-tree-view.h"
+
+G_DEFINE_TYPE(TrgTrackersTreeView, trg_trackers_tree_view,
+ TRG_TYPE_TREE_VIEW)
+
+
+static void
+trg_trackers_tree_view_class_init(TrgTrackersTreeViewClass * klass)
+{
+}
+
+static void trg_trackers_tree_view_init(TrgTrackersTreeView * self)
+{
+ trg_tree_view_add_pixbuf_text_column(TRG_TREE_VIEW(self),
+ TRACKERCOL_ICON,
+ TRACKERCOL_TIER, "Tier", -1);
+ trg_tree_view_add_column(TRG_TREE_VIEW(self), "Announce URL",
+ TRACKERCOL_ANNOUNCE);
+ trg_tree_view_add_column(TRG_TREE_VIEW(self), "Scrape URL",
+ TRACKERCOL_SCRAPE);
+}
+
+TrgTrackersTreeView *trg_trackers_tree_view_new(TrgTrackersModel * model)
+{
+ GObject *obj = g_object_new(TRG_TYPE_TRACKERS_TREE_VIEW, NULL);
+ gtk_tree_view_set_model(GTK_TREE_VIEW(obj), GTK_TREE_MODEL(model));
+
+ return TRG_TRACKERS_TREE_VIEW(obj);
+}
diff --git a/src/trg-trackers-tree-view.h b/src/trg-trackers-tree-view.h
new file mode 100644
index 0000000..02c89fd
--- /dev/null
+++ b/src/trg-trackers-tree-view.h
@@ -0,0 +1,54 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TRG_TRACKERS_TREE_VIEW_H_
+#define TRG_TRACKERS_TREE_VIEW_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "trg-trackers-model.h"
+#include "trg-tree-view.h"
+
+G_BEGIN_DECLS
+#define TRG_TYPE_TRACKERS_TREE_VIEW trg_trackers_tree_view_get_type()
+#define TRG_TRACKERS_TREE_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_TRACKERS_TREE_VIEW, TrgTrackersTreeView))
+#define TRG_TRACKERS_TREE_VIEW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_TRACKERS_TREE_VIEW, TrgTrackersTreeViewClass))
+#define TRG_IS_TRACKERS_TREE_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_TRACKERS_TREE_VIEW))
+#define TRG_IS_TRACKERS_TREE_VIEW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_TRACKERS_TREE_VIEW))
+#define TRG_TRACKERS_TREE_VIEW_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_TRACKERS_TREE_VIEW, TrgTrackersTreeViewClass))
+ typedef struct {
+ TrgTreeView parent;
+} TrgTrackersTreeView;
+
+typedef struct {
+ TrgTreeViewClass parent_class;
+} TrgTrackersTreeViewClass;
+
+GType trg_trackers_tree_view_get_type(void);
+
+TrgTrackersTreeView *trg_trackers_tree_view_new(TrgTrackersModel * model);
+
+G_END_DECLS
+#endif /* TRG_TRACKERS_TREE_VIEW_H_ */
diff --git a/src/trg-tree-view.c b/src/trg-tree-view.c
new file mode 100644
index 0000000..a3575c7
--- /dev/null
+++ b/src/trg-tree-view.c
@@ -0,0 +1,178 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <gtk/gtk.h>
+
+#include "trg-tree-view.h"
+#include "trg-cell-renderer-speed.h"
+#include "trg-cell-renderer-size.h"
+#include "trg-cell-renderer-ratio.h"
+#include "trg-cell-renderer-eta.h"
+
+G_DEFINE_TYPE(TrgTreeView, trg_tree_view, GTK_TYPE_TREE_VIEW)
+
+void trg_tree_view_std_column_setup(GtkTreeViewColumn * column, int index,
+ int width)
+{
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_reorderable(column, TRUE);
+ gtk_tree_view_column_set_sort_column_id(column, index);
+
+ if (width > 0) {
+ gtk_tree_view_column_set_sizing(column,
+ GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_fixed_width(column, width);
+ }
+}
+
+static void trg_tree_view_class_init(TrgTreeViewClass * klass)
+{
+}
+
+static void trg_tree_view_init(TrgTreeView * tv)
+{
+ gtk_tree_view_set_rubber_banding(GTK_TREE_VIEW(tv), TRUE);
+ gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(tv), TRUE);
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tv), TRUE);
+ gtk_tree_selection_set_mode(gtk_tree_view_get_selection
+ (GTK_TREE_VIEW(tv)),
+ GTK_SELECTION_MULTIPLE);
+
+ gtk_widget_set_sensitive(GTK_WIDGET(tv), FALSE);
+}
+
+void trg_tree_view_add_size_column(TrgTreeView * tv, char *title,
+ int index, int width)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ renderer = trg_cell_renderer_size_new();
+ column = gtk_tree_view_column_new_with_attributes(title, renderer,
+ "size-value",
+ index, NULL);
+
+ trg_tree_view_std_column_setup(column, index, width);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tv), column);
+}
+
+void trg_tree_view_add_eta_column(TrgTreeView * tv, char *title, int index,
+ int width)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ renderer = trg_cell_renderer_eta_new();
+ column = gtk_tree_view_column_new_with_attributes(title, renderer,
+ "eta-value",
+ index, NULL);
+
+ trg_tree_view_std_column_setup(column, index, width);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tv), column);
+}
+
+void trg_tree_view_add_prog_column(TrgTreeView * tv,
+ gchar * title, gint index, gint width)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ renderer = gtk_cell_renderer_progress_new();
+ column = gtk_tree_view_column_new_with_attributes(title, renderer,
+ "value", index,
+ NULL);
+
+ trg_tree_view_std_column_setup(column, index, width);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tv), column);
+}
+
+void trg_tree_view_add_speed_column(TrgTreeView * tv, char *title,
+ int index, int width)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ renderer = trg_cell_renderer_speed_new();
+ column = gtk_tree_view_column_new_with_attributes(title, renderer,
+ "speed-value",
+ index, NULL);
+
+ trg_tree_view_std_column_setup(column, index, width);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tv), column);
+}
+
+void trg_tree_view_add_ratio_column(TrgTreeView * tv, char *title,
+ int index, int width)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ renderer = trg_cell_renderer_ratio_new();
+ column = gtk_tree_view_column_new_with_attributes(title, renderer,
+ "ratio-value",
+ index, NULL);
+
+ trg_tree_view_std_column_setup(column, index, width);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tv), column);
+}
+
+void trg_tree_view_add_column_fixed_width(TrgTreeView * tv, char *title,
+ int index, int width)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(title, renderer,
+ "text", index, NULL);
+
+ trg_tree_view_std_column_setup(column, index, width);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tv), column);
+}
+
+void
+trg_tree_view_add_pixbuf_text_column(TrgTreeView * tv,
+ int iconIndex,
+ int nameIndex,
+ gchar * text, int width)
+{
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+
+ column = gtk_tree_view_column_new();
+
+ renderer = gtk_cell_renderer_pixbuf_new();
+ gtk_tree_view_column_set_title(GTK_TREE_VIEW_COLUMN(column), text);
+ gtk_tree_view_column_pack_start(column, renderer, FALSE);
+ gtk_tree_view_column_set_attributes(column, renderer, "stock-id",
+ iconIndex, NULL);
+
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_column_pack_start(column, renderer, TRUE);
+ gtk_tree_view_column_set_attributes(column, renderer, "text",
+ nameIndex, NULL);
+
+ trg_tree_view_std_column_setup(column, nameIndex, width);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tv), column);
+}
+
+GtkWidget *trg_tree_view_new(void)
+{
+ return GTK_WIDGET(g_object_new(TRG_TYPE_TREE_VIEW, NULL));
+}
diff --git a/src/trg-tree-view.h b/src/trg-tree-view.h
new file mode 100644
index 0000000..7400d9e
--- /dev/null
+++ b/src/trg-tree-view.h
@@ -0,0 +1,74 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _TRG_TREE_VIEW_H_
+#define _TRG_TREE_VIEW_H_
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+#define TRG_TYPE_TREE_VIEW trg_tree_view_get_type()
+#define TRG_TREE_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_TREE_VIEW, TrgTreeView))
+#define TRG_TREE_VIEW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_TREE_VIEW, TrgTreeViewClass))
+#define TRG_IS_TREE_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_TREE_VIEW))
+#define TRG_IS_TREE_VIEW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_TREE_VIEW))
+#define TRG_TREE_VIEW_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_TREE_VIEW, TrgTreeViewClass))
+ typedef struct {
+ GtkTreeView parent;
+} TrgTreeView;
+
+typedef struct {
+ GtkTreeViewClass parent_class;
+} TrgTreeViewClass;
+
+GType trg_tree_view_get_type(void);
+
+GtkWidget *trg_tree_view_new(void);
+
+G_END_DECLS
+#define trg_tree_view_add_column(tv, title, index) trg_tree_view_add_column_fixed_width(tv, title, index, -1)
+void trg_tree_view_add_column_fixed_width(TrgTreeView * treeview,
+ char *title, int index,
+ int width);
+
+void trg_tree_view_add_pixbuf_text_column(TrgTreeView *
+ treeview,
+ int iconIndex,
+ int nameIndex,
+ gchar * text, int width);
+
+void trg_tree_view_add_speed_column(TrgTreeView * tv, char *title,
+ int index, int width);
+void trg_tree_view_add_size_column(TrgTreeView * tv, char *title,
+ int index, int width);
+void trg_tree_view_add_prog_column(TrgTreeView * tv, gchar * title,
+ gint index, gint width);
+void trg_tree_view_add_ratio_column(TrgTreeView * tv, char *title,
+ int index, int width);
+void trg_tree_view_add_eta_column(TrgTreeView * tv, char *title, int index,
+ int width);
+void trg_tree_view_std_column_setup(GtkTreeViewColumn * column, int index,
+ int width);
+
+#endif /* _TRG_TREE_VIEW_H_ */
diff --git a/src/util.c b/src/util.c
new file mode 100644
index 0000000..1ca2a15
--- /dev/null
+++ b/src/util.c
@@ -0,0 +1,261 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#include <glib-object.h>
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <gtk/gtk.h>
+
+#include "util.h"
+
+char *tr_strlpercent(char *buf, double x, size_t buflen)
+{
+ return tr_strpercent(buf, x, buflen);
+}
+
+char *tr_strpercent(char *buf, double x, size_t buflen)
+{
+ int precision;
+ if (x < 10.0)
+ precision = 2;
+ else if (x < 100.0)
+ precision = 1;
+ else
+ precision = 0;
+
+ tr_snprintf(buf, buflen, "%.*f%%", precision, tr_truncd(x, precision));
+ return buf;
+}
+
+double tr_truncd(double x, int decimal_places)
+{
+ const int i = (int) pow(10, decimal_places);
+ double x2 = (int) (x * i);
+ return x2 / i;
+}
+
+char *tr_strratio(char *buf, size_t buflen, double ratio,
+ const char *infinity)
+{
+ if ((int) ratio == TR_RATIO_NA)
+ tr_strlcpy(buf, "None", buflen);
+ else if ((int) ratio == TR_RATIO_INF)
+ tr_strlcpy(buf, infinity, buflen);
+ else if (ratio < 10.0)
+ tr_snprintf(buf, buflen, "%.2f", tr_truncd(ratio, 2));
+ else if (ratio < 100.0)
+ tr_snprintf(buf, buflen, "%.1f", tr_truncd(ratio, 1));
+ else
+ tr_snprintf(buf, buflen, "%'.0f", ratio);
+ return buf;
+}
+
+char *tr_strlratio(char *buf, double ratio, size_t buflen)
+{
+ return tr_strratio(buf, buflen, ratio, "\xE2\x88\x9E");
+}
+
+char *tr_strlsize(char *buf, guint64 size, size_t buflen)
+{
+ if (!size)
+ g_strlcpy(buf, "None", buflen);
+#if GLIB_CHECK_VERSION( 2, 16, 0 )
+ else {
+ char *tmp = g_format_size_for_display(size);
+ g_strlcpy(buf, tmp, buflen);
+ g_free(tmp);
+ }
+#else
+ else if (size < (guint64) KILOBYTE_FACTOR)
+ g_snprintf(buf, buflen,
+ ngettext("%'u byte", "%'u bytes", (guint) size),
+ (guint) size);
+ else {
+ gdouble displayed_size;
+ if (size < (guint64) MEGABYTE_FACTOR) {
+ displayed_size = (gdouble) size / KILOBYTE_FACTOR;
+ g_snprintf(buf, buflen, _("%'.1f KB"), displayed_size);
+ } else if (size < (guint64) GIGABYTE_FACTOR) {
+ displayed_size = (gdouble) size / MEGABYTE_FACTOR;
+ g_snprintf(buf, buflen, _("%'.1f MB"), displayed_size);
+ } else {
+ displayed_size = (gdouble) size / GIGABYTE_FACTOR;
+ g_snprintf(buf, buflen, _("%'.1f GB"), displayed_size);
+ }
+ }
+#endif
+ return buf;
+}
+
+char *tr_strlspeed(char *buf, double kb_sec, size_t buflen)
+{
+ const double speed = kb_sec;
+
+ if (speed < 1000.0) /* 0.0 KB to 999.9 KB */
+ g_snprintf(buf, buflen, "%.1f KB/s", speed);
+ else if (speed < 102400.0) /* 0.98 MB to 99.99 MB */
+ g_snprintf(buf, buflen, "%.2f MB/s", (speed / KILOBYTE_FACTOR));
+ else if (speed < 1024000.0) /* 100.0 MB to 999.9 MB */
+ g_snprintf(buf, buflen, "%.1f MB/s", (speed / MEGABYTE_FACTOR));
+ else /* insane speeds */
+ g_snprintf(buf, buflen, "%.2f GB/s", (speed / GIGABYTE_FACTOR));
+
+ return buf;
+}
+
+char *tr_strltime_short(char *buf, gint64 seconds, size_t buflen)
+{
+ int hours, minutes;
+
+ if (seconds < 0)
+ seconds = 0;
+
+ hours = seconds / 3600;
+ minutes = (seconds % 3600) / 60;
+ seconds = (seconds % 3600) % 60;
+
+ g_snprintf(buf, buflen, "%02d:%02d:%02ld", hours, minutes, seconds);
+
+ return buf;
+}
+
+char *tr_strltime_long(char *buf, gint64 seconds, size_t buflen)
+{
+ int days, hours, minutes;
+ char d[128], h[128], m[128], s[128];
+
+ if (seconds < 0)
+ seconds = 0;
+
+ days = seconds / 86400;
+ hours = (seconds % 86400) / 3600;
+ minutes = (seconds % 3600) / 60;
+ seconds = (seconds % 3600) % 60;
+
+ g_snprintf(d, sizeof(d), days > 1 ? "%d days" : "%d day", days);
+ g_snprintf(h, sizeof(h), hours > 1 ? "%d hours" : "%d hour", hours);
+ g_snprintf(m, sizeof(m), minutes > 1 ? "%d minutes" : "%d minute",
+ minutes);
+ g_snprintf(s, sizeof(s),
+ seconds > 1 ? "%ld seconds" : "%ld second", seconds);
+
+ if (days) {
+ if (days >= 4 || !hours) {
+ g_strlcpy(buf, d, buflen);
+ } else {
+ g_snprintf(buf, buflen, "%s, %s", d, h);
+ }
+ } else if (hours) {
+ if (hours >= 4 || !minutes) {
+ g_strlcpy(buf, h, buflen);
+ } else {
+ g_snprintf(buf, buflen, "%s, %s", h, m);
+ }
+ } else if (minutes) {
+ if (minutes >= 4 || !seconds) {
+ g_strlcpy(buf, m, buflen);
+ } else {
+ g_snprintf(buf, buflen, "%s, %s", m, s);
+ }
+ } else {
+ g_strlcpy(buf, s, buflen);
+ }
+
+ return buf;
+}
+
+char *gtr_localtime(time_t time)
+{
+ const struct tm tm = *localtime(&time);
+ char buf[256], *eoln;
+
+ g_strlcpy(buf, asctime(&tm), sizeof(buf));
+ if ((eoln = strchr(buf, '\n')))
+ *eoln = '\0';
+
+ return g_locale_to_utf8(buf, -1, NULL, NULL, NULL);
+}
+
+char *gtr_localtime2(char *buf, time_t time, size_t buflen)
+{
+ char *tmp = gtr_localtime(time);
+ g_strlcpy(buf, tmp, buflen);
+ g_free(tmp);
+ return buf;
+}
+
+int tr_snprintf(char *buf, size_t buflen, const char *fmt, ...)
+{
+ int len;
+ va_list args;
+
+ va_start(args, fmt);
+ len = evutil_vsnprintf(buf, buflen, fmt, args);
+ va_end(args);
+ return len;
+}
+
+size_t tr_strlcpy(char *dst, const void *src, size_t siz)
+{
+#ifdef HAVE_STRLCPY
+ return strlcpy(dst, src, siz);
+#else
+ char *d = dst;
+ const char *s = src;
+ size_t n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0) {
+ while (--n != 0) {
+ if ((*d++ = *s++) == '\0')
+ break;
+ }
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++);
+ }
+
+ return s - (char *) src - 1; /* count does not include NUL */
+#endif
+}
+
+int
+evutil_vsnprintf(char *buf, size_t buflen, const char *format, va_list ap)
+{
+#ifdef _MSC_VER
+ int r = _vsnprintf(buf, buflen, format, ap);
+ buf[buflen - 1] = '\0';
+ if (r >= 0)
+ return r;
+ else
+ return _vscprintf(format, ap);
+#else
+ int r = vsnprintf(buf, buflen, format, ap);
+ buf[buflen - 1] = '\0';
+ return r;
+#endif
+}
diff --git a/src/util.h b/src/util.h
new file mode 100644
index 0000000..27ca2d7
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,56 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef UTIL_H_
+#define UTIL_H_
+
+#include <glib-object.h>
+
+#define WINDOW_ICON_FILE "/usr/share/icons/hicolor/scalable/apps/transmission.svg"
+
+#define trg_strlspeed(a, b) tr_strlspeed(a, b, sizeof(a))
+#define trg_strlpercent(a, b) tr_strlpercent(a, b, sizeof(a))
+#define trg_strlsize(a, b) tr_strlsize(a, b, sizeof(a))
+#define trg_strlratio(a, b) tr_strlratio(a, b, sizeof(a))
+
+#define TR_RATIO_NA -1
+#define TR_RATIO_INF -2
+#define KILOBYTE_FACTOR 1024.0
+#define MEGABYTE_FACTOR ( 1024.0 * 1024.0 )
+#define GIGABYTE_FACTOR ( 1024.0 * 1024.0 * 1024.0 )
+
+char *tr_strltime_long(char *buf, gint64 seconds, size_t buflen);
+char *tr_strltime_short(char *buf, gint64 seconds, size_t buflen);
+char *tr_strpercent(char *buf, double x, size_t buflen);
+char *tr_strlpercent(char *buf, double x, size_t buflen);
+char *tr_strratio(char *buf, size_t buflen, double ratio,
+ const char *infinity);
+char *tr_strlratio(char *buf, double ratio, size_t buflen);
+char *tr_strlspeed(char *buf, double kb_sec, size_t buflen);
+char *tr_strlsize(char *buf, guint64 size, size_t buflen);
+char *gtr_localtime(time_t time);
+char *gtr_localtime2(char *buf, time_t time, size_t buflen);
+int tr_snprintf(char *buf, size_t buflen, const char *fmt, ...);
+int tr_snprintf(char *buf, size_t buflen, const char *fmt, ...);
+size_t tr_strlcpy(char *dst, const void *src, size_t siz);
+double tr_truncd(double x, int decimal_places);
+int evutil_vsnprintf(char *buf, size_t buflen, const char *format,
+ va_list ap);
+
+#endif /* UTIL_H_ */