source: trunk/gtk/tr_torrent.c @ 248

Last change on this file since 248 was 248, checked in by joshe, 16 years ago

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.

File size: 9.6 KB
Line 
1#include <string.h>
2#include <unistd.h>
3
4#include <gtk/gtk.h>
5#include <glib/gi18n.h>
6
7#define TR_WANT_BACKEND_PRIVATE
8
9#include "transmission.h"
10#include "bencode.h"
11
12#include "tr_backend.h"
13#include "tr_torrent.h"
14#include "util.h"
15
16enum {
17  TR_TORRENT_HANDLE = 1,
18  TR_TORRENT_BACKEND,
19  TR_TORRENT_DIR,
20  TR_TORRENT_PAUSED,
21};
22
23static void
24tr_torrent_init(GTypeInstance *instance, gpointer g_class);
25static void
26tr_torrent_set_property(GObject *object, guint property_id,
27                        const GValue *value, GParamSpec *pspec);
28static void
29tr_torrent_get_property(GObject *object, guint property_id,
30                        GValue *value, GParamSpec *pspec);
31static void
32tr_torrent_class_init(gpointer g_class, gpointer g_class_data);
33static void
34tr_torrent_dispose(GObject *obj);
35static void
36tr_torrent_finalize(GObject *obj);
37static void
38tr_torrent_set_folder(TrTorrent *tor);
39static gboolean
40tr_torrent_paused(TrTorrent *tor);
41
42GType
43tr_torrent_get_type(void) {
44  static GType type = 0;
45
46  if(0 == type) {
47    static const GTypeInfo info = {
48      sizeof (TrTorrentClass),
49      NULL,   /* base_init */
50      NULL,   /* base_finalize */
51      tr_torrent_class_init,   /* class_init */
52      NULL,   /* class_finalize */
53      NULL,   /* class_data */
54      sizeof (TrTorrent),
55      0,      /* n_preallocs */
56      tr_torrent_init, /* instance_init */
57      NULL,
58    };
59    type = g_type_register_static(G_TYPE_OBJECT, "TrTorrentType", &info, 0);
60  }
61  return type;
62}
63
64static void
65tr_torrent_class_init(gpointer g_class, gpointer g_class_data SHUTUP) {
66  GObjectClass *gobject_class = G_OBJECT_CLASS(g_class);
67  //TrTorrentClass *klass = TR_TORRENT_CLASS(g_class);
68  GParamSpec *pspec;
69
70  gobject_class->set_property = tr_torrent_set_property;
71  gobject_class->get_property = tr_torrent_get_property;
72  gobject_class->dispose = tr_torrent_dispose;
73  gobject_class->finalize = tr_torrent_finalize;
74
75  pspec = g_param_spec_pointer("torrent-handle", "Torrent handle",
76                               "Torrent handle from libtransmission",
77                               G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
78  g_object_class_install_property(gobject_class, TR_TORRENT_HANDLE, pspec);
79
80  pspec = g_param_spec_object("backend", "Backend",
81                              "Libtransmission backend object",
82                              TR_BACKEND_TYPE,
83                              G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
84  g_object_class_install_property(gobject_class, TR_TORRENT_BACKEND, pspec);
85
86  pspec = g_param_spec_string("download-directory", "Download directory",
87                              "Directory to download files to", NULL,
88                              G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
89  g_object_class_install_property(gobject_class, TR_TORRENT_DIR, pspec);
90
91  pspec = g_param_spec_boolean("paused", "Paused",
92                               "Is the torrent paused or running", TRUE,
93                               G_PARAM_READWRITE);
94  g_object_class_install_property(gobject_class, TR_TORRENT_PAUSED, pspec);
95}
96
97static void
98tr_torrent_init(GTypeInstance *instance, gpointer g_class SHUTUP) {
99  TrTorrent *self = (TrTorrent *)instance;
100
101  self->handle = NULL;
102  self->back = NULL;
103  self->dir = NULL;
104  self->disposed = FALSE;
105}
106
107static void
108tr_torrent_set_property(GObject *object, guint property_id,
109                        const GValue *value, GParamSpec *pspec) {
110  TrTorrent *self = (TrTorrent*)object;
111
112  if(self->disposed)
113    return;
114
115  switch(property_id) {
116    case TR_TORRENT_HANDLE:
117      g_assert(NULL == self->handle);
118      self->handle = g_value_get_pointer(value);
119      if(NULL != self->handle && NULL != self->dir)
120        tr_torrent_set_folder(self);
121      break;
122    case TR_TORRENT_BACKEND:
123      g_assert(NULL == self->back);
124      self->back = g_object_ref(g_value_get_object(value));
125      break;
126    case TR_TORRENT_DIR:
127      g_assert(NULL == self->dir);
128      self->dir = g_value_dup_string(value);
129      if(NULL != self->handle && NULL != self->dir)
130        tr_torrent_set_folder(self);
131      break;
132    case TR_TORRENT_PAUSED:
133      g_assert(NULL != self->handle);
134      if(tr_torrent_paused(self) != g_value_get_boolean(value))
135        (g_value_get_boolean(value) ? tr_torrentStop : tr_torrentStart)
136          (self->handle);
137      break;
138    default:
139      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
140      break;
141  }
142}
143
144static void
145tr_torrent_get_property(GObject *object, guint property_id,
146                        GValue *value, GParamSpec *pspec) {
147  TrTorrent *self = (TrTorrent*)object;
148
149  if(self->disposed)
150    return;
151
152  switch(property_id) {
153    case TR_TORRENT_HANDLE:
154      g_value_set_pointer(value, self->handle);
155      break;
156    case TR_TORRENT_BACKEND:
157      g_value_set_object(value, self->back);
158      break;
159    case TR_TORRENT_DIR:
160      g_value_set_string(value, (NULL != self->dir ? self->dir :
161                                 tr_torrentGetFolder(self->handle)));
162      break;
163    case TR_TORRENT_PAUSED:
164      g_value_set_boolean(value, tr_torrent_paused(self));
165      break;
166    default:
167      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
168      break;
169  }
170}
171
172static void
173tr_torrent_dispose(GObject *obj) {
174  GObjectClass *parent = g_type_class_peek(g_type_parent(TR_TORRENT_TYPE));
175  TrTorrent *self = (TrTorrent*)obj;
176
177  if(self->disposed)
178    return;
179  self->disposed = TRUE;
180
181  fprintf(stderr, "tor dispose %p\n", self);
182
183  if(NULL != self->handle) {
184    if(!tr_torrent_paused(self))
185      tr_torrentStop(self->handle);
186    tr_torrentClose(tr_backend_handle(TR_BACKEND(self->back)), self->handle);
187    self->handle = NULL;
188  }
189
190  if(NULL != self->back) {
191    g_object_unref(self->back);
192    self->back = NULL;
193  }
194
195  /* Chain up to the parent class */
196  parent->dispose(obj);
197}
198
199static void
200tr_torrent_finalize(GObject *obj) {
201  GObjectClass *parent = g_type_class_peek(g_type_parent(TR_TORRENT_TYPE));
202  TrTorrent *self = (TrTorrent *)obj;
203
204  fprintf(stderr, "tor finalize %p\n", self);
205
206  /* Chain up to the parent class */
207  parent->finalize(obj);
208}
209
210tr_torrent_t *
211tr_torrent_handle(TrTorrent *tor) {
212  TR_IS_TORRENT(tor);
213
214  if(tor->disposed)
215    return NULL;
216
217  return tor->handle;
218}
219
220tr_stat_t *
221tr_torrent_stat(TrTorrent *tor) {
222  TR_IS_TORRENT(tor);
223
224  if(tor->disposed)
225    return NULL;
226
227  return tr_torrentStat(tor->handle);
228}
229
230tr_info_t *
231tr_torrent_info(TrTorrent *tor) {
232  TR_IS_TORRENT(tor);
233
234  if(tor->disposed)
235    return NULL;
236
237  return tr_torrentInfo(tor->handle);
238}
239
240TrTorrent *
241tr_torrent_new(GObject *backend, const char *torrent, const char *dir,
242               gboolean *paused, char **err) {
243  TrTorrent *ret;
244  tr_torrent_t *handle;
245  int errcode;
246
247  TR_IS_BACKEND(backend);
248  g_assert(NULL != dir);
249
250  *err = NULL;
251
252  errcode = -1;
253  handle = tr_torrentInit(tr_backend_handle(TR_BACKEND(backend)),
254                          torrent, &errcode);
255  if(NULL == handle) {
256    switch(errcode) {
257      case TR_EINVALID:
258        *err = g_strdup_printf(_("%s: not a valid torrent file"), torrent);
259        break;
260      case TR_EDUPLICATE:
261        *err = g_strdup_printf(_("%s: torrent is already open"), torrent);
262        break;
263      default:
264        *err = g_strdup(torrent);
265        break;
266    }
267    return NULL;
268  }
269
270  ret = g_object_new(TR_TORRENT_TYPE, "torrent-handle", handle,
271                     "backend", backend, "download-directory", dir, NULL);
272  tr_backend_add_torrent(TR_BACKEND(backend), G_OBJECT(ret));
273
274  g_object_set(ret, "paused", (NULL == paused ? FALSE : *paused), NULL);
275
276  return ret;
277}
278
279TrTorrent *
280tr_torrent_new_with_state(GObject *backend, benc_val_t *state, char **err) {
281  int ii;
282  benc_val_t *name, *data;
283  char *torrent, *dir;
284  gboolean hadpaused, paused;
285
286  *err = NULL;
287
288  if(TYPE_DICT != state->type)
289    return NULL;
290
291  torrent = dir = NULL;
292  hadpaused = FALSE;
293
294  for(ii = 0; ii + 1 < state->val.l.count; ii += 2) {
295    name = state->val.l.vals + ii;
296    data = state->val.l.vals + ii + 1;
297    if(TYPE_STR == name->type &&
298       (TYPE_STR == data->type || TYPE_INT == data->type)) {
299      if(0 == strcmp("torrent", name->val.s.s))
300        torrent = data->val.s.s;
301      else if(0 == strcmp("dir", name->val.s.s))
302        dir = data->val.s.s;
303      else if(0 == strcmp("paused", name->val.s.s)) {
304        hadpaused = TRUE;
305        paused = (data->val.i ? TRUE : FALSE);
306      }
307    }
308  }
309
310  if(NULL == torrent || NULL == dir)
311    return NULL;
312
313  return tr_torrent_new(backend, torrent, dir,
314                        (hadpaused ? &paused : NULL), err);
315}
316
317void
318tr_torrent_get_state(TrTorrent *tor, benc_val_t *state) {
319  tr_info_t *in = tr_torrentInfo(tor->handle);
320  const char *strs[] = {
321    "torrent", in->torrent, "dir", tr_torrentGetFolder(tor->handle), "paused", 
322  };
323  unsigned int ii;
324  const unsigned int len = 6;
325
326  TR_IS_TORRENT(tor);
327
328  if(tor->disposed)
329    return;
330
331  state->type = TYPE_DICT;
332  state->val.l.vals = g_new0(benc_val_t, len);
333  state->val.l.alloc = state->val.l.count = len;
334
335  g_assert(len > ALEN(strs));
336  for(ii = 0; ii < ALEN(strs); ii++) {
337    state->val.l.vals[ii].type = TYPE_STR;
338    state->val.l.vals[ii].val.s.s = g_strdup(strs[ii]);
339    state->val.l.vals[ii].val.s.i = strlen(strs[ii]);
340  }
341
342  state->val.l.vals[ii].type = TYPE_INT;
343  state->val.l.vals[ii].val.i = tr_torrent_paused(tor);
344  ii++;
345
346  g_assert(len == ii);
347}
348
349static void
350tr_torrent_set_folder(TrTorrent *tor) {
351  char *wd;
352
353  if(NULL != tor->dir)
354    tr_torrentSetFolder(tor->handle, tor->dir);
355  else {
356    wd = g_new(char, MAX_PATH_LENGTH + 1);
357    tr_torrentSetFolder(tor->handle,
358                        (NULL == getcwd(wd, MAX_PATH_LENGTH + 1) ? "." : wd));
359    g_free(wd);
360  }
361}
362
363static gboolean
364tr_torrent_paused(TrTorrent *tor) {
365  tr_stat_t *st = tr_torrentStat(tor->handle);
366
367  return (TR_STATUS_INACTIVE & st->status ? TRUE : FALSE);
368}
Note: See TracBrowser for help on using the repository browser.