Major internal restructuring for the GTK GUI,

GObject-derived wrappers are used for tr_handle_t and tr_torrent_t.
Use bencoding to store prefs and state file.
Make sure to always group error messages when adding multiple torrents at once.
Remove some unused code.
Many miscellaneous cleanups.
This commit is contained in:
Josh Elsasser
2006-05-03 06:58:16 +00:00
parent 796e2b868e
commit 229d9c84dc
15 changed files with 1287 additions and 736 deletions

View File

@@ -1,7 +1,8 @@
include ../Makefile.config
include ../Makefile.common
SRCS = conf.c dialogs.c io.c ipc.c main.c trcellrenderertorrent.c util.c
SRCS = conf.c dialogs.c io.c ipc.c main.c tr_backend.c tr_torrent.c \
tr_cell_renderer_torrent.c util.c
OBJS = $(SRCS:%.c=%.o)
CFLAGS += $(CFLAGS_GTK) -I../libtransmission

View File

@@ -40,7 +40,6 @@
#include <glib/gi18n.h>
#include "conf.h"
#include "transmission.h"
#include "util.h"
#define CONF_SUBDIR "gtk"
@@ -61,10 +60,18 @@ static int
lockfile(const char *file, char **errstr);
static void
cf_removelocks(void);
static char *
cf_readfile(const char *file, const char *oldfile, gsize *len,
gboolean *usedold, char **errstr);
static void
cf_benc_append(benc_val_t *val, char type, int incsize);
static void
cf_writebenc(const char *file, const char *tmp, benc_val_t *data,
char **errstr);
static gboolean
writefile_traverse(gpointer key, gpointer value, gpointer data);
static char *
getstateval(struct cf_torrentstate *state, char *line);
getstateval(benc_val_t *state, char *line);
static char *gl_confdir = NULL;
static char *gl_old_confdir = NULL;
@@ -171,15 +178,59 @@ cf_sockname(void) {
return g_build_filename(gl_confdir, FILE_SOCKET, NULL);
}
gboolean
cf_loadprefs(char **errstr) {
char *path = g_build_filename(gl_confdir, FILE_PREFS, NULL);
char *oldpath;
static char *
cf_readfile(const char *file, const char *oldfile, gsize *len,
gboolean *usedold, char **errstr) {
char *path;
GIOChannel *io;
GError *err;
char *line, *sep;
gsize len, termpos;
char term = PREF_SEP_LINE;
char *ret;
*errstr = NULL;
*usedold = FALSE;
ret = NULL;
err = NULL;
*len = 0;
path = g_build_filename(gl_confdir, file, NULL);
io = g_io_channel_new_file(path, "r", &err);
if(NULL != err && g_error_matches(err, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
g_error_free(err);
err = NULL;
g_free(path);
path = g_build_filename(gl_old_confdir, oldfile, NULL);
io = g_io_channel_new_file(path, "r", &err);
*usedold = TRUE;
}
if(NULL != err) {
if(!g_error_matches(err, G_FILE_ERROR, G_FILE_ERROR_NOENT))
*errstr = g_strdup_printf(
_("Failed to open the file %s for reading:\n%s"), path, err->message);
goto done;
}
g_io_channel_set_encoding(io, NULL, NULL);
if(G_IO_STATUS_ERROR == g_io_channel_read_to_end(io, &ret, len, &err)) {
*errstr = g_strdup_printf(
_("Error while reading from the file %s:\n%s"), path, err->message);
goto done;
}
done:
if(NULL != err)
g_error_free(err);
if(NULL != io)
g_io_channel_unref(io);
return ret;
}
void
cf_loadprefs(char **errstr) {
char *data, *line, *eol, *sep, *key;
gsize len;
benc_val_t val;
gboolean usedold;
int ii;
*errstr = NULL;
@@ -189,58 +240,103 @@ cf_loadprefs(char **errstr) {
gl_prefs = g_tree_new_full((GCompareDataFunc)g_ascii_strcasecmp, NULL,
g_free, g_free);
err = NULL;
io = g_io_channel_new_file(path, "r", &err);
if(NULL != err) {
if(!g_error_matches(err, G_FILE_ERROR, G_FILE_ERROR_NOENT))
*errstr = g_strdup_printf(
_("Failed to open the file %s for reading:\n%s"), path, err->message);
else {
g_error_free(err);
err = NULL;
oldpath = g_build_filename(gl_old_confdir, OLD_FILE_PREFS, NULL);
io = g_io_channel_new_file(oldpath, "r", &err);
g_free(oldpath);
}
if(NULL != err)
goto done;
data = cf_readfile(FILE_PREFS, OLD_FILE_PREFS, &len, &usedold, errstr);
if(NULL != *errstr) {
g_assert(NULL == data);
return;
}
g_io_channel_set_line_term(io, &term, 1);
err = NULL;
for(;;) {
assert(NULL == err) ;
switch(g_io_channel_read_line(io, &line, &len, &termpos, &err)) {
case G_IO_STATUS_ERROR:
*errstr = g_strdup_printf(
_("Error while reading from the file %s:\n%s"), path, err->message);
goto done;
case G_IO_STATUS_NORMAL:
if(NULL != line) {
if(g_utf8_validate(line, len, NULL) &&
NULL != (sep = strchr(line, PREF_SEP_KEYVAL)) && sep > line) {
*sep = '\0';
line[termpos] = '\0';
g_tree_insert(gl_prefs, g_strcompress(line), g_strcompress(sep + 1));
}
g_free(line);
if(NULL == data)
return;
bzero(&val, sizeof(val));
if(!usedold && !tr_bencLoad(data, len, &val, NULL)) {
if(TYPE_DICT == val.type) {
key = NULL;
for(ii = 0; ii < val.val.l.count; ii++) {
if(NULL == key) {
g_assert(TYPE_STR == val.val.l.vals[ii].type);
key = val.val.l.vals[ii].val.s.s;
} else {
if(TYPE_INT == val.val.l.vals[ii].type)
g_tree_insert(gl_prefs, g_strdup(key),
g_strdup_printf("%"PRIu64, val.val.l.vals[ii].val.i));
else if(TYPE_STR == val.val.l.vals[ii].type)
g_tree_insert(gl_prefs, g_strdup(key),
g_strdup(val.val.l.vals[ii].val.s.s));
key = NULL;
}
break;
case G_IO_STATUS_EOF:
goto done;
default:
assert(!"unknown return code");
goto done;
}
}
} else {
/* XXX remove this in a release or two */
for(line = data; NULL != (eol = strchr(line, PREF_SEP_LINE));
line = eol + 1) {
*eol = '\0';
if(g_utf8_validate(line, -1, NULL) &&
NULL != (sep = strchr(line, PREF_SEP_KEYVAL))) {
*sep = '\0';
g_tree_insert(gl_prefs, g_strcompress(line), g_strcompress(sep+1));
}
}
cf_saveprefs(errstr);
}
g_free(data);
}
benc_val_t *
cf_loadstate(char **errstr) {
char *data, *line, *eol, *prog;
gsize len;
gboolean usedold;
benc_val_t *state, *torstate;
*errstr = NULL;
data = cf_readfile(FILE_STATE, OLD_FILE_STATE, &len, &usedold, errstr);
if(NULL != *errstr) {
g_assert(NULL == data);
return NULL;
}
if(NULL == data)
return NULL;
state = g_new0(benc_val_t, 1);
if(usedold || tr_bencLoad(data, len, state, NULL)) {
/* XXX all this evil compat code should go away at some point */
tr_bencFree(state);
bzero(state, sizeof(benc_val_t));
state->type = TYPE_LIST;
for(line = data; NULL != (eol = strchr(line, PREF_SEP_LINE));
line = eol + 1) {
*eol = '\0';
if(g_utf8_validate(line, -1, NULL)) {
cf_benc_append(state, TYPE_DICT, 10);
torstate = state->val.l.vals + state->val.l.count - 1;
prog = line;
while(NULL != (prog = getstateval(torstate, prog)))
;
}
}
}
done:
if(NULL != err)
g_error_free(err);
if(NULL != io)
g_io_channel_unref(io);
g_free(path);
return NULL == *errstr;
g_free(data);
return state;
}
static void
cf_benc_append(benc_val_t *val, char type, int incsize) {
if(++val->val.l.count > val->val.l.alloc) {
val->val.l.alloc += incsize;
val->val.l.vals = g_renew(benc_val_t, val->val.l.vals, val->val.l.alloc);
bzero(val->val.l.vals + val->val.l.alloc - incsize,
incsize * sizeof(benc_val_t));
}
val->val.l.vals[val->val.l.count-1].type = type;
}
const char *
@@ -257,176 +353,98 @@ cf_setpref(const char *name, const char *value) {
g_tree_insert(gl_prefs, g_strdup(name), g_strdup(value));
}
struct writeinfo {
GIOChannel *io;
GError *err;
};
gboolean
cf_saveprefs(char **errstr) {
char *file = g_build_filename(gl_confdir, FILE_PREFS, NULL);
char *tmpfile = g_build_filename(gl_confdir, FILE_PREFS_TMP, NULL);
static void
cf_writebenc(const char *file, const char *tmp, benc_val_t *data,
char **errstr) {
char *path = g_build_filename(gl_confdir, file, NULL);
char *pathtmp = g_build_filename(gl_confdir, tmp, NULL);
GIOChannel *io = NULL;
struct writeinfo info;
int fd;
assert(NULL != gl_prefs);
assert(NULL != errstr);
GError *err;
char *datastr;
size_t len;
gsize written;
*errstr = NULL;
err = NULL;
datastr = NULL;
if(0 > (fd = lockfile(tmpfile, errstr))) {
g_free(errstr);
*errstr = g_strdup_printf(_("Failed to open or lock the file %s:\n%s"),
tmpfile, strerror(errno));
io = g_io_channel_new_file(pathtmp, "w", &err);
if(NULL != err) {
*errstr = g_strdup_printf(_("Failed to open the file %s for writing:\n%s"),
pathtmp, err->message);
goto done;
}
g_io_channel_set_encoding(io, NULL, NULL);
#ifdef NDEBUG
ftruncate(fd, 0);
#else
assert(0 == ftruncate(fd, 0));
#endif
len = 0;
datastr = tr_bencSaveMalloc(data, &len);
info.err = NULL;
io = g_io_channel_unix_new(fd);
g_io_channel_set_close_on_unref(io, TRUE);
info.io = io;
info.err = NULL;
g_tree_foreach(gl_prefs, writefile_traverse, &info);
if(NULL != info.err ||
G_IO_STATUS_ERROR == g_io_channel_shutdown(io, TRUE, &info.err)) {
written = 0;
g_io_channel_write_chars(io, datastr, len, &written, &err);
if(NULL != err)
g_io_channel_flush(io, &err);
if(NULL != err) {
*errstr = g_strdup_printf(_("Error while writing to the file %s:\n%s"),
tmpfile, info.err->message);
g_error_free(info.err);
pathtmp, err->message);
goto done;
}
if(0 > rename(tmpfile, file)) {
if(0 > rename(pathtmp, path)) {
*errstr = g_strdup_printf(_("Failed to rename the file %s to %s:\n%s"),
tmpfile, file, strerror(errno));
pathtmp, file, strerror(errno));
goto done;
}
done:
g_free(file);
g_free(tmpfile);
g_free(path);
g_free(pathtmp);
if(NULL != io)
g_io_channel_unref(io);
if(NULL != datastr)
free(datastr);
}
return NULL == *errstr;
void
cf_saveprefs(char **errstr) {
benc_val_t val;
benc_val_t *ptr;
*errstr = NULL;
bzero(&val, sizeof(val));
val.type = TYPE_DICT;
val.val.l.alloc = val.val.l.count = g_tree_nnodes(gl_prefs) * 2;
val.val.l.vals = g_new0(benc_val_t, val.val.l.alloc);
ptr = val.val.l.vals;
g_tree_foreach(gl_prefs, writefile_traverse, &ptr);
g_assert(ptr - val.val.l.vals == val.val.l.alloc);
cf_writebenc(FILE_PREFS, FILE_PREFS_TMP, &val, errstr);
tr_bencFree(&val);
}
static gboolean
writefile_traverse(gpointer key, gpointer value, gpointer data) {
struct writeinfo *info = data;
char *ekey, *eval, *line;
char sep[2];
int len;
benc_val_t **ptr = data;
benc_val_t *bkey = *ptr;
benc_val_t *bval = (*ptr) + 1;
ekey = g_strescape(key, NULL);
eval = g_strescape(value, NULL);
sep[0] = PREF_SEP_KEYVAL;
sep[1] = '\0';
line = g_strjoin(sep, ekey, eval, NULL);
len = strlen(line);
line[len] = PREF_SEP_LINE;
*ptr = (*ptr) + 2;
switch(g_io_channel_write_chars(info->io, line, len + 1, NULL, &info->err)) {
case G_IO_STATUS_ERROR:
goto done;
case G_IO_STATUS_NORMAL:
break;
default:
assert(!"unknown return code");
goto done;
}
bkey->type = TYPE_STR;
bkey->val.s.s = key;
bkey->val.s.i = strlen(key);
done:
g_free(ekey);
g_free(eval);
g_free(line);
return NULL != info->err;
}
bval->type = TYPE_STR;
bval->val.s.s = value;
bval->val.s.i = strlen(value);
GList *
cf_loadstate(char **errstr) {
char *path = g_build_filename(gl_confdir, FILE_STATE, NULL);
char *oldpath;
GIOChannel *io;
GError *err;
char term = STATE_SEP;
GList *ret = NULL;
char *line, *ptr;
gsize len, termpos;
struct cf_torrentstate *ts;
err = NULL;
io = g_io_channel_new_file(path, "r", &err);
if(NULL != err) {
if(!g_error_matches(err, G_FILE_ERROR, G_FILE_ERROR_NOENT))
*errstr = g_strdup_printf(
_("Failed to open the file %s for reading:\n%s"), path, err->message);
else {
g_error_free(err);
err = NULL;
oldpath = g_build_filename(gl_old_confdir, OLD_FILE_STATE, NULL);
io = g_io_channel_new_file(oldpath, "r", &err);
g_free(oldpath);
}
if(NULL != err)
goto done;
}
g_io_channel_set_line_term(io, &term, 1);
err = NULL;
for(;;) {
assert(NULL == err);
switch(g_io_channel_read_line(io, &line, &len, &termpos, &err)) {
case G_IO_STATUS_ERROR:
*errstr = g_strdup_printf(
_("Error while reading from the file %s:\n%s"), path, err->message);
goto done;
case G_IO_STATUS_NORMAL:
if(NULL != line) {
if(g_utf8_validate(line, -1, NULL)) {
ts = g_new0(struct cf_torrentstate, 1);
ptr = line;
while(NULL != (ptr = getstateval(ts, ptr)))
;
if(NULL != ts->ts_torrent && NULL != ts->ts_directory)
ret = g_list_append(ret, ts);
else
cf_freestate(ts);
}
g_free(line);
}
break;
case G_IO_STATUS_EOF:
goto done;
default:
assert(!"unknown return code");
goto done;
}
}
done:
if(NULL != err)
g_error_free(err);
if(NULL != io)
g_io_channel_unref(io);
if(NULL != *errstr && NULL != ret) {
g_list_foreach(ret, (GFunc)g_free, NULL);
g_list_free(ret);
ret = NULL;
}
g_free(path);
return ret;
return FALSE;
}
static char *
getstateval(struct cf_torrentstate *state, char *line) {
getstateval(benc_val_t *state, char *line) {
char *start, *end;
/* skip any leading whitespace */
@@ -459,103 +477,33 @@ getstateval(struct cf_torrentstate *state, char *line) {
*end = '\0';
/* if it's a key we recognize then save the data */
if(0 == strcmp(line, "torrent"))
state->ts_torrent = g_strcompress(start);
else if(0 == strcmp(line, "dir"))
state->ts_directory = g_strcompress(start);
else if(0 == strcmp(line, "paused"))
state->ts_paused = strbool(start);
if(0 == strcmp(line, "torrent") || 0 == strcmp(line, "dir") ||
0 == strcmp(line, "paused")) {
cf_benc_append(state, TYPE_STR, 6);
state->val.l.vals[state->val.l.count-1].val.s.s = g_strdup(line);
state->val.l.vals[state->val.l.count-1].val.s.i = strlen(line);
if('p' == *line) {
cf_benc_append(state, TYPE_INT, 6);
state->val.l.vals[state->val.l.count-1].val.i = strbool(start);
} else {
cf_benc_append(state, TYPE_STR, 6);
state->val.l.vals[state->val.l.count-1].val.s.s = g_strdup(start);
state->val.l.vals[state->val.l.count-1].val.s.i = strlen(start);
}
}
/* return a pointer to just past the end of the value */
return end + 1;
}
gboolean
cf_savestate(GList *torrents, char **errstr) {
char *file = g_build_filename(gl_confdir, FILE_STATE, NULL);
char *tmpfile = g_build_filename(gl_confdir, FILE_STATE_TMP, NULL);
GIOChannel *io = NULL;
GError *err;
int fd;
char *torrentfile, *torrentdir, *line;
gsize written;
gboolean paused;
GIOStatus res;
tr_stat_t *sb;
tr_info_t *in;
void
cf_savestate(benc_val_t *state, char **errstr) {
*errstr = NULL;
if(0 > (fd = lockfile(tmpfile, errstr))) {
g_free(errstr);
*errstr = g_strdup_printf(_("Failed to open or lock the file %s:\n%s"),
tmpfile, strerror(errno));
goto done;
}
#ifdef NDEBUG
ftruncate(fd, 0);
#else
assert(0 == ftruncate(fd, 0));
#endif
io = g_io_channel_unix_new(fd);
g_io_channel_set_close_on_unref(io, TRUE);
err = NULL;
while(NULL != torrents) {
sb = tr_torrentStat(torrents->data);
in = tr_torrentInfo(torrents->data);
paused = (TR_STATUS_INACTIVE & sb->status);
torrentfile = g_strescape(in->torrent, "");
torrentdir = g_strescape(tr_torrentGetFolder(torrents->data), "");
/* g_strcompress */
line = g_strdup_printf("torrent=\"%s\" dir=\"%s\" paused=\"%s\"%c",
torrentfile, torrentdir, (paused ? "yes" : "no"),
STATE_SEP);
res = g_io_channel_write_chars(io, line, strlen(line), &written, &err);
g_free(torrentfile);
g_free(torrentdir);
g_free(line);
switch(res) {
case G_IO_STATUS_ERROR:
goto done;
case G_IO_STATUS_NORMAL:
break;
default:
assert(!"unknown return code");
goto done;
}
torrents = torrents->next;
}
if(NULL != err ||
G_IO_STATUS_ERROR == g_io_channel_shutdown(io, TRUE, &err)) {
*errstr = g_strdup_printf(_("Error while writing to the file %s:\n%s"),
tmpfile, err->message);
g_error_free(err);
goto done;
}
if(0 > rename(tmpfile, file)) {
*errstr = g_strdup_printf(_("Failed to rename the file %s to %s:\n%s"),
tmpfile, file, strerror(errno));
goto done;
}
done:
g_free(file);
g_free(tmpfile);
if(NULL != io)
g_io_channel_unref(io);
return NULL == *errstr;
cf_writebenc(FILE_STATE, FILE_STATE_TMP, state, errstr);
}
void
cf_freestate(struct cf_torrentstate *state) {
if(NULL != state->ts_torrent)
g_free(state->ts_torrent);
if(NULL != state->ts_directory)
g_free(state->ts_directory);
cf_freestate(benc_val_t *state) {
tr_bencFree(state);
g_free(state);
}

View File

@@ -28,12 +28,7 @@
#define TG_CONF_H
#include "transmission.h"
struct cf_torrentstate {
char *ts_torrent;
char *ts_directory;
gboolean ts_paused;
};
#include "bencode.h"
gboolean
cf_init(const char *confdir, char **errstr);
@@ -41,19 +36,19 @@ gboolean
cf_lock(char **errstr);
char *
cf_sockname(void);
gboolean
void
cf_loadprefs(char **errstr);
const char *
cf_getpref(const char *name);
void
cf_setpref(const char *name, const char *value);
gboolean
cf_saveprefs(char **errstr);
GList *
cf_loadstate(char **errstr);
gboolean
cf_savestate(GList *torrents, char **errstr);
void
cf_freestate(struct cf_torrentstate *state);
cf_saveprefs(char **errstr);
benc_val_t *
cf_loadstate(char **errstr);
void
cf_savestate(benc_val_t *state, char **errstr);
void
cf_freestate(benc_val_t *state);
#endif /* TG_CONF_H */

View File

@@ -45,13 +45,12 @@ struct prefdata {
GtkSpinButton *ulimit;
GtkFileChooser *dir;
GtkWindow *parent;
tr_handle_t *tr;
TrBackend *back;
};
struct addcb {
add_torrent_func_t addfunc;
add_torrents_func_t addfunc;
GtkWindow *parent;
torrents_added_func_t donefunc;
void *data;
gboolean autostart;
gboolean usingaltdir;
@@ -64,6 +63,8 @@ windclosed(GtkWidget *widget SHUTUP, gpointer gdata);
static void
clicklimitbox(GtkWidget *widget, gpointer gdata);
static void
freedata(gpointer gdata, GClosure *closure);
static void
clickdialog(GtkWidget *widget, int resp, gpointer gdata);
static void
autoclick(GtkWidget *widget, gpointer gdata);
@@ -73,7 +74,7 @@ static void
addresp(GtkWidget *widget, gint resp, gpointer gdata);
void
makeprefwindow(GtkWindow *parent, tr_handle_t *tr, gboolean *opened) {
makeprefwindow(GtkWindow *parent, TrBackend *back, gboolean *opened) {
char *title = g_strdup_printf(_("%s Preferences"), g_get_application_name());
GtkWidget *wind = gtk_dialog_new_with_buttons(title, parent,
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
@@ -119,7 +120,8 @@ makeprefwindow(GtkWindow *parent, tr_handle_t *tr, gboolean *opened) {
data->ulimit = GTK_SPIN_BUTTON(lim[1].num);
data->dir = GTK_FILE_CHOOSER(dirstr);
data->parent = parent;
data->tr = tr;
data->back = back;
g_object_ref(G_OBJECT(back));
#define RN(n) (n), (n) + 1
@@ -175,7 +177,7 @@ makeprefwindow(GtkWindow *parent, tr_handle_t *tr, gboolean *opened) {
gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(wind)->vbox), table);
g_signal_connect_data(wind, "response", G_CALLBACK(clickdialog),
data, (GClosureNotify)g_free, 0);
data, freedata, 0);
g_signal_connect(wind, "destroy", G_CALLBACK(windclosed), opened);
gtk_widget_show_all(wind);
}
@@ -197,6 +199,14 @@ clicklimitbox(GtkWidget *widget, gpointer gdata) {
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
}
static void
freedata(gpointer gdata, GClosure *closure SHUTUP) {
struct prefdata *data = gdata;
g_object_unref(G_OBJECT(data->back));
g_free(data);
}
static void
clickdialog(GtkWidget *widget, int resp, gpointer gdata) {
struct prefdata *data = gdata;
@@ -244,15 +254,17 @@ clickdialog(GtkWidget *widget, int resp, gpointer gdata) {
cf_setpref(PREF_UPLIMIT, strnum);
/* save prefs */
if(!cf_saveprefs(&errstr)) {
cf_saveprefs(&errstr);
if(NULL != errstr) {
errmsg(data->parent, "%s", errstr);
g_free(strnum);
g_free(errstr);
}
/* XXX would be nice to have errno strings, are they printed to stdout? */
tr_setBindPort(data->tr, gtk_spin_button_get_value_as_int(data->port));
setlimit(data->tr);
tr_setBindPort(tr_backend_handle(data->back),
gtk_spin_button_get_value_as_int(data->port));
setlimit(data->back);
}
if(GTK_RESPONSE_APPLY != resp)
@@ -260,7 +272,7 @@ clickdialog(GtkWidget *widget, int resp, gpointer gdata) {
}
void
setlimit(tr_handle_t *tr) {
setlimit(TrBackend *back) {
struct { void (*func)(tr_handle_t*, int);
const char *use; const char *num; gboolean defuse; long def; } lim[] = {
{tr_setDownloadLimit, PREF_USEDOWNLIMIT, PREF_DOWNLIMIT,
@@ -270,6 +282,7 @@ setlimit(tr_handle_t *tr) {
};
const char *pref;
unsigned int ii;
tr_handle_t *tr = tr_backend_handle(back);
for(ii = 0; ii < ALEN(lim); ii++) {
pref = cf_getpref(lim[ii].use);
@@ -283,8 +296,7 @@ setlimit(tr_handle_t *tr) {
}
void
makeaddwind(GtkWindow *parent, add_torrent_func_t addfunc,
torrents_added_func_t donefunc, void *cbdata) {
makeaddwind(GtkWindow *parent, add_torrents_func_t addfunc, void *cbdata) {
GtkWidget *wind = gtk_file_chooser_dialog_new(_("Add a Torrent"), parent,
GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
@@ -303,7 +315,6 @@ makeaddwind(GtkWindow *parent, add_torrent_func_t addfunc,
data->addfunc = addfunc;
data->parent = parent;
data->donefunc = donefunc;
data->data = cbdata;
data->autostart = TRUE;
data->usingaltdir = FALSE;
@@ -360,22 +371,24 @@ static void
addresp(GtkWidget *widget, gint resp, gpointer gdata) {
struct addcb *data = gdata;
GSList *files, *ii;
gboolean added = FALSE;
char *dir = NULL;
GList *stupidgtk;
gboolean paused;
char *dir;
if(GTK_RESPONSE_ACCEPT == resp) {
dir = NULL;
if(data->usingaltdir)
dir = gtk_file_chooser_get_filename(data->altdir);
files = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(widget));
stupidgtk = NULL;
for(ii = files; NULL != ii; ii = ii->next)
if(data->addfunc(data->data, ii->data, dir,
/* XXX need to group errors here */
!data->autostart, NULL))
added = TRUE;
if(added)
data->donefunc(data->data);
stupidgtk = g_list_append(stupidgtk, ii->data);
paused = !data->autostart;
data->addfunc(data->data, NULL, stupidgtk, dir, &paused);
if(NULL != dir)
g_free(dir);
g_slist_free(files);
freestrlist(stupidgtk);
}
gtk_widget_destroy(widget);
@@ -418,7 +431,7 @@ addresp(GtkWidget *widget, gint resp, gpointer gdata) {
} while(0)
void
makeinfowind(GtkWindow *parent, tr_torrent_t *tor) {
makeinfowind(GtkWindow *parent, TrTorrent *tor) {
tr_stat_t *sb;
tr_info_t *in;
GtkWidget *wind, *label;
@@ -427,8 +440,10 @@ makeinfowind(GtkWindow *parent, tr_torrent_t *tor) {
const int rowcount = 14;
GtkWidget *table = gtk_table_new(rowcount, 2, FALSE);
sb = tr_torrentStat(tor);
in = tr_torrentInfo(tor);
/* XXX should use model and update this window regularly */
sb = tr_torrent_stat(tor);
in = tr_torrent_info(tor);
str = g_strdup_printf(_("%s Properties"), in->name);
wind = gtk_dialog_new_with_buttons(str, parent,
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
@@ -474,7 +489,7 @@ makeinfowind(GtkWindow *parent, tr_torrent_t *tor) {
INFOSEP(table, ii);
INFOLINE(table, ii, _("Directory:"), tr_torrentGetFolder(tor));
INFOLINE(table, ii, _("Directory:"), tr_torrentGetFolder(tr_torrent_handle(tor)));
INFOLINEA(table, ii, _("Downloaded:"), readablesize(sb->downloaded));
INFOLINEA(table, ii, _("Uploaded:"), readablesize(sb->uploaded));

View File

@@ -27,7 +27,8 @@
#ifndef TG_PREFS_H
#define TG_PREFS_H
#include "transmission.h"
#include "tr_backend.h"
#include "tr_torrent.h"
#include "util.h"
/* macros for names of prefs we use */
@@ -45,19 +46,18 @@
#define DEF_USEUPLIMIT TRUE
void
makeprefwindow(GtkWindow *parent, tr_handle_t *tr, gboolean *opened);
makeprefwindow(GtkWindow *parent, TrBackend *back, gboolean *opened);
/* set the upload limit based on saved prefs */
void
setlimit(tr_handle_t *tr);
setlimit(TrBackend *back);
/* show the "add a torrent" dialog */
void
makeaddwind(GtkWindow *parent, add_torrent_func_t addfunc,
torrents_added_func_t donefunc, void *cbdata);
makeaddwind(GtkWindow *parent, add_torrents_func_t addfunc, void *cbdata);
/* show the info window for a torrent */
void
makeinfowind(GtkWindow *parent, tr_torrent_t *tor);
makeinfowind(GtkWindow *parent, TrTorrent *tor);
#endif /* TG_PREFS_H */

View File

@@ -37,7 +37,9 @@
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include "transmission.h"
#include "bencode.h"
#include "conf.h"
#include "io.h"
#include "ipc.h"
@@ -54,8 +56,7 @@ enum contype { CON_SERV, CON_ADDFILE };
struct constate_serv {
void *wind;
add_torrent_func_t addfunc;
torrents_added_func_t donefunc;
add_torrents_func_t addfunc;
void *cbdata;
};
@@ -82,8 +83,7 @@ struct constate {
};
void
ipc_socket_setup(void *parent, add_torrent_func_t addfunc,
torrents_added_func_t donefunc, void *cbdata);
ipc_socket_setup(void *parent, add_torrents_func_t addfunc, void *cbdata);
gboolean
ipc_sendfiles_blocking(GList *files);
static void
@@ -126,8 +126,7 @@ static const struct handlerdef gl_funcs_addfile[] = {
static char *gl_sockpath = NULL;
void
ipc_socket_setup(void *parent, add_torrent_func_t addfunc,
torrents_added_func_t donefunc, void *cbdata) {
ipc_socket_setup(void *parent, add_torrents_func_t addfunc, void *cbdata) {
struct constate *con;
con = g_new0(struct constate, 1);
@@ -137,7 +136,6 @@ ipc_socket_setup(void *parent, add_torrent_func_t addfunc,
con->type = CON_SERV;
con->u.serv.wind = parent;
con->u.serv.addfunc = addfunc;
con->u.serv.donefunc = donefunc;
con->u.serv.cbdata = cbdata;
serv_bind(con);
@@ -378,33 +376,18 @@ all_io_closed(GSource *source SHUTUP, void *vdata) {
static void
srv_addfile(struct constate *con, const char *name SHUTUP, benc_val_t *val) {
struct constate_serv *srv = &con->u.serv;
GList *errs = NULL;
char *str;
GList *files;
int ii;
gboolean added;
benc_val_t *file;
if(TYPE_LIST == val->type) {
added = FALSE;
for(ii = 0; ii < val->val.l.count; ii++) {
file = &val->val.l.vals[ii];
if(TYPE_STR == file->type && g_utf8_validate(file->val.s.s, -1, NULL)) {
/* XXX somehow escape invalid utf-8 */
added = TRUE;
srv->addfunc(srv->cbdata, file->val.s.s, NULL, FALSE, &errs);
}
}
if(NULL != errs) {
str = joinstrlist(errs, "\n");
errmsg(srv->wind, ngettext("Failed to load the torrent file %s",
"Failed to load the torrent files:\n%s",
g_list_length(errs)), str);
freestrlist(errs);
g_free(str);
}
if(added)
srv->donefunc(srv->cbdata);
files = NULL;
for(ii = 0; ii < val->val.l.count; ii++)
if(TYPE_STR == val->val.l.vals[ii].type &&
/* XXX somehow escape invalid utf-8 */
g_utf8_validate(val->val.l.vals[ii].val.s.s, -1, NULL))
files = g_list_append(files, val->val.l.vals[ii].val.s.s);
srv->addfunc(srv->cbdata, NULL, files, NULL, NULL);
g_list_free(files);
}
}

View File

@@ -30,8 +30,7 @@
#include "util.h"
void
ipc_socket_setup(void *wind, add_torrent_func_t addfunc,
torrents_added_func_t donefunc, void *cbdata);
ipc_socket_setup(void *wind, add_torrents_func_t addfunc, void *cbdata);
gboolean
ipc_sendfiles_blocking(GList *files);

File diff suppressed because it is too large Load Diff

238
gtk/tr_backend.c Normal file
View File

@@ -0,0 +1,238 @@
#include <string.h>
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#define TR_WANT_TORRENT_PRIVATE
#include "transmission.h"
#include "bencode.h"
#include "conf.h"
#include "tr_backend.h"
#include "tr_torrent.h"
#include "util.h"
/*
enum {
TR_BACKEND_HANDLE = 1,
};
*/
static void
tr_backend_init(GTypeInstance *instance, gpointer g_class);
static void
tr_backend_set_property(GObject *object, guint property_id,
const GValue *value, GParamSpec *pspec);
static void
tr_backend_get_property(GObject *object, guint property_id,
GValue *value, GParamSpec *pspec);
static void
tr_backend_class_init(gpointer g_class, gpointer g_class_data);
static void
tr_backend_dispose(GObject *obj);
static void
tr_backend_finalize(GObject *obj);
static void
tr_backend_torrent_finalized(gpointer gdata, GObject *tor);
GType
tr_backend_get_type(void) {
static GType type = 0;
if(0 == type) {
static const GTypeInfo info = {
sizeof (TrBackendClass),
NULL, /* base_init */
NULL, /* base_finalize */
tr_backend_class_init, /* class_init */
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (TrBackend),
0, /* n_preallocs */
tr_backend_init, /* instance_init */
NULL,
};
type = g_type_register_static(G_TYPE_OBJECT, "TrBackendType", &info, 0);
}
return type;
}
static void
tr_backend_class_init(gpointer g_class, gpointer g_class_data SHUTUP) {
GObjectClass *gobject_class = G_OBJECT_CLASS(g_class);
//GParamSpec *pspec;
gobject_class->set_property = tr_backend_set_property;
gobject_class->get_property = tr_backend_get_property;
gobject_class->dispose = tr_backend_dispose;
gobject_class->finalize = tr_backend_finalize;
/*
pspec = g_param_spec_pointer("backend-handle", _("Backend handle"),
_("Backend handle from libtransmission"),
G_PARAM_READWRITE);
g_object_class_install_property(gobject_class, TR_BACKEND_HANDLE, pspec);
*/
}
static void
tr_backend_init(GTypeInstance *instance, gpointer g_class SHUTUP) {
TrBackend *self = (TrBackend *)instance;
self->handle = tr_init();
self->disposed = FALSE;
}
static void
tr_backend_set_property(GObject *object, guint property_id,
const GValue *value SHUTUP, GParamSpec *pspec) {
TrBackend *self = (TrBackend*)object;
if(self->disposed)
return;
switch(property_id) {
/*
case TR_BACKEND_HANDLE:
g_assert(NULL == self->handle);
self->handle = g_value_get_pointer(value);
break;
*/
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
break;
}
}
static void
tr_backend_get_property(GObject *object, guint property_id,
GValue *value SHUTUP, GParamSpec *pspec) {
TrBackend *self = (TrBackend*)object;
if(self->disposed)
return;
switch(property_id) {
/*
case TR_BACKEND_HANDLE:
g_value_set_pointer(value, self->handle);
break;
*/
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
break;
}
}
static void
tr_backend_dispose(GObject *obj) {
GObjectClass *parent = g_type_class_peek(g_type_parent(TR_BACKEND_TYPE));
TrBackend *self = (TrBackend*)obj;
GList *ii;
if(self->disposed)
return;
self->disposed = TRUE;
fprintf(stderr, "back dispose %p\n", self);
if(NULL != self->torrents) {
for(ii = self->torrents; NULL != ii; ii = ii->next)
g_object_weak_unref(ii->data, tr_backend_torrent_finalized, self);
g_list_free(self->torrents);
self->torrents = NULL;
}
/* Chain up to the parent class */
parent->dispose(obj);
}
static void
tr_backend_finalize(GObject *obj) {
GObjectClass *parent = g_type_class_peek(g_type_parent(TR_BACKEND_TYPE));
TrBackend *self = (TrBackend *)obj;
fprintf(stderr, "back finalize %p\n", self);
if(NULL != self->handle)
tr_close(self->handle);
/* Chain up to the parent class */
parent->finalize(obj);
}
TrBackend *
tr_backend_new(void) {
return g_object_new(TR_BACKEND_TYPE, NULL);
}
tr_handle_t *
tr_backend_handle(TrBackend *back) {
TR_IS_BACKEND(back);
return back->handle;
}
void
tr_backend_save_state(TrBackend *back, char **errstr) {
benc_val_t state;
int ii;
GList *jj;
TR_IS_BACKEND(back);
bzero(&state, sizeof(state));
state.type = TYPE_LIST;
state.val.l.alloc = state.val.l.count = g_list_length(back->torrents);
state.val.l.vals = g_new0(benc_val_t, state.val.l.alloc);
for(ii = 0, jj = back->torrents; NULL != jj; ii++, jj = jj->next)
tr_torrent_get_state(jj->data, state.val.l.vals + ii);
cf_savestate(&state, errstr);
tr_bencFree(&state);
}
GList *
tr_backend_load_state(TrBackend *back, benc_val_t *state, GList **errors) {
GList *ret = NULL;
int ii;
TrTorrent *tor;
char *errstr;
TR_IS_BACKEND(back);
if(TYPE_LIST != state->type)
return NULL;
for(ii = 0; ii < state->val.l.count; ii++) {
errstr = NULL;
tor = tr_torrent_new_with_state(G_OBJECT(back), state->val.l.vals + ii,
&errstr);
if(NULL != errstr)
*errors = g_list_append(*errors, errstr);
if(NULL != tor)
ret = g_list_append(ret, tor);
}
return ret;
}
void
tr_backend_add_torrent(TrBackend *back, GObject *tor) {
TR_IS_BACKEND(back);
TR_IS_TORRENT(tor);
g_object_weak_ref(tor, tr_backend_torrent_finalized, back);
back->torrents = g_list_append(back->torrents, tor);
}
static void
tr_backend_torrent_finalized(gpointer gdata, GObject *tor) {
TrBackend *back = gdata;
TR_IS_BACKEND(back);
back->torrents = g_list_remove(back->torrents, tor);
}

56
gtk/tr_backend.h Normal file
View File

@@ -0,0 +1,56 @@
#ifndef TR_BACKEND_H
#define TR_BACKEND_H
#include <glib-object.h>
#include "transmission.h"
#include "bencode.h"
#define TR_BACKEND_TYPE (tr_backend_get_type ())
#define TR_BACKEND(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), TR_BACKEND_TYPE, TrBackend))
#define TR_BACKEND_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), TR_BACKEND_TYPE, TrBackendClass))
#define TR_IS_BACKEND(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), TR_BACKEND_TYPE))
#define TR_IS_BACKEND_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), TR_BACKEND_TYPE))
#define TR_BACKEND_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj), TR_BACKEND_TYPE, TrBackendClass))
typedef struct _TrBackend TrBackend;
typedef struct _TrBackendClass TrBackendClass;
/* treat the contents of this structure as private */
struct _TrBackend {
GObject parent;
tr_handle_t *handle;
GList *torrents;
gboolean disposed;
};
struct _TrBackendClass {
GObjectClass parent;
};
GType
tr_backend_get_type(void);
TrBackend *
tr_backend_new(void);
tr_handle_t *
tr_backend_handle(TrBackend *back);
void
tr_backend_save_state(TrBackend *back, char **errstr);
GList *
tr_backend_load_state(TrBackend *back, benc_val_t *state, GList **errors);
#ifdef TR_WANT_BACKEND_PRIVATE
void
tr_backend_add_torrent(TrBackend *back, GObject *tor);
#endif
#endif

View File

@@ -28,7 +28,7 @@
#include <gtk/gtk.h>
#include "trcellrenderertorrent.h"
#include "tr_cell_renderer_torrent.h"
#include "util.h"
enum { PROP_0, PROP_VALUE, PROP_TEXT, PROP_LABEL };

368
gtk/tr_torrent.c Normal file
View File

@@ -0,0 +1,368 @@
#include <string.h>
#include <unistd.h>
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#define TR_WANT_BACKEND_PRIVATE
#include "transmission.h"
#include "bencode.h"
#include "tr_backend.h"
#include "tr_torrent.h"
#include "util.h"
enum {
TR_TORRENT_HANDLE = 1,
TR_TORRENT_BACKEND,
TR_TORRENT_DIR,
TR_TORRENT_PAUSED,
};
static void
tr_torrent_init(GTypeInstance *instance, gpointer g_class);
static void
tr_torrent_set_property(GObject *object, guint property_id,
const GValue *value, GParamSpec *pspec);
static void
tr_torrent_get_property(GObject *object, guint property_id,
GValue *value, GParamSpec *pspec);
static void
tr_torrent_class_init(gpointer g_class, gpointer g_class_data);
static void
tr_torrent_dispose(GObject *obj);
static void
tr_torrent_finalize(GObject *obj);
static void
tr_torrent_set_folder(TrTorrent *tor);
static gboolean
tr_torrent_paused(TrTorrent *tor);
GType
tr_torrent_get_type(void) {
static GType type = 0;
if(0 == type) {
static const GTypeInfo info = {
sizeof (TrTorrentClass),
NULL, /* base_init */
NULL, /* base_finalize */
tr_torrent_class_init, /* class_init */
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (TrTorrent),
0, /* n_preallocs */
tr_torrent_init, /* instance_init */
NULL,
};
type = g_type_register_static(G_TYPE_OBJECT, "TrTorrentType", &info, 0);
}
return type;
}
static void
tr_torrent_class_init(gpointer g_class, gpointer g_class_data SHUTUP) {
GObjectClass *gobject_class = G_OBJECT_CLASS(g_class);
//TrTorrentClass *klass = TR_TORRENT_CLASS(g_class);
GParamSpec *pspec;
gobject_class->set_property = tr_torrent_set_property;
gobject_class->get_property = tr_torrent_get_property;
gobject_class->dispose = tr_torrent_dispose;
gobject_class->finalize = tr_torrent_finalize;
pspec = g_param_spec_pointer("torrent-handle", "Torrent handle",
"Torrent handle from libtransmission",
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
g_object_class_install_property(gobject_class, TR_TORRENT_HANDLE, pspec);
pspec = g_param_spec_object("backend", "Backend",
"Libtransmission backend object",
TR_BACKEND_TYPE,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
g_object_class_install_property(gobject_class, TR_TORRENT_BACKEND, pspec);
pspec = g_param_spec_string("download-directory", "Download directory",
"Directory to download files to", NULL,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
g_object_class_install_property(gobject_class, TR_TORRENT_DIR, pspec);
pspec = g_param_spec_boolean("paused", "Paused",
"Is the torrent paused or running", TRUE,
G_PARAM_READWRITE);
g_object_class_install_property(gobject_class, TR_TORRENT_PAUSED, pspec);
}
static void
tr_torrent_init(GTypeInstance *instance, gpointer g_class SHUTUP) {
TrTorrent *self = (TrTorrent *)instance;
self->handle = NULL;
self->back = NULL;
self->dir = NULL;
self->disposed = FALSE;
}
static void
tr_torrent_set_property(GObject *object, guint property_id,
const GValue *value, GParamSpec *pspec) {
TrTorrent *self = (TrTorrent*)object;
if(self->disposed)
return;
switch(property_id) {
case TR_TORRENT_HANDLE:
g_assert(NULL == self->handle);
self->handle = g_value_get_pointer(value);
if(NULL != self->handle && NULL != self->dir)
tr_torrent_set_folder(self);
break;
case TR_TORRENT_BACKEND:
g_assert(NULL == self->back);
self->back = g_object_ref(g_value_get_object(value));
break;
case TR_TORRENT_DIR:
g_assert(NULL == self->dir);
self->dir = g_value_dup_string(value);
if(NULL != self->handle && NULL != self->dir)
tr_torrent_set_folder(self);
break;
case TR_TORRENT_PAUSED:
g_assert(NULL != self->handle);
if(tr_torrent_paused(self) != g_value_get_boolean(value))
(g_value_get_boolean(value) ? tr_torrentStop : tr_torrentStart)
(self->handle);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
break;
}
}
static void
tr_torrent_get_property(GObject *object, guint property_id,
GValue *value, GParamSpec *pspec) {
TrTorrent *self = (TrTorrent*)object;
if(self->disposed)
return;
switch(property_id) {
case TR_TORRENT_HANDLE:
g_value_set_pointer(value, self->handle);
break;
case TR_TORRENT_BACKEND:
g_value_set_object(value, self->back);
break;
case TR_TORRENT_DIR:
g_value_set_string(value, (NULL != self->dir ? self->dir :
tr_torrentGetFolder(self->handle)));
break;
case TR_TORRENT_PAUSED:
g_value_set_boolean(value, tr_torrent_paused(self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
break;
}
}
static void
tr_torrent_dispose(GObject *obj) {
GObjectClass *parent = g_type_class_peek(g_type_parent(TR_TORRENT_TYPE));
TrTorrent *self = (TrTorrent*)obj;
if(self->disposed)
return;
self->disposed = TRUE;
fprintf(stderr, "tor dispose %p\n", self);
if(NULL != self->handle) {
if(!tr_torrent_paused(self))
tr_torrentStop(self->handle);
tr_torrentClose(tr_backend_handle(TR_BACKEND(self->back)), self->handle);
self->handle = NULL;
}
if(NULL != self->back) {
g_object_unref(self->back);
self->back = NULL;
}
/* Chain up to the parent class */
parent->dispose(obj);
}
static void
tr_torrent_finalize(GObject *obj) {
GObjectClass *parent = g_type_class_peek(g_type_parent(TR_TORRENT_TYPE));
TrTorrent *self = (TrTorrent *)obj;
fprintf(stderr, "tor finalize %p\n", self);
/* Chain up to the parent class */
parent->finalize(obj);
}
tr_torrent_t *
tr_torrent_handle(TrTorrent *tor) {
TR_IS_TORRENT(tor);
if(tor->disposed)
return NULL;
return tor->handle;
}
tr_stat_t *
tr_torrent_stat(TrTorrent *tor) {
TR_IS_TORRENT(tor);
if(tor->disposed)
return NULL;
return tr_torrentStat(tor->handle);
}
tr_info_t *
tr_torrent_info(TrTorrent *tor) {
TR_IS_TORRENT(tor);
if(tor->disposed)
return NULL;
return tr_torrentInfo(tor->handle);
}
TrTorrent *
tr_torrent_new(GObject *backend, const char *torrent, const char *dir,
gboolean *paused, char **err) {
TrTorrent *ret;
tr_torrent_t *handle;
int errcode;
TR_IS_BACKEND(backend);
g_assert(NULL != dir);
*err = NULL;
errcode = -1;
handle = tr_torrentInit(tr_backend_handle(TR_BACKEND(backend)),
torrent, &errcode);
if(NULL == handle) {
switch(errcode) {
case TR_EINVALID:
*err = g_strdup_printf(_("%s: not a valid torrent file"), torrent);
break;
case TR_EDUPLICATE:
*err = g_strdup_printf(_("%s: torrent is already open"), torrent);
break;
default:
*err = g_strdup(torrent);
break;
}
return NULL;
}
ret = g_object_new(TR_TORRENT_TYPE, "torrent-handle", handle,
"backend", backend, "download-directory", dir, NULL);
tr_backend_add_torrent(TR_BACKEND(backend), G_OBJECT(ret));
g_object_set(ret, "paused", (NULL == paused ? FALSE : *paused), NULL);
return ret;
}
TrTorrent *
tr_torrent_new_with_state(GObject *backend, benc_val_t *state, char **err) {
int ii;
benc_val_t *name, *data;
char *torrent, *dir;
gboolean hadpaused, paused;
*err = NULL;
if(TYPE_DICT != state->type)
return NULL;
torrent = dir = NULL;
hadpaused = FALSE;
for(ii = 0; ii + 1 < state->val.l.count; ii += 2) {
name = state->val.l.vals + ii;
data = state->val.l.vals + ii + 1;
if(TYPE_STR == name->type &&
(TYPE_STR == data->type || TYPE_INT == data->type)) {
if(0 == strcmp("torrent", name->val.s.s))
torrent = data->val.s.s;
else if(0 == strcmp("dir", name->val.s.s))
dir = data->val.s.s;
else if(0 == strcmp("paused", name->val.s.s)) {
hadpaused = TRUE;
paused = (data->val.i ? TRUE : FALSE);
}
}
}
if(NULL == torrent || NULL == dir)
return NULL;
return tr_torrent_new(backend, torrent, dir,
(hadpaused ? &paused : NULL), err);
}
void
tr_torrent_get_state(TrTorrent *tor, benc_val_t *state) {
tr_info_t *in = tr_torrentInfo(tor->handle);
const char *strs[] = {
"torrent", in->torrent, "dir", tr_torrentGetFolder(tor->handle), "paused",
};
unsigned int ii;
const unsigned int len = 6;
TR_IS_TORRENT(tor);
if(tor->disposed)
return;
state->type = TYPE_DICT;
state->val.l.vals = g_new0(benc_val_t, len);
state->val.l.alloc = state->val.l.count = len;
g_assert(len > ALEN(strs));
for(ii = 0; ii < ALEN(strs); ii++) {
state->val.l.vals[ii].type = TYPE_STR;
state->val.l.vals[ii].val.s.s = g_strdup(strs[ii]);
state->val.l.vals[ii].val.s.i = strlen(strs[ii]);
}
state->val.l.vals[ii].type = TYPE_INT;
state->val.l.vals[ii].val.i = tr_torrent_paused(tor);
ii++;
g_assert(len == ii);
}
static void
tr_torrent_set_folder(TrTorrent *tor) {
char *wd;
if(NULL != tor->dir)
tr_torrentSetFolder(tor->handle, tor->dir);
else {
wd = g_new(char, MAX_PATH_LENGTH + 1);
tr_torrentSetFolder(tor->handle,
(NULL == getcwd(wd, MAX_PATH_LENGTH + 1) ? "." : wd));
g_free(wd);
}
}
static gboolean
tr_torrent_paused(TrTorrent *tor) {
tr_stat_t *st = tr_torrentStat(tor->handle);
return (TR_STATUS_INACTIVE & st->status ? TRUE : FALSE);
}

61
gtk/tr_torrent.h Normal file
View File

@@ -0,0 +1,61 @@
#ifndef TR_TORRENT_H
#define TR_TORRENT_H
#include <glib-object.h>
#include "transmission.h"
#include "bencode.h"
#define TR_TORRENT_TYPE (tr_torrent_get_type ())
#define TR_TORRENT(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), TR_TORRENT_TYPE, TrTorrent))
#define TR_TORRENT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), TR_TORRENT_TYPE, TrTorrentClass))
#define TR_IS_TORRENT(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), TR_TORRENT_TYPE))
#define TR_IS_TORRENT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), TR_TORRENT_TYPE))
#define TR_TORRENT_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj), TR_TORRENT_TYPE, TrTorrentClass))
typedef struct _TrTorrent TrTorrent;
typedef struct _TrTorrentClass TrTorrentClass;
/* treat the contents of this structure as private */
struct _TrTorrent {
GObject parent;
tr_torrent_t *handle;
GObject *back;
char *dir;
gboolean disposed;
};
struct _TrTorrentClass {
GObjectClass parent;
};
GType
tr_torrent_get_type(void);
tr_torrent_t *
tr_torrent_handle(TrTorrent *tor);
tr_stat_t *
tr_torrent_stat(TrTorrent *tor);
tr_info_t *
tr_torrent_info(TrTorrent *tor);
TrTorrent *
tr_torrent_new(GObject *backend, const char *torrent, const char *dir,
gboolean *paused, char **err);
TrTorrent *
tr_torrent_new_with_state(GObject *backend, benc_val_t *state, char **err);
#ifdef TR_WANT_TORRENT_PRIVATE
void
tr_torrent_get_state(TrTorrent *tor, benc_val_t *state);
#endif
#endif

View File

@@ -37,8 +37,7 @@
#define SHUTUP
#endif
typedef gboolean (*add_torrent_func_t)(void *, const char *, const char *, gboolean, GList **);
typedef void (*torrents_added_func_t)(void *);
typedef void (*add_torrents_func_t)(void*,void*,GList*,const char*,gboolean*);
/* return number of items in array */
#define ALEN(a) (sizeof(a) / sizeof((a)[0]))