Changeset 162


Ignore:
Timestamp:
Mar 23, 2006, 12:39:39 PM (16 years ago)
Author:
titer
Message:

Merge from branches/new_api:r161

Location:
trunk
Files:
5 deleted
26 edited
4 copied

Legend:

Unmodified
Added
Removed
  • trunk/gtk/conf.c

    r91 r162  
    415415
    416416gboolean
    417 cf_savestate(int count, tr_stat_t *torrents, char **errstr) {
     417cf_savestate(GList *torrents, char **errstr) {
    418418  char *file = g_build_filename(confdir, FILE_STATE, NULL);
    419419  char *tmpfile = g_build_filename(confdir, FILE_STATE_TMP, NULL);
    420420  GIOChannel *io = NULL;
    421421  GError *err;
    422   int fd, ii;
     422  int fd;
    423423  char *torrentfile, *torrentdir, *line;
    424424  gsize written;
    425425  gboolean paused;
    426426  GIOStatus res;
     427  tr_stat_t *sb;
     428  tr_info_t *in;
    427429
    428430  *errstr = NULL;
     
    445447
    446448  err = NULL;
    447   for(ii = 0; ii < count; ii++) {
    448     /* XXX need a better way to query running/stopped state */
    449     paused = ((TR_STATUS_STOPPING | TR_STATUS_PAUSE) & torrents[ii].status);
    450     torrentfile = g_strescape(torrents[ii].info.torrent, "");
    451     torrentdir = g_strescape(torrents[ii].folder, "");
     449  while(NULL != torrents) {
     450    sb = tr_torrentStat(torrents->data);
     451    in = tr_torrentInfo(torrents->data);
     452    paused = (TR_STATUS_INACTIVE & sb->status);
     453    torrentfile = g_strescape(in->torrent, "");
     454    torrentdir = g_strescape(tr_torrentGetFolder(torrents->data), "");
    452455    /* g_strcompress */
    453456    line = g_strdup_printf("torrent=\"%s\" dir=\"%s\" paused=\"%s\"%c",
     
    467470        goto done;
    468471    }
     472    torrents = torrents->next;
    469473  }
    470474  if(NULL != err ||
  • trunk/gtk/conf.h

    r51 r162  
    5151cf_loadstate(char **errstr);
    5252gboolean
    53 cf_savestate(int count, tr_stat_t *torrents, char **errstr);
     53cf_savestate(GList *torrents, char **errstr);
    5454void
    5555cf_freestate(struct cf_torrentstate *state);
  • trunk/gtk/dialogs.c

    r131 r162  
    5050  add_torrent_func_t addfunc;
    5151  GtkWindow *parent;
    52   tr_handle_t *tr;
    5352  torrents_added_func_t donefunc;
    54   void *donedata;
     53  void *data;
    5554  gboolean autostart;
    5655  gboolean usingaltdir;
     
    220219    }
    221220
     221    /* XXX would be nice to have errno strings, are they printed to stdout? */
    222222    tr_setBindPort(data->tr, gtk_spin_button_get_value_as_int(data->port));
    223223    setlimit(data->tr);
     
    241241
    242242void
    243 makeaddwind(add_torrent_func_t addfunc, GtkWindow *parent, tr_handle_t *tr,
    244             torrents_added_func_t donefunc, void *donedata) {
     243makeaddwind(GtkWindow *parent, add_torrent_func_t addfunc,
     244            torrents_added_func_t donefunc, void *cbdata) {
    245245  GtkWidget *wind = gtk_file_chooser_dialog_new(_("Add a Torrent"), parent,
    246246    GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
     
    261261  data->addfunc = addfunc;
    262262  data->parent = parent;
    263   data->tr = tr;
    264263  data->donefunc = donefunc;
    265   data->donedata = donedata;
     264  data->data = cbdata;
    266265  data->autostart = TRUE;
    267266  data->usingaltdir = FALSE;
     
    327326    files = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(widget));
    328327    for(ii = files; NULL != ii; ii = ii->next)
    329       if(data->addfunc(data->tr, data->parent, ii->data, dir,
     328      if(data->addfunc(data->data, ii->data, dir,
     329                       /* XXX need to group errors here */
    330330                       !data->autostart, NULL))
    331331        added = TRUE;
    332332    if(added)
    333       data->donefunc(data->donedata);
     333      data->donefunc(data->data);
    334334    if(NULL != dir)
    335335      g_free(dir);
     
    376376
    377377void
    378 makeinfowind(GtkWindow *parent, tr_handle_t *tr, int id) {
     378makeinfowind(GtkWindow *parent, tr_torrent_t *tor) {
    379379  tr_stat_t *sb;
     380  tr_info_t *in;
    380381  GtkWidget *wind, *label;
    381382  int ii;
     
    384385  GtkWidget *table = gtk_table_new(rowcount, 2, FALSE);
    385386
    386   /* XXX would be nice to be able to stat just one */
    387   if(id >= tr_torrentStat(tr, &sb))
    388     assert(!"XXX ");
    389   str = g_strdup_printf(_("%s Properties"), sb[id].info.name);
     387  sb = tr_torrentStat(tor);
     388  in = tr_torrentInfo(tor);
     389  str = g_strdup_printf(_("%s Properties"), in->name);
    390390  wind = gtk_dialog_new_with_buttons(str, parent,
    391391    GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
     
    402402  label = gtk_label_new(NULL);
    403403  gtk_label_set_selectable(GTK_LABEL(label), TRUE);
    404   str = g_markup_printf_escaped("<big>%s</big>", sb[id].info.name);
     404  str = g_markup_printf_escaped("<big>%s</big>", in->name);
    405405  gtk_label_set_markup(GTK_LABEL(label), str);
    406406  g_free(str);
     
    411411  INFOSEP(table, ii);
    412412
    413   if(80 == sb[id].info.trackerPort)
     413  if(80 == in->trackerPort)
    414414    INFOLINEA(table, ii, _("Tracker:"), g_strdup_printf("http://%s",
    415               sb[id].info.trackerAddress));
     415              in->trackerAddress));
    416416  else
    417417    INFOLINEA(table, ii, _("Tracker:"), g_strdup_printf("http://%s:%i",
    418               sb[id].info.trackerAddress, sb[id].info.trackerPort));
    419   INFOLINE(table, ii, _("Announce:"), sb[id].info.trackerAnnounce);
    420   INFOLINEA(table, ii, _("Piece Size:"), readablesize(sb[id].info.pieceSize));
    421   INFOLINEF(table, ii, "%i", _("Pieces:"), sb[id].info.pieceCount);
    422   INFOLINEA(table, ii, _("Total Size:"), readablesize(sb[id].info.totalSize));
    423   if(0 > sb[id].seeders)
     418              in->trackerAddress, in->trackerPort));
     419  INFOLINE(table, ii, _("Announce:"), in->trackerAnnounce);
     420  INFOLINEA(table, ii, _("Piece Size:"), readablesize(in->pieceSize));
     421  INFOLINEF(table, ii, "%i", _("Pieces:"), in->pieceCount);
     422  INFOLINEA(table, ii, _("Total Size:"), readablesize(in->totalSize));
     423  if(0 > sb->seeders)
    424424    INFOLINE(table, ii, _("Seeders:"), _("?"));
    425425  else
    426     INFOLINEF(table, ii, "%i", _("Seeders:"), sb[id].seeders);
    427   if(0 > sb[id].leechers)
     426    INFOLINEF(table, ii, "%i", _("Seeders:"), sb->seeders);
     427  if(0 > sb->leechers)
    428428    INFOLINE(table, ii, _("Leechers:"), _("?"));
    429429  else
    430     INFOLINEF(table, ii, "%i", _("Leechers:"), sb[id].leechers);
     430    INFOLINEF(table, ii, "%i", _("Leechers:"), sb->leechers);
    431431
    432432  INFOSEP(table, ii);
    433433
    434   INFOLINE(table, ii, _("Directory:"), sb[id].folder);
    435   INFOLINEA(table, ii, _("Downloaded:"), readablesize(sb[id].downloaded));
    436   INFOLINEA(table, ii, _("Uploaded:"), readablesize(sb[id].uploaded));
     434  INFOLINE(table, ii, _("Directory:"), tr_torrentGetFolder(tor));
     435  INFOLINEA(table, ii, _("Downloaded:"), readablesize(sb->downloaded));
     436  INFOLINEA(table, ii, _("Uploaded:"), readablesize(sb->uploaded));
    437437
    438438  INFOSEP(table, ii);
     
    444444                   G_CALLBACK(gtk_widget_destroy), NULL);
    445445  gtk_widget_show_all(wind);
    446   free(sb);
    447 }
     446}
  • trunk/gtk/dialogs.h

    r125 r162  
    3838#define DEFAULT_UPLIMIT         20
    3939
    40 typedef gboolean (*add_torrent_func_t)(tr_handle_t*, GtkWindow*, const char*, const char *, gboolean, GList **);
     40typedef gboolean (*add_torrent_func_t)(void *, const char *, const char *, gboolean, GList **);
    4141typedef void (*torrents_added_func_t)(void *);
    4242
     
    5050/* show the "add a torrent" dialog */
    5151void
    52 makeaddwind(add_torrent_func_t addfunc, GtkWindow *parent, tr_handle_t *tr,
    53             torrents_added_func_t donefunc, void *donedata);
     52makeaddwind(GtkWindow *parent, add_torrent_func_t addfunc,
     53            torrents_added_func_t donefunc, void *cbdata);
    5454
    5555/* show the info window for a torrent */
    5656void
    57 makeinfowind(GtkWindow *parent, tr_handle_t *tr, int index);
     57makeinfowind(GtkWindow *parent, tr_torrent_t *tor);
    5858
    5959#endif /* TG_PREFS_H */
  • trunk/gtk/main.c

    r131 r162  
    2828#include <assert.h>
    2929#include <errno.h>
     30#include <signal.h>
    3031#include <string.h>
    3132#include <stdio.h>
     
    5152  tr_handle_t *tr;
    5253  GtkWindow *wind;
    53   GtkListStore *model;
     54  GtkTreeModel *model;
    5455  GtkTreeView *view;
    5556  GtkStatusbar *bar;
     
    7879void
    7980makewind(GtkWidget *wind, tr_handle_t *tr, GList *saved);
    80 gboolean
    81 winclose(GtkWidget *widget, GdkEvent *event, gpointer gdata);
    82 gboolean
    83 exitcheck(gpointer gdata);
    84 void
    85 stoptransmission(void *tr);
    86 void
    87 setupdrag(GtkWidget *widget, struct cbdata *data);
    88 void
    89 gotdrag(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
    90         GtkSelectionData *sel, guint info, guint time, gpointer gdata);
    9181GtkWidget *
    9282makewind_toolbar(struct cbdata *data);
    9383GtkWidget *
    9484makewind_list(struct cbdata *data);
     85gboolean
     86winclose(GtkWidget *widget, GdkEvent *event, gpointer gdata);
     87gboolean
     88exitcheck(gpointer gdata);
     89void
     90stoptransmission(tr_handle_t *tr);
     91void
     92setupdrag(GtkWidget *widget, struct cbdata *data);
     93void
     94gotdrag(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
     95        GtkSelectionData *sel, guint info, guint time, gpointer gdata);
    9596static void
    9697stylekludge(GObject *obj, GParamSpec *spec, gpointer gdata);
     
    114115            GList *ids, int status);
    115116void
    116 killmenu(GtkWidget *menu, gpointer *gdata SHUTUP);
     117killmenu(GtkWidget *menu, gpointer *gdata);
    117118void
    118119actionclick(GtkWidget *widget, gpointer gdata);
     120void
     121findtorrent(GtkTreeModel *model, tr_torrent_t *tor, GtkTreeIter *iter);
    119122gint
    120123intrevcmp(gconstpointer a, gconstpointer b);
     
    124127
    125128gboolean
    126 addtorrent(tr_handle_t *tr, GtkWindow *parentwind, const char *torrent,
    127            const char *dir, gboolean paused, GList **errs);
     129addtorrent(void *vdata, const char *torrent, const char *dir, gboolean paused,
     130           GList **errs);
    128131void
    129132addedtorrents(void *vdata);
    130133gboolean
    131 savetorrents(tr_handle_t *tr, GtkWindow *wind, int count, tr_stat_t *stat);
     134savetorrents(tr_handle_t *tr, GtkWindow *wind);
    132135void
    133136orstatus(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
     
    136139makeidlist(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
    137140           gpointer gdata);
     141void
     142maketorrentlist(tr_torrent_t *tor, void *data);
     143void
     144setupsighandlers(void);
     145void
     146fatalsig(int sig);
    138147
    139148#define TR_TYPE_PIECES_NAME     "tr-type-pieces"
     
    147156enum listfrom { FROM_BUTTON, FROM_POPUP };
    148157
    149 #define LIST_INDEX            "torrent-list-indexes"
     158#define LIST_IDS              "torrent-list-ids"
    150159#define LIST_MENU_WIDGET      "torrent-list-popup-menu-widget"
    151160
     
    156165   N_("Add a new torrent"), "XXX"},
    157166  {N_("Start"),       GTK_STOCK_EXECUTE,      ACT_START,  FALSE,
    158    (TR_STATUS_STOPPING | TR_STATUS_PAUSE),
     167   TR_STATUS_INACTIVE,
    159168   N_("Start a torrent that is not running"), "XXX"},
    160169  {N_("Stop"),        GTK_STOCK_STOP,         ACT_STOP,   FALSE,
    161    ~(TR_STATUS_STOPPING | TR_STATUS_PAUSE),
     170   TR_STATUS_ACTIVE,
    162171   N_("Stop a torrent that is running"), "XXX"},
    163172  {N_("Remove"),      GTK_STOCK_REMOVE,       ACT_DELETE, FALSE, ~0,
     
    170179
    171180#define CBDATA_PTR              "callback-data-pointer"
     181
     182#define SIGCOUNT_MAX            3
     183
     184static sig_atomic_t global_sigcount = 0;
     185static int global_lastsig = 0;
     186
    172187int
    173188main(int argc, char **argv) {
     
    179194  long intval;
    180195
     196  setupsighandlers();
     197
    181198  gtk_init(&argc, &argv);
    182199
     
    187204
    188205  tr = tr_init();
    189 
    190   setuphandlers(stoptransmission, tr);
    191206
    192207  gtk_rc_parse_string(
     
    306321  for(ii = g_list_first(saved); NULL != ii; ii = ii->next) {
    307322    ts = ii->data;
    308     addtorrent(tr, GTK_WINDOW(wind), ts->ts_torrent, ts->ts_directory,
    309                ts->ts_paused, &loaderrs);
     323    addtorrent(data, ts->ts_torrent, ts->ts_directory, ts->ts_paused,
     324               &loaderrs);
    310325    cf_freestate(ts);
    311326  }
     
    320335    g_list_free(loaderrs);
    321336    g_free(str);
    322     savetorrents(tr, GTK_WINDOW(wind), -1, NULL);
     337    savetorrents(tr, GTK_WINDOW(wind));
    323338  }
    324339
     
    341356}
    342357
    343 /* XXX is this the right thing to do? */
    344 #define TR_TORRENT_NEEDS_STOP(t) \
    345   ((t) & TR_STATUS_CHECK || (t) & TR_STATUS_DOWNLOAD || (t) & TR_STATUS_SEED)
     358GtkWidget *
     359makewind_toolbar(struct cbdata *data) {
     360  GtkWidget *bar = gtk_toolbar_new();
     361  GtkToolItem *item;
     362  unsigned int ii;
     363
     364  gtk_toolbar_set_tooltips(GTK_TOOLBAR(bar), TRUE);
     365  gtk_toolbar_set_show_arrow(GTK_TOOLBAR(bar), FALSE);
     366  gtk_toolbar_set_style(GTK_TOOLBAR(bar), GTK_TOOLBAR_BOTH);
     367
     368  data->buttons = g_new(GtkWidget*, ALEN(actionitems));
     369
     370  for(ii = 0; ii < ALEN(actionitems); ii++) {
     371    item = gtk_tool_button_new_from_stock(actionitems[ii].id);
     372    data->buttons[ii] = GTK_WIDGET(item);
     373    gtk_tool_button_set_label(GTK_TOOL_BUTTON(item),
     374                              gettext(actionitems[ii].name));
     375    gtk_tool_item_set_tooltip(GTK_TOOL_ITEM(item), GTK_TOOLBAR(bar)->tooltips,
     376                              gettext(actionitems[ii].ttext),
     377                              actionitems[ii].tpriv);
     378    g_object_set_data(G_OBJECT(item), LIST_ACTION,
     379                      GINT_TO_POINTER(actionitems[ii].act));
     380    g_object_set_data(G_OBJECT(item), LIST_ACTION_FROM,
     381                      GINT_TO_POINTER(FROM_BUTTON));
     382    g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(actionclick), data);
     383    gtk_toolbar_insert(GTK_TOOLBAR(bar), item, -1);
     384  }
     385
     386  return bar;
     387}
     388
     389/* XXX check for unused data in model */
     390enum {MC_NAME, MC_SIZE, MC_STAT, MC_ERR, MC_TERR, MC_PROG, MC_DRATE, MC_URATE,
     391      MC_ETA, MC_PEERS, MC_UPEERS, MC_DPEERS, MC_PIECES, MC_DOWN, MC_UP,
     392      MC_TORRENT, MC_ROW_COUNT};
     393
     394GtkWidget *
     395makewind_list(struct cbdata *data) {
     396  GType types[] = {
     397    /* info->name, info->totalSize, status,     error,      trackerError, */
     398    G_TYPE_STRING, G_TYPE_UINT64,   G_TYPE_INT, G_TYPE_INT, G_TYPE_STRING,
     399    /* progress,  rateDownload, rateUpload,   eta,        peersTotal, */
     400    G_TYPE_FLOAT, G_TYPE_FLOAT, G_TYPE_FLOAT, G_TYPE_INT, G_TYPE_INT,
     401    /* peersUploading, peersDownloading, pieces,         downloaded, */
     402    G_TYPE_INT,        G_TYPE_INT,       TR_TYPE_PIECES, G_TYPE_UINT64,
     403    /* uploaded,   the handle for the torrent */
     404    G_TYPE_UINT64, G_TYPE_POINTER};
     405  GtkListStore *store;
     406  GtkWidget *view;
     407  GtkTreeViewColumn *col;
     408  GtkTreeSelection *sel;
     409  GtkCellRenderer *namerend, *progrend;
     410  char *str;
     411
     412  assert(MC_ROW_COUNT == ALEN(types));
     413
     414  store = gtk_list_store_newv(MC_ROW_COUNT, types);
     415  view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
     416  /* XXX do I need to worry about reference counts anywhere else? */
     417  g_object_unref(G_OBJECT(store));
     418  data->model = GTK_TREE_MODEL(store);
     419  data->view = GTK_TREE_VIEW(view);
     420
     421  namerend = gtk_cell_renderer_text_new();
     422  col = gtk_tree_view_column_new_with_attributes(_("Name"), namerend, NULL);
     423  gtk_tree_view_column_set_cell_data_func(col, namerend, dfname, NULL, NULL);
     424  gtk_tree_view_column_set_expand(col, TRUE);
     425  gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
     426
     427  progrend = tr_cell_renderer_torrent_new();
     428  /* this string is only used to determing the size of the progress bar */
     429  str = g_markup_printf_escaped("<big>%s</big>", _("  fnord    fnord  "));
     430  g_object_set(progrend, "label", str, NULL);
     431  g_free(str);
     432  col = gtk_tree_view_column_new_with_attributes(_("Progress"), progrend, NULL);
     433  gtk_tree_view_column_set_cell_data_func(col, progrend, dfprog, NULL, NULL);
     434  gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
     435
     436  /* XXX this shouldn't be necessary */
     437  g_signal_connect(view, "notify", G_CALLBACK(stylekludge), progrend);
     438
     439  gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE);
     440  sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
     441  gtk_tree_selection_set_mode(GTK_TREE_SELECTION(sel), GTK_SELECTION_MULTIPLE);
     442  g_signal_connect(G_OBJECT(sel), "changed", G_CALLBACK(fixbuttons), data);
     443  g_signal_connect(G_OBJECT(view), "button-press-event",
     444                   G_CALLBACK(listclick), data);
     445  g_signal_connect(G_OBJECT(view), "popup-menu", G_CALLBACK(listpopup), data);
     446  g_signal_connect(G_OBJECT(view), "row-activated",
     447                   G_CALLBACK(doubleclick), data);
     448  gtk_widget_show_all(view);
     449
     450  return view;
     451}
    346452
    347453gboolean
     
    350456  struct exitdata *edata;
    351457  tr_stat_t *st;
    352   int ii;
     458  GtkTreeIter iter;
     459  tr_torrent_t *tor;
     460  gboolean going;
    353461
    354462  if(0 >= data->timer)
     
    356464  data->timer = -1;
    357465
    358   blocksigs();
    359 
    360   for(ii = tr_torrentStat(data->tr, &st); 0 < ii; ii--) {
    361     if(TR_TORRENT_NEEDS_STOP(st[ii-1].status)) {
    362       /*fprintf(stderr, "quit: stopping %i %s\n", ii, st[ii-1].info.name);*/
    363       tr_torrentStop(data->tr, ii - 1);
     466  going = gtk_tree_model_get_iter_first(data->model, &iter);
     467  while(going) {
     468    gtk_tree_model_get(data->model, &iter, MC_TORRENT, &tor, -1);
     469    st = tr_torrentStat(tor);
     470    if(TR_STATUS_ACTIVE & st->status) {
     471      tr_torrentStop(tor);
     472      going = gtk_tree_model_iter_next(data->model, &iter);
    364473    } else {
    365       /*fprintf(stderr, "quit: closing %i %s\n", ii, st[ii-1].info.name);*/
    366       tr_torrentClose(data->tr, ii - 1);
     474      tr_torrentClose(data->tr, tor);
     475      going = gtk_list_store_remove(GTK_LIST_STORE(data->model), &iter);
    367476    }
    368477  }
    369   free(st);
    370 
    371   unblocksigs();
    372478
    373479  /* XXX should disable widgets or something */
     
    379485  edata->timer = g_timeout_add(500, exitcheck, edata);
    380486
    381   /*fprintf(stderr, "quit: starting timeout at %i\n", edata->started);*/
    382 
    383487  /* returning FALSE means to destroy the window */
    384488  return TRUE;
     
    389493  struct exitdata *data = gdata;
    390494  tr_stat_t *st;
    391   int ii;
    392 
    393   blocksigs();
    394 
    395   for(ii = tr_torrentStat(data->cbdata->tr, &st); 0 < ii; ii--) {
    396     if(TR_STATUS_PAUSE & st[ii-1].status) {
    397       /*fprintf(stderr, "quit: closing %i %s\n", ii, st[ii-1].info.name);*/
    398       tr_torrentClose(data->cbdata->tr, ii - 1);
     495  GtkTreeIter iter;
     496  tr_torrent_t *tor;
     497  gboolean go;
     498
     499  go = gtk_tree_model_get_iter_first(data->cbdata->model, &iter);
     500  while(go) {
     501    gtk_tree_model_get(data->cbdata->model, &iter, MC_TORRENT, &tor, -1);
     502    st = tr_torrentStat(tor);
     503    if(!(TR_STATUS_PAUSE & st->status))
     504      go = gtk_tree_model_iter_next(data->cbdata->model, &iter);
     505    else {
     506      tr_torrentClose(data->cbdata->tr, tor);
     507      go = gtk_list_store_remove(GTK_LIST_STORE(data->cbdata->model), &iter);
    399508    }
    400509  }
    401   free(st);
    402 
    403   /*fprintf(stderr, "quit: %i torrents left at %i\n",
    404     tr_torrentCount(data->cbdata->tr), time(NULL));*/
     510
    405511  /* keep going if we still have torrents and haven't hit the exit timeout */
    406512  if(0 < tr_torrentCount(data->cbdata->tr) &&
    407513     time(NULL) - data->started < TRACKER_EXIT_TIMEOUT) {
     514    assert(gtk_tree_model_get_iter_first(data->cbdata->model, &iter));
    408515    updatemodel(data->cbdata);
    409     unblocksigs();
    410516    return TRUE;
    411517  }
     
    417523  data->timer = -1;
    418524
    419   /*fprintf(stderr, "quit: giving up on %i torrents\n",
    420     tr_torrentCount(data->cbdata->tr));*/
    421525  stoptransmission(data->cbdata->tr);
    422   clearhandlers();
    423   unblocksigs();
    424526
    425527  gtk_widget_destroy(GTK_WIDGET(data->cbdata->wind));
     
    432534
    433535void
    434 stoptransmission(void *tr) {
    435   while(0 < tr_torrentCount(tr))
    436     tr_torrentClose(tr, 0);
     536stoptransmission(tr_handle_t *tr) {
     537  GList *list, *ii;
     538
     539  list = NULL;
     540  tr_torrentIterate(tr, maketorrentlist, &list);
     541  for(ii = g_list_first(list); NULL != ii; ii = ii->next)
     542    tr_torrentClose(tr, ii->data);
     543  g_list_free(list);
     544
    437545  tr_close(tr);
    438546}
     
    501609              deslashed = hostless;
    502610            /* finally, try to add it as a torrent */
    503             if(addtorrent(data->tr, data->wind, deslashed, NULL, FALSE, &errs))
     611            if(addtorrent(data, deslashed, NULL, FALSE, &errs))
    504612              gotfile = TRUE;
    505613          }
     
    542650}
    543651
    544 GtkWidget *
    545 makewind_toolbar(struct cbdata *data) {
    546   GtkWidget *bar = gtk_toolbar_new();
    547   GtkToolItem *item;
    548   unsigned int ii;
    549 
    550   gtk_toolbar_set_tooltips(GTK_TOOLBAR(bar), TRUE);
    551   gtk_toolbar_set_show_arrow(GTK_TOOLBAR(bar), FALSE);
    552   gtk_toolbar_set_style(GTK_TOOLBAR(bar), GTK_TOOLBAR_BOTH);
    553 
    554   data->buttons = g_new(GtkWidget*, ALEN(actionitems));
    555 
    556   for(ii = 0; ii < ALEN(actionitems); ii++) {
    557     item = gtk_tool_button_new_from_stock(actionitems[ii].id);
    558     data->buttons[ii] = GTK_WIDGET(item);
    559     gtk_tool_button_set_label(GTK_TOOL_BUTTON(item), gettext(actionitems[ii].name));
    560     gtk_tool_item_set_tooltip(GTK_TOOL_ITEM(item), GTK_TOOLBAR(bar)->tooltips,
    561                               gettext(actionitems[ii].ttext), actionitems[ii].tpriv);
    562     g_object_set_data(G_OBJECT(item), LIST_ACTION,
    563                       GINT_TO_POINTER(actionitems[ii].act));
    564     g_object_set_data(G_OBJECT(item), LIST_ACTION_FROM,
    565                       GINT_TO_POINTER(FROM_BUTTON));
    566     g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(actionclick), data);
    567     gtk_toolbar_insert(GTK_TOOLBAR(bar), item, -1);
    568   }
    569 
    570   return bar;
    571 }
    572 
    573 /* XXX check for unused data in model */
    574 enum {MC_NAME, MC_SIZE, MC_STAT, MC_ERR, MC_PROG, MC_DRATE, MC_URATE,
    575       MC_ETA, MC_PEERS, MC_UPEERS, MC_DPEERS, MC_PIECES, MC_DOWN, MC_UP,
    576       MC_ROW_INDEX, MC_ROW_COUNT};
    577 
    578 GtkWidget *
    579 makewind_list(struct cbdata *data) {
    580   GType types[] = {
    581     /* info->name, info->totalSize, status,     error,         progress */
    582     G_TYPE_STRING, G_TYPE_UINT64,   G_TYPE_INT, G_TYPE_STRING, G_TYPE_FLOAT,
    583     /* rateDownload, rateUpload,   eta,        peersTotal, peersUploading */
    584     G_TYPE_FLOAT,    G_TYPE_FLOAT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT,
    585     /* peersDownloading, pieces,         downloaded,    uploaded */
    586     G_TYPE_INT,          TR_TYPE_PIECES, G_TYPE_UINT64, G_TYPE_UINT64,
    587     /* index into the torrent array */
    588     G_TYPE_INT};
    589   GtkListStore *model;
    590   GtkWidget *view;
    591   GtkTreeViewColumn *col;
    592   GtkTreeSelection *sel;
    593   GtkCellRenderer *namerend, *progrend;
    594   char *str;
    595 
    596   assert(MC_ROW_COUNT == ALEN(types));
    597 
    598   model = gtk_list_store_newv(MC_ROW_COUNT, types);
    599   view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
    600   /* XXX do I need to worry about reference counts anywhere else? */
    601   g_object_unref(G_OBJECT(model));
    602   data->model = model;
    603   data->view = GTK_TREE_VIEW(view);
    604 
    605   namerend = gtk_cell_renderer_text_new();
    606   col = gtk_tree_view_column_new_with_attributes(_("Name"), namerend, NULL);
    607   gtk_tree_view_column_set_cell_data_func(col, namerend, dfname, NULL, NULL);
    608   gtk_tree_view_column_set_expand(col, TRUE);
    609   gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
    610 
    611   progrend = tr_cell_renderer_torrent_new();
    612   /* this string is only used to determing the size of the progress bar */
    613   str = g_markup_printf_escaped("<big>%s</big>", _("  fnord    fnord  "));
    614   g_object_set(progrend, "label", str, NULL);
    615   g_free(str);
    616   col = gtk_tree_view_column_new_with_attributes(_("Progress"), progrend, NULL);
    617   gtk_tree_view_column_set_cell_data_func(col, progrend, dfprog, NULL, NULL);
    618   gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
    619 
    620   /* XXX this shouldn't be necessary */
    621   g_signal_connect(view, "notify", G_CALLBACK(stylekludge), progrend);
    622 
    623   gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE);
    624   sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
    625   gtk_tree_selection_set_mode(GTK_TREE_SELECTION(sel), GTK_SELECTION_MULTIPLE);
    626   g_signal_connect(G_OBJECT(sel), "changed", G_CALLBACK(fixbuttons), data);
    627   g_signal_connect(G_OBJECT(view), "button-press-event",
    628                    G_CALLBACK(listclick), data);
    629   g_signal_connect(G_OBJECT(view), "popup-menu", G_CALLBACK(listpopup), data);
    630   g_signal_connect(G_OBJECT(view), "row-activated",
    631                    G_CALLBACK(doubleclick), data);
    632   gtk_widget_show_all(view);
    633 
    634   return view;
    635 }
    636 
    637652/* kludge to have the progress bars notice theme changes */
    638653static void
     
    667682dfname(GtkTreeViewColumn *col SHUTUP, GtkCellRenderer *rend,
    668683       GtkTreeModel *model, GtkTreeIter *iter, gpointer gdata SHUTUP) {
    669   char *name, *mb, *err, *str, *top, *bottom;
     684  char *name, *mb, *terr, *str, *top, *bottom;
    670685  guint64 size;
    671686  gfloat prog;
    672   int status, eta, tpeers, upeers, dpeers;
    673 
    674   /* XXX should I worry about gtk_tree_model_get failing? */
     687  int status, err, eta, tpeers, upeers, dpeers;
     688
    675689  gtk_tree_model_get(model, iter, MC_NAME, &name, MC_STAT, &status,
    676     MC_SIZE, &size, MC_PROG, &prog, MC_ETA, &eta, MC_PEERS, &tpeers,
    677     MC_UPEERS, &upeers, MC_DPEERS, &dpeers, -1);
     690    MC_ERR, &err, MC_SIZE, &size, MC_PROG, &prog, MC_ETA, &eta,
     691    MC_PEERS, &tpeers, MC_UPEERS, &upeers, MC_DPEERS, &dpeers, -1);
    678692
    679693  if(0 > tpeers)
     
    708722  }
    709723
    710   if(status & TR_TRACKER_ERROR) {
    711     gtk_tree_model_get(model, iter, MC_ERR, &err, -1);
    712     bottom = g_strconcat(_("Error: "), err, NULL);
    713     g_free(err);
     724  if(TR_NOERROR != err) {
     725    gtk_tree_model_get(model, iter, MC_TERR, &terr, -1);
     726    bottom = g_strconcat(_("Error: "), terr, NULL);
     727    g_free(terr);
    714728  }
    715729  else if(status & TR_STATUS_DOWNLOAD)
     
    737751  guint64 down, up;
    738752
    739   /* XXX should I worry about gtk_tree_model_get failing? */
    740753  gtk_tree_model_get(model, iter, MC_PROG, &prog, MC_DRATE, &dl, MC_URATE, &ul,
    741754                     MC_DOWN, &down, MC_UP, &up, -1);
     
    764777updatemodel(gpointer gdata) {
    765778  struct cbdata *data = gdata;
     779  tr_torrent_t *tor;
    766780  tr_stat_t *st;
    767   int ii, max;
     781  tr_info_t *in;
    768782  GtkTreeIter iter;
    769783  float up, down;
    770784  char *upstr, *downstr, *str;
    771785
    772   blocksigs();
    773 
    774   max = tr_torrentStat(data->tr, &st);
    775   for(ii = 0; ii < max; ii++) {
    776     if(!(ii ? gtk_tree_model_iter_next(GTK_TREE_MODEL(data->model), &iter) :
    777          gtk_tree_model_get_iter_first(GTK_TREE_MODEL(data->model), &iter)))
    778       gtk_list_store_append(data->model, &iter);
    779     /* XXX find out if setting the same data emits changed signal */
    780     gtk_list_store_set(
    781       data->model, &iter, MC_ROW_INDEX, ii,
    782       MC_NAME, st[ii].info.name, MC_SIZE, st[ii].info.totalSize,
    783       MC_STAT, st[ii].status, MC_ERR, st[ii].error, MC_PROG, st[ii].progress,
    784       MC_DRATE, st[ii].rateDownload, MC_URATE, st[ii].rateUpload,
    785       MC_ETA, st[ii].eta, MC_PEERS, st[ii].peersTotal,
    786       MC_UPEERS, st[ii].peersUploading, MC_DPEERS, st[ii].peersDownloading,
    787       MC_DOWN, st[ii].downloaded, MC_UP, st[ii].uploaded, -1);
    788   }
    789   free(st);
    790 
    791   /* remove any excess rows */
    792   if(ii ? gtk_tree_model_iter_next(GTK_TREE_MODEL(data->model), &iter) :
    793      gtk_tree_model_get_iter_first(GTK_TREE_MODEL(data->model), &iter))
    794     while(gtk_list_store_remove(data->model, &iter))
    795       ;
     786  if(0 < global_sigcount) {
     787    stoptransmission(data->tr);
     788    global_sigcount = SIGCOUNT_MAX;
     789    raise(global_lastsig);
     790  }
     791
     792  if(gtk_tree_model_get_iter_first(data->model, &iter)) {
     793    do {
     794      gtk_tree_model_get(data->model, &iter, MC_TORRENT, &tor, -1);
     795      st = tr_torrentStat(tor);
     796      in = tr_torrentInfo(tor);
     797      /* XXX find out if setting the same data emits changed signal */
     798      gtk_list_store_set(GTK_LIST_STORE(data->model), &iter, MC_NAME, in->name,
     799        MC_SIZE, in->totalSize, MC_STAT, st->status, MC_ERR, st->error,
     800        MC_TERR, st->trackerError, MC_PROG, st->progress,
     801        MC_DRATE, st->rateDownload, MC_URATE, st->rateUpload, MC_ETA, st->eta,
     802        MC_PEERS, st->peersTotal, MC_UPEERS, st->peersUploading,
     803        MC_DPEERS, st->peersDownloading, MC_DOWN, st->downloaded,
     804        MC_UP, st->uploaded, -1);
     805    } while(gtk_tree_model_iter_next(data->model, &iter));
     806  }
    796807
    797808  /* update the status bar */
     
    810821  fixbuttons(NULL, data);
    811822
    812   unblocksigs();
    813 
    814823  return TRUE;
    815824}
     
    822831  GtkTreePath *path;
    823832  GtkTreeIter iter;
    824   int index, status;
     833  int status;
     834  gpointer tor;
    825835  GList *ids;
    826836
     
    832842      dopopupmenu(event, data, NULL, 0);
    833843    else {
    834       if(gtk_tree_model_get_iter(GTK_TREE_MODEL(data->model), &iter, path)) {
     844      if(gtk_tree_model_get_iter(data->model, &iter, path)) {
    835845        /* get ID and status for the right-clicked row */
    836         gtk_tree_model_get(GTK_TREE_MODEL(data->model), &iter,
    837                            MC_ROW_INDEX, &index, MC_STAT, &status, -1);
     846        gtk_tree_model_get(data->model, &iter, MC_TORRENT, &tor,
     847                           MC_STAT, &status, -1);
    838848        /* get a list of selected IDs */
    839849        ids = NULL;
    840850        gtk_tree_selection_selected_foreach(sel, makeidlist, &ids);
    841851        /* is the clicked row selected? */
    842         if(NULL == g_list_find(ids, GINT_TO_POINTER(index))) {
     852        if(NULL == g_list_find(ids, tor)) {
    843853          /* no, do the popup for just the clicked row */
    844854          g_list_free(ids);
    845           dopopupmenu(event, data, g_list_append(NULL, GINT_TO_POINTER(index)),
    846                       status);
     855          dopopupmenu(event, data, g_list_append(NULL, tor), status);
    847856        } else {
    848857          /* yes, do the popup for all the selected rows */
     
    863872  struct cbdata *data = gdata;
    864873  GtkTreeSelection *sel = gtk_tree_view_get_selection(data->view);
    865   GtkTreeModel *model;
    866874  GList *ids;
    867875  int status;
     
    870878    dopopupmenu(NULL, data, NULL, 0);
    871879  else {
    872     assert(model == GTK_TREE_MODEL(data->model));
    873880    status = 0;
    874881    gtk_tree_selection_selected_foreach(sel, orstatus, &status);
     
    901908                      GINT_TO_POINTER(FROM_POPUP));
    902909    /* set a glist of selected torrent's IDs */
    903     g_object_set_data(G_OBJECT(item), LIST_INDEX, ids);
     910    g_object_set_data(G_OBJECT(item), LIST_IDS, ids);
    904911    /* set the menu widget, so the activate handler can destroy it */
    905912    g_object_set_data(G_OBJECT(item), LIST_MENU_WIDGET, menu);
     
    910917
    911918  /* set up the glist to be freed when the menu is destroyed */
    912   g_object_set_data_full(G_OBJECT(menu), LIST_INDEX, ids,
     919  g_object_set_data_full(G_OBJECT(menu), LIST_IDS, ids,
    913920                         (GDestroyNotify)g_list_free);
    914921
     
    936943  enum listfrom from =
    937944    GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), LIST_ACTION_FROM));
    938   int index, count;
    939945  unsigned int actindex;
    940946  tr_stat_t *sb;
    941947  GList *ids, *ii;
     948  tr_torrent_t *tor;
    942949  gboolean updatesave;
     950  GtkTreeIter iter;
    943951
    944952  /* destroy the popup menu, if any */
     
    948956  switch(act) {
    949957    case ACT_OPEN:
    950       makeaddwind(addtorrent, data->wind, data->tr, addedtorrents, data);
     958      makeaddwind(data->wind, addtorrent, addedtorrents, data);
    951959      return;
    952960    case ACT_PREF:
     
    962970      ids = NULL;
    963971      gtk_tree_selection_selected_foreach(sel, makeidlist, &ids);
    964       /* XXX should I assert(0 <= index) to insure a row was selected? */
    965972      break;
    966973    case FROM_POPUP:
    967       ids = g_object_get_data(G_OBJECT(widget), LIST_INDEX);
     974      ids = g_object_get_data(G_OBJECT(widget), LIST_IDS);
    968975      break;
    969976    default:
     
    977984  assert(actindex < ALEN(actionitems));
    978985
    979   blocksigs();
    980986  updatesave = FALSE;
    981   count = tr_torrentStat(data->tr, &sb);
    982987
    983988  for(ii = g_list_sort(ids, intrevcmp); NULL != ii; ii = ii->next) {
    984     index = GPOINTER_TO_INT(ii->data);
    985     if(index >= count) {
    986       assert(!"illegal torrent id");
    987       continue;
    988     }
     989    tor = ii->data;
     990    sb = tr_torrentStat(tor);
     991
    989992    /* check if this action is valid for this torrent */
    990993    if(actionitems[actindex].nomenu ||
    991994       (actionitems[actindex].avail &&
    992         !(actionitems[actindex].avail & sb[index].status)))
     995        !(actionitems[actindex].avail & sb->status)))
    993996      continue;
    994997
    995998    switch(act) {
    996999      case ACT_START:
    997         tr_torrentStart(data->tr, index);
     1000        tr_torrentStart(tor);
    9981001        updatesave = TRUE;
    9991002        break;
    10001003      case ACT_STOP:
    1001         tr_torrentStop(data->tr, index);
     1004        tr_torrentStop(tor);
    10021005        updatesave = TRUE;
    10031006        break;
    10041007      case ACT_DELETE:
    1005         if(TR_TORRENT_NEEDS_STOP(sb[index].status))
    1006           tr_torrentStop(data->tr, index);
    1007         tr_torrentClose(data->tr, index);
     1008        if(TR_STATUS_ACTIVE & sb->status)
     1009          tr_torrentStop(tor);
     1010        tr_torrentClose(data->tr, tor);
     1011        findtorrent(data->model, tor, &iter);
     1012        gtk_list_store_remove(GTK_LIST_STORE(data->model), &iter);
    10081013        updatesave = TRUE;
    10091014        /* XXX should only unselect deleted rows */
    1010         gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(data->view));
     1015        gtk_tree_selection_unselect_all(
     1016          gtk_tree_view_get_selection(data->view));
    10111017        break;
    10121018      case ACT_INFO:
    1013         makeinfowind(data->wind, data->tr, index);
     1019        makeinfowind(data->wind, tor);
    10141020        break;
    10151021      default:
     
    10181024    }
    10191025  }
    1020   free(sb);
    10211026
    10221027  if(updatesave) {
    1023     savetorrents(data->tr, data->wind, -1, NULL);
     1028    savetorrents(data->tr, data->wind);
    10241029    updatemodel(data);
    10251030  }
    1026 
    1027   unblocksigs();
    10281031
    10291032  if(FROM_BUTTON == from)
    10301033    g_list_free(ids);
     1034}
     1035
     1036void
     1037findtorrent(GtkTreeModel *model, tr_torrent_t *tor, GtkTreeIter *iter) {
     1038  gpointer ptr;
     1039
     1040  if(gtk_tree_model_get_iter_first(model, iter)) {
     1041    do {
     1042      gtk_tree_model_get(model, iter, MC_TORRENT, &ptr, -1);
     1043      if(tor == ptr)
     1044        return;
     1045    } while(gtk_tree_model_iter_next(model, iter));
     1046  }
     1047
     1048  assert(!"torrent not found");
    10311049}
    10321050
     
    10491067  struct cbdata *data = gdata;
    10501068  GtkTreeIter iter;
    1051   int index;
    1052 
    1053   if(gtk_tree_model_get_iter(GTK_TREE_MODEL(data->model), &iter, path)) {
    1054     gtk_tree_model_get(GTK_TREE_MODEL(data->model), &iter,
    1055                        MC_ROW_INDEX, &index, -1);
    1056     makeinfowind(data->wind, data->tr, index);
    1057   }
    1058 }
    1059 
    1060 gboolean
    1061 addtorrent(tr_handle_t *tr, GtkWindow *parentwind, const char *torrent,
    1062            const char *dir, gboolean paused, GList **errs) {
     1069  tr_torrent_t *tor;
     1070
     1071  if(gtk_tree_model_get_iter(data->model, &iter, path)) {
     1072    gtk_tree_model_get(data->model, &iter, MC_TORRENT, &tor, -1);
     1073    makeinfowind(data->wind, tor);
     1074  }
     1075}
     1076
     1077gboolean
     1078addtorrent(void *vdata, const char *torrent, const char *dir, gboolean paused,
     1079           GList **errs) {
     1080  const struct { const int err; const char *msg; } errstrs[] = {
     1081    {TR_EINVALID,       N_("not a valid torrent file")},
     1082    {TR_EDUPLICATE,     N_("torrent is already open")},
     1083  };
     1084  struct cbdata *data = vdata;
     1085  tr_torrent_t *new;
    10631086  char *wd;
     1087  int err;
     1088  unsigned int ii;
     1089  GtkTreeIter iter;
    10641090
    10651091  if(NULL == dir && NULL != (dir = cf_getpref(PREF_DIR))) {
    10661092    if(!mkdir_p(dir, 0777)) {
    1067       errmsg(parentwind, _("Failed to create the directory %s:\n%s"),
     1093      errmsg(data->wind, _("Failed to create the directory %s:\n%s"),
    10681094             dir, strerror(errno));
    10691095      return FALSE;
     
    10711097  }
    10721098
    1073   blocksigs();
    1074 
    1075   if(0 != tr_torrentInit(tr, torrent)) {
    1076     unblocksigs();
    1077     /* XXX would be nice to have errno strings, are they printed to stdout? */
    1078     if(NULL == errs)
    1079       errmsg(parentwind, _("Failed to load the torrent file %s"), torrent);
    1080     else
    1081       *errs = g_list_append(*errs, g_strdup(torrent));
     1099  if(NULL == (new = tr_torrentInit(data->tr, torrent, &err))) {
     1100    for(ii = 0; ii < ALEN(errstrs); ii++)
     1101      if(err == errstrs[ii].err)
     1102        break;
     1103    if(NULL == errs) {
     1104      if(ii == ALEN(errstrs))
     1105        errmsg(data->wind, _("Failed to load the torrent file %s"), torrent);
     1106      else
     1107        errmsg(data->wind, _("Failed to load the torrent file %s: %s"),
     1108               torrent, gettext(errstrs[ii].msg));
     1109    } else {
     1110      if(ii == ALEN(errstrs))
     1111        *errs = g_list_append(*errs, g_strdup(torrent));
     1112      else
     1113        *errs = g_list_append(*errs, g_strdup_printf(_("%s (%s)"),
     1114                              torrent, gettext(errstrs[ii].msg)));
     1115    }
    10821116    return FALSE;
    10831117  }
    10841118
     1119  gtk_list_store_append(GTK_LIST_STORE(data->model), &iter);
     1120  gtk_list_store_set(GTK_LIST_STORE(data->model), &iter, MC_TORRENT, new, -1);
     1121
    10851122  if(NULL != dir)
    1086     tr_torrentSetFolder(tr, tr_torrentCount(tr) - 1, dir);
     1123    tr_torrentSetFolder(new, dir);
    10871124  else {
    10881125    wd = g_new(char, MAXPATHLEN + 1);
    10891126    if(NULL == getcwd(wd, MAXPATHLEN + 1))
    1090       tr_torrentSetFolder(tr, tr_torrentCount(tr) - 1, ".");
     1127      tr_torrentSetFolder(new, ".");
    10911128    else {
    1092       tr_torrentSetFolder(tr, tr_torrentCount(tr) - 1, wd);
     1129      tr_torrentSetFolder(new, wd);
    10931130      free(wd);
    10941131    }
     
    10961133
    10971134  if(!paused)
    1098     tr_torrentStart(tr, tr_torrentCount(tr) - 1);
    1099 
    1100   unblocksigs();
     1135    tr_torrentStart(new);
    11011136
    11021137  return TRUE;
     
    11081143
    11091144  updatemodel(data);
    1110   savetorrents(data->tr, data->wind, -1, NULL);
    1111 }
    1112 
    1113 gboolean
    1114 savetorrents(tr_handle_t *tr, GtkWindow *wind, int count, tr_stat_t *stat) {
     1145  savetorrents(data->tr, data->wind);
     1146}
     1147
     1148gboolean
     1149savetorrents(tr_handle_t *tr, GtkWindow *wind) {
     1150  GList *torrents;
    11151151  char *errstr;
    1116   tr_stat_t *st;
    11171152  gboolean ret;
    11181153
    1119   assert(NULL != tr || 0 <= count);
    1120 
    1121   if(0 <= count)
    1122     ret = cf_savestate(count, stat, &errstr);
    1123   else {
    1124     blocksigs();
    1125     count = tr_torrentStat(tr, &st);
    1126     unblocksigs();
    1127     ret = cf_savestate(count, st, &errstr);
    1128     free(st);
    1129   }
    1130 
    1131   if(!ret) {
     1154  torrents = NULL;
     1155  tr_torrentIterate(tr, maketorrentlist, &torrents);
     1156
     1157  if(!(ret = cf_savestate(torrents, &errstr))) {
    11321158    errmsg(wind, "%s", errstr);
    11331159    g_free(errstr);
    11341160  }
     1161
     1162  g_list_free(torrents);
    11351163
    11361164  return ret;
     
    11521180           gpointer gdata) {
    11531181  GList **ids = gdata;
    1154   int index;
    1155 
    1156   gtk_tree_model_get(model, iter, MC_ROW_INDEX, &index, -1);
    1157   *ids = g_list_append(*ids, GINT_TO_POINTER(index));
    1158 }
     1182  gpointer ptr;
     1183
     1184  gtk_tree_model_get(model, iter, MC_TORRENT, &ptr, -1);
     1185  *ids = g_list_append(*ids, ptr);
     1186}
     1187
     1188void
     1189maketorrentlist(tr_torrent_t *tor, void *data) {
     1190  GList **list = data;
     1191
     1192  *list = g_list_append(*list, tor);
     1193}
     1194
     1195void
     1196setupsighandlers(void) {
     1197  int sigs[] = {SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGUSR1, SIGUSR2};
     1198  struct sigaction sa;
     1199  unsigned int ii;
     1200
     1201  bzero(&sa, sizeof(sa));
     1202  sa.sa_handler = fatalsig;
     1203  for(ii = 0; ii < ALEN(sigs); ii++)
     1204    sigaction(sigs[ii], &sa, NULL);
     1205}
     1206
     1207void
     1208fatalsig(int sig) {
     1209  struct sigaction sa;
     1210
     1211  global_lastsig = sig;
     1212
     1213  if(SIGCOUNT_MAX <= ++global_sigcount) {
     1214    bzero(&sa, sizeof(sa));
     1215    sa.sa_handler = SIG_DFL;
     1216    sigaction(sig, &sa, NULL);
     1217    raise(sig);
     1218  }
     1219}
  • trunk/gtk/po/transmission-gtk.pot

    r117 r162  
    99"Project-Id-Version: PACKAGE VERSION\n"
    1010"Report-Msgid-Bugs-To: \n"
    11 "POT-Creation-Date: 2006-02-10 00:50-0500\n"
     11"POT-Creation-Date: 2006-03-20 11:30-0800\n"
    1212"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
    1313"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
     
    4444msgstr ""
    4545
    46 #: conf.c:115 dialogs.c:191 main.c:1065
     46#: conf.c:115 dialogs.c:190 main.c:1093
    4747#, c-format
    4848msgid ""
     
    7070msgstr ""
    7171
    72 #: conf.c:233 conf.c:432
     72#: conf.c:233 conf.c:434
    7373#, c-format
    7474msgid ""
     
    7777msgstr ""
    7878
    79 #: conf.c:253 conf.c:472
     79#: conf.c:253 conf.c:476
    8080#, c-format
    8181msgid ""
     
    8484msgstr ""
    8585
    86 #: conf.c:260 conf.c:479
     86#: conf.c:260 conf.c:483
    8787#, c-format
    8888msgid ""
     
    9191msgstr ""
    9292
    93 #: dialogs.c:76
     93#: dialogs.c:75
    9494#, c-format
    9595msgid "%s Preferences"
    9696msgstr ""
    9797
    98 #: dialogs.c:84
     98#: dialogs.c:83
    9999msgid "_Limit upload speed"
    100100msgstr ""
    101101
    102 #: dialogs.c:87
     102#: dialogs.c:86
    103103msgid "Choose download directory"
    104104msgstr ""
    105105
    106106#. limit label and entry
    107 #: dialogs.c:121
     107#: dialogs.c:120
    108108msgid "Maximum _upload speed:"
    109109msgstr ""
    110110
    111111#. directory label and chooser
    112 #: dialogs.c:135
     112#: dialogs.c:134
    113113msgid "_Download directory:"
    114114msgstr ""
    115115
    116116#. port label and entry
    117 #: dialogs.c:144
     117#: dialogs.c:143
    118118msgid "Listening _port:"
    119119msgstr ""
     
    135135msgstr ""
    136136
    137 #: dialogs.c:285
     137#: dialogs.c:284
    138138msgid "Torrent files"
    139139msgstr ""
    140140
    141 #: dialogs.c:287
     141#: dialogs.c:286
    142142msgid "All files"
    143143msgstr ""
     
    192192msgstr ""
    193193
    194 #: main.c:155
     194#: main.c:164
    195195msgid "Add"
    196196msgstr ""
    197197
    198 #: main.c:156
     198#: main.c:165
    199199msgid "Add a new torrent"
    200200msgstr ""
    201201
    202 #: main.c:157
     202#: main.c:166
    203203msgid "Start"
    204204msgstr ""
    205205
    206 #: main.c:159
     206#: main.c:168
    207207msgid "Start a torrent that is not running"
    208208msgstr ""
    209209
    210 #: main.c:160
     210#: main.c:169
    211211msgid "Stop"
    212212msgstr ""
    213213
    214 #: main.c:162
     214#: main.c:171
    215215msgid "Stop a torrent that is running"
    216216msgstr ""
    217217
    218 #: main.c:163
     218#: main.c:172
    219219msgid "Remove"
    220220msgstr ""
    221221
    222 #: main.c:164
     222#: main.c:173
    223223msgid "Remove a torrent"
    224224msgstr ""
    225225
    226 #: main.c:165
     226#: main.c:174
    227227msgid "Properties"
    228228msgstr ""
    229229
    230 #: main.c:166
     230#: main.c:175
    231231msgid "Show additional information about a torrent"
    232232msgstr ""
    233233
    234 #: main.c:167
     234#: main.c:176
    235235msgid "Preferences"
    236236msgstr ""
    237237
    238 #: main.c:168
     238#: main.c:177
    239239msgid "Customize application behavior"
    240240msgstr ""
    241241
    242 #: main.c:186
     242#: main.c:203
    243243msgid "Transmission"
    244244msgstr ""
    245245
    246 #: main.c:316 main.c:518 main.c:1077
     246#: main.c:331 main.c:626 main.c:1105
    247247#, c-format
    248248msgid "Failed to load the torrent file %s"
     
    253253msgstr[1] ""
    254254
    255 #: main.c:606
     255#: main.c:422
    256256msgid "Name"
    257257msgstr ""
    258258
    259259#. this string is only used to determing the size of the progress bar
    260 #: main.c:613
     260#: main.c:429
    261261msgid "  fnord    fnord  "
    262262msgstr ""
    263263
    264 #: main.c:616
     264#: main.c:432
    265265msgid "Progress"
    266266msgstr ""
    267267
    268 #: main.c:691
     268#: main.c:703
    269269#, c-format
    270270msgid "Checking existing files (%.1f%%)"
    271271msgstr ""
    272272
    273 #: main.c:693
     273#: main.c:706
     274#, c-format
     275msgid "Finishing in --:--:-- (%.1f%%)"
     276msgstr ""
     277
     278#: main.c:708
    274279#, c-format
    275280msgid "Finishing in %02i:%02i:%02i (%.1f%%)"
    276281msgstr ""
    277282
    278 #: main.c:696
     283#: main.c:712
    279284#, c-format
    280285msgid "Seeding, uploading to %d of %d peer"
     
    283288msgstr[1] ""
    284289
    285 #: main.c:700
     290#: main.c:716
    286291msgid "Stopping..."
    287292msgstr ""
    288293
    289 #: main.c:702
     294#: main.c:718
    290295#, c-format
    291296msgid "Stopped (%.1f%%)"
    292297msgstr ""
    293298
    294 #: main.c:710
     299#: main.c:726
    295300msgid "Error: "
    296301msgstr ""
    297302
    298 #: main.c:714
     303#: main.c:730
    299304#, c-format
    300305msgid "Downloading from %i of %i peer"
     
    303308msgstr[1] ""
    304309
    305 #: main.c:748
     310#: main.c:763
    306311#, c-format
    307312msgid ""
     
    310315msgstr ""
    311316
    312 #: main.c:751
     317#: main.c:766
    313318#, c-format
    314319msgid ""
     
    317322msgstr ""
    318323
    319 #: main.c:799
     324#: main.c:812
    320325#, c-format
    321326msgid "     Total DL: %s/s     Total UL: %s/s"
    322327msgstr ""
    323328
     329#: main.c:1081
     330msgid "not a valid torrent file"
     331msgstr ""
     332
     333#: main.c:1082
     334msgid "torrent is already open"
     335msgstr ""
     336
     337#: main.c:1107
     338#, c-format
     339msgid "Failed to load the torrent file %s: %s"
     340msgstr ""
     341
     342#: main.c:1113
     343#, c-format
     344msgid "%s (%s)"
     345msgstr ""
     346
    324347#: util.c:63
    325348msgid "B"
     
    350373msgstr ""
    351374
    352 #: util.c:87
     375#: util.c:88
    353376msgid "N/A"
    354377msgstr ""
    355378
    356379#. this is a UTF-8 infinity symbol
    357 #: util.c:91
     380#: util.c:92
    358381msgid "∞"
    359382msgstr ""
  • trunk/gtk/util.c

    r131 r162  
    4040
    4141static void
    42 sigexithandler(int sig);
    43 static void
    4442errcb(GtkWidget *wind, int resp, gpointer data);
    4543
     
    186184}
    187185
    188 static int exit_sigs[] = {SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGUSR1, SIGUSR2};
    189 static callbackfunc_t exit_func = NULL;
    190 static void *exit_data = NULL;
    191 static int exit_block_level = 0;
    192 
    193 void
    194 setuphandlers(callbackfunc_t func, void *data) {
    195   struct sigaction sa;
    196   unsigned int ii;
    197 
    198   exit_data = data;
    199   exit_func = func;
    200 
    201   bzero(&sa, sizeof(sa));
    202   sa.sa_handler = sigexithandler;
    203   for(ii = 0; ii < ALEN(exit_sigs); ii++)
    204     sigaction(exit_sigs[ii], &sa, NULL);
    205 }
    206 
    207 void
    208 clearhandlers(void) {
    209   struct sigaction sa;
    210   unsigned int ii;
    211 
    212   bzero(&sa, sizeof(sa));
    213   sa.sa_handler = SIG_DFL;
    214   for(ii = 0; ii < ALEN(exit_sigs); ii++)
    215     sigaction(exit_sigs[ii], &sa, NULL);
    216 }
    217 
    218 static void
    219 sigexithandler(int sig) {
    220   exit_func(exit_data);
    221   clearhandlers();
    222   raise(sig);
    223 }
    224 
    225 void
    226 blocksigs(void) {
    227   sigset_t mask;
    228   unsigned int ii;
    229 
    230   if(0 < (exit_block_level++))
    231     return;
    232 
    233   sigemptyset(&mask);
    234   for(ii = 0; ii < ALEN(exit_sigs); ii++)
    235     sigaddset(&mask, exit_sigs[ii]);
    236   sigprocmask(SIG_BLOCK, &mask, NULL);
    237 }
    238 
    239 void
    240 unblocksigs(void) {
    241   sigset_t mask;
    242   unsigned int ii;
    243 
    244   if(0 < (--exit_block_level))
    245     return;
    246 
    247   sigemptyset(&mask);
    248   for(ii = 0; ii < ALEN(exit_sigs); ii++)
    249     sigaddset(&mask, exit_sigs[ii]);
    250   sigprocmask(SIG_UNBLOCK, &mask, NULL);
    251 }
    252 
    253186GtkWidget *
    254187errmsg(GtkWindow *wind, const char *format, ...) {
  • trunk/gtk/util.h

    r131 r162  
    7171urldecode(const char *str, int len);
    7272
    73 /* set up a handler for various fatal signals */
    74 void
    75 setuphandlers(callbackfunc_t func, void *data);
    76 
    77 /* clear the handlers for fatal signals */
    78 void
    79 clearhandlers(void);
    80 
    81 /* blocks and unblocks delivery of fatal signals. calls to these
    82    functions can be nested as long as unblocksigs() is called exactly
    83    as many times as blocksigs().  only the first blocksigs() will
    84    block signals and only the last unblocksigs() will unblock them. */
    85 void
    86 blocksigs(void);
    87 void
    88 unblocksigs(void);
    89 
    9073/* if wind is NULL then you must call gtk_widget_show on the returned widget */
    9174
  • trunk/libtransmission/choking.c

    r109 r162  
    139139void tr_chokingPulse( tr_choking_t * c )
    140140{
    141     int i, peersTotalCount, unchoked, mustOptimistic = 1;
     141    int peersTotalCount, unchoked, mustOptimistic = 1;
    142142    tr_peer_t ** canChoke, ** canUnchoke;
    143143    tr_peer_t ** canChokeZero, ** canUnchokeZero;
     
    153153    /* Lock all torrents and get the total number of peers */
    154154    peersTotalCount = 0;
    155     for( i = 0; i < c->h->torrentCount; i++ )
    156     {
    157         tor = c->h->torrents[i];
     155    for( tor = c->h->torrentList; tor; tor = tor->next )
     156    {
    158157        tr_lockLock( &tor->lock );
    159158        peersTotalCount += tor->peerCount;
     
    166165    unchoked        = 0;
    167166
    168     for( i = 0; i < c->h->torrentCount; i++ )
     167    for( tor = c->h->torrentList; tor; tor = tor->next )
    169168    {
    170169        tr_peer_t * peer;
    171         int j;
    172 
    173         tor = c->h->torrents[i];
    174         for( j = 0; j < tor->peerCount; j++ )
     170        int i;
     171
     172        for( i = 0; i < tor->peerCount; i++ )
    175173        {
    176             peer = tor->peers[j];
     174            peer = tor->peers[i];
    177175
    178176            if( !tr_peerIsConnected( peer ) )
     
    321319
    322320    /* Unlock all torrents */
    323     for( i = 0; i < c->h->torrentCount; i++ )
    324     {
    325         tr_lockUnlock( &c->h->torrents[i]->lock );
     321    for( tor = c->h->torrentList; tor; tor = tor->next )
     322    {
     323        tr_lockUnlock( &tor->lock );
    326324    }
    327325
  • trunk/libtransmission/internal.h

    r65 r162  
    11/******************************************************************************
    2  * Copyright (c) 2005 Eric Petit
     2 * Copyright (c) 2005-2006 Transmission authors and contributors
    33 *
    44 * Permission is hereby granted, free of charge, to any person obtaining a
     
    8383#define TR_MAX_PEER_COUNT 60
    8484
    85 typedef struct tr_torrent_s tr_torrent_t;
    8685typedef struct tr_completion_s tr_completion_t;
    8786
     
    109108
    110109    int               status;
    111         int                               finished;
    112     char              error[128];
     110    int               error;
     111    char              trackerError[128];
     112    int               finished;
    113113
    114114    char            * id;
    115115    char            * key;
     116    int             * bindPort;
    116117
    117118    /* An escaped string used to include the hash in HTTP queries */
     
    127128    int               blockCount;
    128129   
    129 #if 0
    130     /* Status for each block
    131        -1 = we have it
    132         n = we are downloading it from n peers */
    133     char            * blockHave;
    134     int               blockHaveCount;
    135     uint8_t         * bitfield;
    136 #endif
    137130    tr_completion_t * completion;
    138131
     
    151144    uint64_t          downloaded;
    152145    uint64_t          uploaded;
     146
     147    tr_stat_t         stats[2];
     148    int               statCur;
     149
     150    tr_torrent_t    * prev;
     151    tr_torrent_t    * next;
    153152};
    154153
     
    159158{
    160159    int            torrentCount;
    161     tr_torrent_t * torrents[TR_MAX_TORRENT_COUNT];
     160    tr_torrent_t * torrentList;
    162161
    163162    tr_ratecontrol_t * upload;
  • trunk/libtransmission/tracker.c

    r61 r162  
    5858static void recvAnswer ( tr_tracker_t * tc );
    5959
    60 tr_tracker_t * tr_trackerInit( tr_handle_t * h, tr_torrent_t * tor )
     60tr_tracker_t * tr_trackerInit( tr_torrent_t * tor )
    6161{
    6262    tr_tracker_t * tc;
     
    6464    tc           = calloc( 1, sizeof( tr_tracker_t ) );
    6565    tc->tor      = tor;
    66     tc->id       = h->id;
     66    tc->id       = tor->id;
    6767
    6868    tc->started  = 1;
     
    7575    tc->buf      = malloc( tc->size );
    7676
    77     tc->bindPort = h->bindPort;
     77    tc->bindPort = *(tor->bindPort);
    7878    tc->newPort  = -1;
    7979
     
    375375    {
    376376        tr_err( "Tracker: %s", bePeers->val.s.s );
    377         tor->status |= TR_TRACKER_ERROR;
    378         snprintf( tor->error, sizeof( tor->error ),
     377        tor->error |= TR_ETRACKER;
     378        snprintf( tor->trackerError, sizeof( tor->trackerError ),
    379379                  "%s", bePeers->val.s.s );
    380380        goto cleanup;
    381381    }
    382 
    383     tor->status &= ~TR_TRACKER_ERROR;
     382    tor->error &= ~TR_ETRACKER;
    384383
    385384    if( !tc->interval )
  • trunk/libtransmission/tracker.h

    r26 r162  
    2626typedef struct tr_tracker_s tr_tracker_t;
    2727
    28 tr_tracker_t * tr_trackerInit      ( tr_handle_t *, tr_torrent_t * );
     28tr_tracker_t * tr_trackerInit      ( tr_torrent_t * );
    2929void           tr_trackerChangePort( tr_tracker_t *, int );
    3030int            tr_trackerPulse     ( tr_tracker_t * );
  • trunk/libtransmission/transmission.c

    r133 r162  
    11/******************************************************************************
    2  * Copyright (c) 2005 Eric Petit
     2 * Copyright (c) 2005-2006 Transmission authors and contributors
    33 *
    44 * Permission is hereby granted, free of charge, to any person obtaining a
     
    2626 * Local prototypes
    2727 **********************************************************************/
    28 static void torrentReallyStop( tr_handle_t * h, int t );
     28static void torrentReallyStop( tr_torrent_t * );
    2929static void  downloadLoop( void * );
    3030static void  acceptLoop( void * );
     
    8686void tr_setBindPort( tr_handle_t * h, int port )
    8787{
    88     int ii, sock;
     88    int sock;
     89    tr_torrent_t * tor;
    8990
    9091    if( h->bindPort == port )
     
    107108    h->bindPort = port;
    108109
    109     for( ii = 0; ii < h->torrentCount; ii++ )
    110     {
    111         tr_lockLock( &h->torrents[ii]->lock );
    112         if( NULL != h->torrents[ii]->tracker )
    113         {
    114             tr_trackerChangePort( h->torrents[ii]->tracker, port );
    115         }
    116         tr_lockUnlock( &h->torrents[ii]->lock );
     110    for( tor = h->torrentList; tor; tor = tor->next )
     111    {
     112        tr_lockLock( &tor->lock );
     113        if( NULL != tor->tracker )
     114        {
     115            tr_trackerChangePort( tor->tracker, port );
     116        }
     117        tr_lockUnlock( &tor->lock );
    117118    }
    118119
     
    147148{
    148149    tr_torrent_t * tor;
    149     int i;
    150150
    151151    *dl = 0.0;
    152152    *ul = 0.0;
    153     for( i = 0; i < h->torrentCount; i++ )
    154     {
    155         tor = h->torrents[i];
     153    for( tor = h->torrentList; tor; tor = tor->next )
     154    {
    156155        tr_lockLock( &tor->lock );
    157156        if( tor->status & TR_STATUS_DOWNLOAD )
     
    168167 * to fill it.
    169168 **********************************************************************/
    170 int tr_torrentInit( tr_handle_t * h, const char * path )
    171 {
    172     tr_torrent_t  * tor;
     169tr_torrent_t * tr_torrentInit( tr_handle_t * h, const char * path,
     170                               int * error )
     171{
     172    tr_torrent_t  * tor, * tor_tmp;
    173173    tr_info_t     * inf;
    174174    int             i;
    175175    char          * s1, * s2;
    176176
    177     if( h->torrentCount >= TR_MAX_TORRENT_COUNT )
    178     {
    179         tr_err( "Maximum number of torrents reached" );
    180         return 1;
    181     }
    182 
    183177    tor = calloc( sizeof( tr_torrent_t ), 1 );
    184178    inf = &tor->info;
     
    187181    if( tr_metainfoParse( inf, path ) )
    188182    {
     183        *error = TR_EINVALID;
    189184        free( tor );
    190         return 1;
     185        return NULL;
    191186    }
    192187
    193188    /* Make sure this torrent is not already open */
    194     for( i = 0; i < h->torrentCount; i++ )
    195     {
    196         if( !memcmp( tor->info.hash, h->torrents[i]->info.hash,
     189    for( tor_tmp = h->torrentList; tor_tmp; tor_tmp = tor_tmp->next )
     190    {
     191        if( !memcmp( tor->info.hash, tor_tmp->info.hash,
    197192                     SHA_DIGEST_LENGTH ) )
    198193        {
    199             tr_err( "Torrent already open" );
     194            *error = TR_EDUPLICATE;
    200195            free( tor );
    201             return 1;
     196            return NULL;
    202197        }
    203198    }
     
    206201    tor->id     = h->id;
    207202    tor->key    = h->key;
     203    tor->bindPort = &h->bindPort;
    208204        tor->finished = 0;
    209205
     
    247243    /* We have a new torrent */
    248244    tr_lockLock( &h->acceptLock );
    249     h->torrents[h->torrentCount] = tor;
     245    tor->prev = NULL;
     246    tor->next = h->torrentList;
     247    if( tor->next )
     248    {
     249        tor->next->prev = tor;
     250    }
     251    h->torrentList = tor;
    250252    (h->torrentCount)++;
    251253    tr_lockUnlock( &h->acceptLock );
    252254
    253     return 0;
     255    return tor;
     256}
     257
     258tr_info_t * tr_torrentInfo( tr_torrent_t * tor )
     259{
     260    return &tor->info;
    254261}
    255262
    256263/***********************************************************************
    257264 * tr_torrentScrape
    258  ***********************************************************************
    259  * Allocates a tr_torrent_t structure, then relies on tr_metainfoParse
    260  * to fill it.
    261  **********************************************************************/
    262 int tr_torrentScrape( tr_handle_t * h, int t, int * s, int * l )
    263 {
    264     return tr_trackerScrape( h->torrents[t], s, l );
    265 }
    266 
    267 void tr_torrentSetFolder( tr_handle_t * h, int t, const char * path )
    268 {
    269     tr_torrent_t * tor = h->torrents[t];
    270 
     265 **********************************************************************/
     266int tr_torrentScrape( tr_torrent_t * tor, int * s, int * l )
     267{
     268    return tr_trackerScrape( tor, s, l );
     269}
     270
     271void tr_torrentSetFolder( tr_torrent_t * tor, const char * path )
     272{
    271273    tor->destination = strdup( path );
    272274}
    273275
    274 char * tr_torrentGetFolder( tr_handle_t * h, int t )
    275 {
    276     tr_torrent_t * tor = h->torrents[t];
    277 
     276char * tr_torrentGetFolder( tr_torrent_t * tor )
     277{
    278278    return tor->destination;
    279279}
    280280
    281 void tr_torrentStart( tr_handle_t * h, int t )
    282 {
    283     tr_torrent_t * tor = h->torrents[t];
    284 
     281void tr_torrentStart( tr_torrent_t * tor )
     282{
    285283    if( tor->status & ( TR_STATUS_STOPPING | TR_STATUS_STOPPED ) )
    286284    {
    287285        /* Join the thread first */
    288         torrentReallyStop( h, t );
    289     }
    290 
    291     tor->status   = TR_STATUS_CHECK;
    292     tor->tracker  = tr_trackerInit( h, tor );
    293 
    294     if( 0 > h->bindPort )
    295     {
    296         tr_setBindPort( h, TR_DEFAULT_PORT );
    297     }
     286        torrentReallyStop( tor );
     287    }
     288
     289    tor->status  = TR_STATUS_CHECK;
     290    tor->tracker = tr_trackerInit( tor );
    298291
    299292    tor->date = tr_date();
     
    302295}
    303296
    304 void tr_torrentStop( tr_handle_t * h, int t )
    305 {
    306     tr_torrent_t * tor = h->torrents[t];
    307 
     297void tr_torrentStop( tr_torrent_t * tor )
     298{
    308299    tr_lockLock( &tor->lock );
    309300    tr_trackerStopped( tor->tracker );
     
    320311 * Joins the download thread and frees/closes everything related to it.
    321312 **********************************************************************/
    322 static void torrentReallyStop( tr_handle_t * h, int t )
    323 {
    324     tr_torrent_t * tor = h->torrents[t];
    325 
     313static void torrentReallyStop( tr_torrent_t * tor )
     314{
    326315    tor->die = 1;
    327316    tr_threadJoin( &tor->thread );
     
    346335}
    347336
    348 int tr_getFinished( tr_handle_t * h, int i)
    349 {
    350         return h->torrents[i]->finished;
    351 }
    352 void tr_setFinished( tr_handle_t * h, int i, int val)
    353 {
    354         h->torrents[i]->finished = val;
    355 }
    356 
    357 int tr_torrentStat( tr_handle_t * h, tr_stat_t ** stat )
     337void tr_torrentIterate( tr_handle_t * h, tr_callback_t func, void * d )
     338{
     339    tr_torrent_t * tor;
     340
     341    for( tor = h->torrentList; tor; tor = tor->next )
     342    {
     343        func( tor, d );
     344    }
     345}
     346
     347int tr_getFinished( tr_torrent_t * tor )
     348{
     349    if( tor->finished )
     350    {
     351        tor->finished = 0;
     352        return 1;
     353    }
     354    return 0;
     355}
     356
     357tr_stat_t * tr_torrentStat( tr_torrent_t * tor )
    358358{
    359359    tr_stat_t * s;
    360     tr_torrent_t * tor;
    361     tr_info_t * inf;
    362     int i, j, k, piece;
    363 
    364     if( h->torrentCount < 1 )
    365     {
    366         *stat = NULL;
    367         return 0;
    368     }
    369 
    370     s = malloc( h->torrentCount * sizeof( tr_stat_t ) );
    371 
    372     for( i = 0; i < h->torrentCount; i++ )
    373     {
    374         tor = h->torrents[i];
    375         inf = &tor->info;
    376 
    377         if( ( tor->status & TR_STATUS_STOPPED ) ||
    378             ( ( tor->status & TR_STATUS_STOPPING ) &&
    379               tr_date() > tor->stopDate + 60000 ) )
    380         {
    381             torrentReallyStop( h, i );
    382             tor->status = TR_STATUS_PAUSE;
    383         }
    384 
    385         tr_lockLock( &tor->lock );
    386 
    387         memcpy( &s[i].info, &tor->info, sizeof( tr_info_t ) );
    388         s[i].status = tor->status;
    389         memcpy( s[i].error, tor->error, sizeof( s[i].error ) );
    390 
    391         s[i].peersTotal       = 0;
    392         s[i].peersUploading   = 0;
    393         s[i].peersDownloading = 0;
    394 
     360    tr_info_t * inf = &tor->info;
     361    int i;
     362
     363    tor->statCur = ( tor->statCur + 1 ) % 2;
     364    s = &tor->stats[tor->statCur];
     365
     366    if( ( tor->status & TR_STATUS_STOPPED ) ||
     367        ( ( tor->status & TR_STATUS_STOPPING ) &&
     368          tr_date() > tor->stopDate + 60000 ) )
     369    {
     370        torrentReallyStop( tor );
     371        tor->status = TR_STATUS_PAUSE;
     372    }
     373
     374    tr_lockLock( &tor->lock );
     375
     376    s->status = tor->status;
     377    memcpy( s->trackerError, tor->trackerError,
     378            sizeof( s->trackerError ) );
     379
     380    s->peersTotal       = 0;
     381    s->peersUploading   = 0;
     382    s->peersDownloading = 0;
     383
     384    for( i = 0; i < tor->peerCount; i++ )
     385    {
     386        if( tr_peerIsConnected( tor->peers[i] ) )
     387        {
     388            (s->peersTotal)++;
     389            if( tr_peerIsUploading( tor->peers[i] ) )
     390            {
     391                (s->peersUploading)++;
     392            }
     393            if( tr_peerIsDownloading( tor->peers[i] ) )
     394            {
     395                (s->peersDownloading)++;
     396            }
     397        }
     398    }
     399
     400    s->progress = tr_cpCompletionAsFloat( tor->completion );
     401    if( tor->status & TR_STATUS_DOWNLOAD )
     402        s->rateDownload = tr_rcRate( tor->download );
     403    else
     404        /* tr_rcRate() doesn't make the difference between 'piece'
     405           messages and other messages, which causes a non-zero
     406           download rate even tough we are not downloading. So we
     407           force it to zero not to confuse the user. */
     408        s->rateDownload = 0.0;
     409    s->rateUpload = tr_rcRate( tor->upload );
     410   
     411    s->seeders    = tr_trackerSeeders(tor);
     412        s->leechers       = tr_trackerLeechers(tor);
     413
     414    if( s->rateDownload < 0.1 )
     415    {
     416        s->eta = -1;
     417    }
     418    else
     419    {
     420        s->eta = (float) ( 1.0 - s->progress ) *
     421            (float) inf->totalSize / s->rateDownload / 1024.0;
     422        if( s->eta > 99 * 3600 + 59 * 60 + 59 )
     423        {
     424            s->eta = -1;
     425        }
     426    }
     427
     428    s->downloaded = tor->downloaded;
     429    s->uploaded   = tor->uploaded;
     430
     431    tr_lockUnlock( &tor->lock );
     432
     433    return s;
     434}
     435
     436void tr_torrentAvailability( tr_torrent_t * tor, int8_t * tab, int size )
     437{
     438    int i, j, piece;
     439
     440    tr_lockLock( &tor->lock );
     441    for( i = 0; i < size; i++ )
     442    {
     443        piece = i * tor->info.pieceCount / size;
     444
     445        if( tr_cpPieceIsComplete( tor->completion, piece ) )
     446        {
     447            tab[i] = -1;
     448            continue;
     449        }
     450
     451        tab[i] = 0;
    395452        for( j = 0; j < tor->peerCount; j++ )
    396453        {
    397             if( tr_peerIsConnected( tor->peers[j] ) )
     454            if( tr_peerBitfield( tor->peers[j] ) &&
     455                tr_bitfieldHas( tr_peerBitfield( tor->peers[j] ), piece ) )
    398456            {
    399                 (s[i].peersTotal)++;
    400                 if( tr_peerIsUploading( tor->peers[j] ) )
    401                 {
    402                     (s[i].peersUploading)++;
    403                 }
    404                 if( tr_peerIsDownloading( tor->peers[j] ) )
    405                 {
    406                     (s[i].peersDownloading)++;
    407                 }
     457                (tab[i])++;
    408458            }
    409459        }
    410 
    411         s[i].progress     = tr_cpCompletionAsFloat( tor->completion );
    412         if( tor->status & TR_STATUS_DOWNLOAD )
    413             s[i].rateDownload = tr_rcRate( tor->download );
    414         else
    415             /* tr_rcRate() doesn't make the difference between 'piece'
    416                messages and other messages, which causes a non-zero
    417                download rate even tough we are not downloading. So we
    418                force it to zero not to confuse the user. */
    419             s[i].rateDownload = 0.0;
    420         s[i].rateUpload = tr_rcRate( tor->upload );
    421        
    422         s[i].seeders      = tr_trackerSeeders(tor);
    423                 s[i].leechers     = tr_trackerLeechers(tor);
    424 
    425         if( s[i].rateDownload < 0.1 )
    426         {
    427             s[i].eta = -1;
    428         }
    429         else
    430         {
    431             s[i].eta = (float) ( 1.0 - s[i].progress ) *
    432                 (float) inf->totalSize / s[i].rateDownload / 1024.0;
    433             if( s[i].eta > 99 * 3600 + 59 * 60 + 59 )
    434             {
    435                 s[i].eta = -1;
    436             }
    437         }
    438 
    439         for( j = 0; j < 120; j++ )
    440         {
    441             piece = j * inf->pieceCount / 120;
    442 
    443             if( tr_cpPieceIsComplete( tor->completion, piece ) )
    444             {
    445                 s[i].pieces[j] = -1;
    446                 continue;
    447             }
    448 
    449             s[i].pieces[j] = 0;
    450            
    451             for( k = 0; k < tor->peerCount; k++ )
    452             {
    453                 if( tr_peerBitfield( tor->peers[k] ) &&
    454                     tr_bitfieldHas( tr_peerBitfield( tor->peers[k] ), piece ) )
    455                 {
    456                     (s[i].pieces[j])++;
    457                 }
    458             }
    459         }
    460 
    461         s[i].downloaded = tor->downloaded;
    462         s[i].uploaded   = tor->uploaded;
    463 
    464         s[i].folder = tor->destination;
    465 
    466         tr_lockUnlock( &tor->lock );
    467     }
    468 
    469     *stat = s;
    470     return h->torrentCount;
     460    }
     461    tr_lockUnlock( &tor->lock );
    471462}
    472463
     
    476467 * Frees memory allocated by tr_torrentInit.
    477468 **********************************************************************/
    478 void tr_torrentClose( tr_handle_t * h, int t )
    479 {
    480     tr_torrent_t * tor = h->torrents[t];
    481     tr_info_t    * inf = &tor->info;
     469void tr_torrentClose( tr_handle_t * h, tr_torrent_t * tor )
     470{
     471    tr_info_t * inf = &tor->info;
    482472
    483473    if( tor->status & ( TR_STATUS_STOPPING | TR_STATUS_STOPPED ) )
    484474    {
    485475        /* Join the thread first */
    486         torrentReallyStop( h, t );
     476        torrentReallyStop( tor );
    487477    }
    488478
     
    503493    free( inf->pieces );
    504494    free( inf->files );
     495
     496    if( tor->prev )
     497    {
     498        tor->prev->next = tor->next;
     499    }
     500    else
     501    {
     502        h->torrentList = tor->next;
     503    }
     504    if( tor->next )
     505    {
     506        tor->next->prev = tor->prev;
     507    }
    505508    free( tor );
    506 
    507     memmove( &h->torrents[t], &h->torrents[t+1],
    508              ( h->torrentCount - t ) * sizeof( void * ) );
    509509
    510510    tr_lockUnlock( &h->acceptLock );
     
    596596    tr_handle_t * h = _h;
    597597    uint64_t      date1, date2, lastchoke = 0;
    598     int           ii, jj;
     598    int           ii;
    599599    uint8_t     * hash;
     600    tr_torrent_t * tor;
    600601
    601602    tr_dbg( "Accept thread started" );
     
    641642            if( NULL != ( hash = tr_peerHash( h->acceptPeers[ii] ) ) )
    642643            {
    643                 for( jj = 0; jj < h->torrentCount; jj++ )
     644                for( tor = h->torrentList; tor; tor = tor->next )
    644645                {
    645                     tr_lockLock( &h->torrents[jj]->lock );
    646                     if( 0 == memcmp( h->torrents[jj]->info.hash, hash,
     646                    tr_lockLock( &tor->lock );
     647                    if( 0 == memcmp( tor->info.hash, hash,
    647648                                     SHA_DIGEST_LENGTH ) )
    648649                    {
    649                       tr_peerAttach( h->torrents[jj], h->acceptPeers[ii] );
    650                       tr_lockUnlock( &h->torrents[jj]->lock );
     650                      tr_peerAttach( tor, h->acceptPeers[ii] );
     651                      tr_lockUnlock( &tor->lock );
    651652                      goto removePeer;
    652653                    }
    653                     tr_lockUnlock( &h->torrents[jj]->lock );
     654                    tr_lockUnlock( &tor->lock );
    654655                }
    655656                tr_peerDestroy( h->fdlimit, h->acceptPeers[ii] );
  • trunk/libtransmission/transmission.h

    r20 r162  
    11/******************************************************************************
    2  * Copyright (c) 2005 Eric Petit
     2 * Copyright (c) 2005-2006 Transmission authors and contributors
    33 *
    44 * Permission is hereby granted, free of charge, to any person obtaining a
     
    3030#include <inttypes.h>
    3131
    32 #define SHA_DIGEST_LENGTH    20
     32#define SHA_DIGEST_LENGTH 20
    3333#ifdef __BEOS__
    3434# include <StorageDefs.h>
    35 # define MAX_PATH_LENGTH B_FILE_NAME_LENGTH
     35# define MAX_PATH_LENGTH  B_FILE_NAME_LENGTH
    3636#else
    37 # define MAX_PATH_LENGTH      1024
    38 #endif
    39 #define TR_MAX_TORRENT_COUNT 50
    40 
    41 #define TR_DEFAULT_PORT      9090
     37# define MAX_PATH_LENGTH  1024
     38#endif
     39
     40#define TR_DEFAULT_PORT   9090
     41#define TR_NOERROR        0
    4242
    4343/***********************************************************************
     
    4848 **********************************************************************/
    4949typedef struct tr_handle_s tr_handle_t;
    50 
    5150tr_handle_t * tr_init();
    5251
     
    7675
    7776/***********************************************************************
     77 * tr_torrentCount
     78 ***********************************************************************
     79 * Returns the count of open torrents
     80 **********************************************************************/
     81int tr_torrentCount( tr_handle_t * h );
     82
     83/***********************************************************************
     84 * tr_torrentIterate
     85 ***********************************************************************
     86 * Iterates on open torrents
     87 **********************************************************************/
     88typedef struct tr_torrent_s tr_torrent_t;
     89typedef void (*tr_callback_t) ( tr_torrent_t *, void * );
     90void tr_torrentIterate( tr_handle_t *, tr_callback_t, void * );
     91
     92/***********************************************************************
    7893 * tr_torrentRates
    7994 ***********************************************************************
     
    8398
    8499/***********************************************************************
    85  * tr_getFinished
    86  ***********************************************************************
    87  * Tests to see if torrent is finished
    88  **********************************************************************/
    89 int tr_getFinished( tr_handle_t *, int );
    90 
    91 /***********************************************************************
    92  * tr_setFinished
    93  ***********************************************************************
    94  * Sets the boolean value finished in the torrent back to false
    95  **********************************************************************/
    96 void tr_setFinished( tr_handle_t *, int, int );
     100 * tr_close
     101 ***********************************************************************
     102 * Frees memory allocated by tr_init.
     103 **********************************************************************/
     104void tr_close( tr_handle_t * );
    97105
    98106/***********************************************************************
     
    100108 ***********************************************************************
    101109 * Opens and parses torrent file at 'path'. If the file exists and is a
    102  * valid torrent file, returns 0 and adds it to the list of torrents
    103  * (but doesn't start it). Returns a non-zero value otherwise.
    104  **********************************************************************/
    105 int tr_torrentInit( tr_handle_t *, const char * path );
     110 * valid torrent file, returns an handle and adds it to the list of
     111 * torrents (but doesn't start it). Returns NULL and sets *error
     112 * otherwise.
     113 **********************************************************************/
     114#define TR_EINVALID     1
     115#define TR_EUNSUPPORTED 2
     116#define TR_EDUPLICATE   3
     117#define TR_EOTHER       666
     118tr_torrent_t * tr_torrentInit( tr_handle_t *, const char * path,
     119                               int * error );
     120
     121typedef struct tr_info_s tr_info_t;
     122tr_info_t * tr_torrentInfo( tr_torrent_t * );
    106123
    107124/***********************************************************************
     
    114131 * before returning.
    115132 **********************************************************************/
    116 int tr_torrentScrape( tr_handle_t *, int, int * s, int * l );
     133int tr_torrentScrape( tr_torrent_t *, int * s, int * l );
    117134
    118135/***********************************************************************
     
    122139 * therefore tr_torrentStart returns immediately.
    123140 **********************************************************************/
    124 void   tr_torrentSetFolder( tr_handle_t *, int, const char * );
    125 char * tr_torrentGetFolder( tr_handle_t *, int );
    126 void   tr_torrentStart( tr_handle_t *, int );
     141void   tr_torrentSetFolder( tr_torrent_t *, const char * );
     142char * tr_torrentGetFolder( tr_torrent_t * );
     143void   tr_torrentStart( tr_torrent_t * );
    127144
    128145/***********************************************************************
     
    137154 *   waiting any further.
    138155 **********************************************************************/
    139 void tr_torrentStop( tr_handle_t *, int );
     156void tr_torrentStop( tr_torrent_t * );
     157
     158/***********************************************************************
     159 * tr_getFinished
     160 ***********************************************************************
     161 * The first call after a torrent is completed returns 1. Returns 0
     162 * in other cases.
     163 **********************************************************************/
     164int tr_getFinished( tr_torrent_t * );
    140165
    141166/***********************************************************************
    142167 * tr_torrentStat
    143168 ***********************************************************************
    144  * Allocates an array of tr_stat_t structures, containing information
    145  * about the current status of all open torrents (see the contents
    146  * of tr_stat_s below). Returns the count of open torrents and sets
    147  * (*s) to the address of the array, or NULL if no torrent is open.
    148  * In the former case, the array belongs to the caller who is
    149  * responsible of freeing it.
    150  * The interface should call this function every 0.5 second or so in
    151  * order to update itself.
     169 * Returns a pointer to an tr_stat_t structure with updated information
     170 * on the torrent. The structure belongs to libtransmission (do not
     171 * free it) and is guaranteed to be unchanged until the next call to
     172 * tr_torrentStat.
     173 * The interface should call this function every second or so in order
     174 * to update itself.
    152175 **********************************************************************/
    153176typedef struct tr_stat_s tr_stat_t;
    154 
    155 int tr_torrentCount( tr_handle_t * h );
    156 int tr_torrentStat( tr_handle_t *, tr_stat_t ** s );
     177tr_stat_t * tr_torrentStat( tr_torrent_t * );
     178
     179/***********************************************************************
     180 * tr_torrentAvailability
     181 ***********************************************************************
     182 * Use this to draw an advanced progress bar which is 'size' pixels
     183 * wide. Fills 'tab' which you must have allocated: each byte is set
     184 * to either -1 if we have the piece, otherwise it is set to the number
     185 * of connected peers who have the piece.
     186 **********************************************************************/
     187void tr_torrentAvailability( tr_torrent_t *, int8_t * tab, int size );
    157188
    158189/***********************************************************************
     
    162193 * you must call tr_torrentStop() before closing it.
    163194 **********************************************************************/
    164 void tr_torrentClose( tr_handle_t *, int );
    165 
    166 /***********************************************************************
    167  * tr_close
    168  ***********************************************************************
    169  * Frees memory allocated by tr_init.
    170  **********************************************************************/
    171 void tr_close( tr_handle_t * );
    172 
    173 
    174 /***********************************************************************
    175  * tr_stat_s
     195void tr_torrentClose( tr_handle_t *, tr_torrent_t * );
     196
     197
     198/***********************************************************************
     199 * tr_info_s
    176200 **********************************************************************/
    177201typedef struct tr_file_s
     
    181205}
    182206tr_file_t;
    183 
    184 typedef struct tr_info_s
     207struct tr_info_s
    185208{
    186209    /* Path to torrent */
     
    205228    int         fileCount;
    206229    tr_file_t * files;
    207 }
    208 tr_info_t;
    209 
     230};
     231
     232/***********************************************************************
     233 * tr_stat_s
     234 **********************************************************************/
    210235struct tr_stat_s
    211236{
    212     tr_info_t   info;
    213 
    214237#define TR_STATUS_CHECK    0x001 /* Checking files */
    215238#define TR_STATUS_DOWNLOAD 0x002 /* Downloading */
     
    219242                                    running (for internal use only) */
    220243#define TR_STATUS_PAUSE    0x020 /* Paused */
    221 #define TR_TRACKER_ERROR   0x100
     244
     245#define TR_STATUS_ACTIVE   (TR_STATUS_CHECK|TR_STATUS_DOWNLOAD|TR_STATUS_SEED)
     246#define TR_STATUS_INACTIVE (TR_STATUS_STOPPING|TR_STATUS_STOPPED|TR_STATUS_PAUSE)
    222247    int         status;
    223     char        error[128];
     248
     249#define TR_ETRACKER 1
     250#define TR_EINOUT   2
     251    int         error;
     252    char        trackerError[128];
    224253
    225254    float       progress;
     
    230259    int         peersUploading;
    231260    int         peersDownloading;
    232     char        pieces[120];
    233261    int         seeders;
    234262    int         leechers;
     
    236264    uint64_t    downloaded;
    237265    uint64_t    uploaded;
    238 
    239     char      * folder;
    240266};
    241267
  • trunk/macosx/Badger.m

    r103 r162  
    1 //
    2 //  Badger.m
    3 //  Transmission
    4 //
    5 //  Created by Mitchell Livingston on 12/21/05.
    6 //
     1/******************************************************************************
     2 * Copyright (c) 2005-2006 Transmission authors and contributors
     3 *
     4 * Permission is hereby granted, free of charge, to any person obtaining a
     5 * copy of this software and associated documentation files (the "Software"),
     6 * to deal in the Software without restriction, including without limitation
     7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     8 * and/or sell copies of the Software, and to permit persons to whom the
     9 * Software is furnished to do so, subject to the following conditions:
     10 *
     11 * The above copyright notice and this permission notice shall be included in
     12 * all copies or substantial portions of the Software.
     13 *
     14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     20 * DEALINGS IN THE SOFTWARE.
     21 *****************************************************************************/
    722
    823#import "Badger.h"
     24#import "StringAdditions.h"
    925
    1026@interface Badger (Private)
     
    88104           
    89105            //place badge text
    90             [self badgeString: [NSString stringWithFormat: @"%d", completed]
     106            [self badgeString: [NSString stringWithInt: completed]
    91107                        forRect: badgeRect];
    92108                       
  • trunk/macosx/Controller.h

    r107 r162  
    11/******************************************************************************
    2  * Copyright (c) 2005 Eric Petit
     2 * Copyright (c) 2005-2006 Transmission authors and contributors
    33 *
    44 * Permission is hereby granted, free of charge, to any person obtaining a
     
    3333@interface Controller : NSObject
    3434{
    35     tr_handle_t                 * fHandle;
     35    tr_handle_t                 * fLib;
    3636    int                         fCount, fSeeding, fDownloading, fCompleted;
    37     tr_stat_t                   * fStat;
    38     int                         fResumeOnWake[TR_MAX_TORRENT_COUNT];
     37    NSMutableArray              * fTorrents;
    3938
    4039    NSToolbar                   * fToolbar;
     
    128127- (void) notifyGrowl:       (NSString *) file;
    129128- (void) revealFromMenu:    (id) sender;
    130 - (void) finderReveal:      (NSString *) path;
    131 - (void) finderTrash:       (NSString *) path;
    132129- (void) growlRegister:     (id) sender;
    133130
  • trunk/macosx/Controller.m

    r126 r162  
    11/******************************************************************************
    2  * Copyright (c) 2005 Eric Petit
     2 * Copyright (c) 2005-2006 Transmission authors and contributors
    33 *
    44 * Permission is hereby granted, free of charge, to any person obtaining a
     
    2323#import <IOKit/IOMessage.h>
    2424
    25 #import "NameCell.h"
    26 #import "ProgressCell.h"
     25#import "Controller.h"
     26#import "Torrent.h"
     27#import "TorrentCell.h"
    2728#import "StringAdditions.h"
    2829#import "Utils.h"
     
    5354@implementation Controller
    5455
     56- (id) init
     57{
     58    fLib      = tr_init();
     59    fTorrents = [[NSMutableArray alloc] initWithCapacity: 10];
     60    return self;
     61}
     62
     63- (void) dealloc
     64{
     65    [fTorrents release];
     66    tr_close( fLib );
     67    [super dealloc];
     68}
     69
    5570- (void) awakeFromNib
    5671{
    5772    [fWindow setContentMinSize: NSMakeSize( 400, 120 )];
    58    
    59     fHandle = tr_init();
    60 
    61     [fPrefsController setPrefsWindow: fHandle];
     73
     74    [fPrefsController setPrefsWindow: fLib];
    6275    fDefaults = [NSUserDefaults standardUserDefaults];
    63    
     76
    6477    [fInfoPanel setFrameAutosaveName:@"InfoPanel"];
    6578
     
    6780    [fAdvancedBarItem setState: [fDefaults
    6881        boolForKey:@"UseAdvancedBar"] ? NSOnState : NSOffState];
    69    
     82
    7083    fToolbar = [[NSToolbar alloc] initWithIdentifier: @"Transmission Toolbar"];
    7184    [fToolbar setDelegate: self];
     
    7588    [fWindow setDelegate: self];
    7689
    77     NSTableColumn * tableColumn;
    78     NameCell * nameCell = [[NameCell alloc] init];
    79     ProgressCell * progressCell = [[ProgressCell alloc] init];
    80 
    81     tableColumn  = [fTableView tableColumnWithIdentifier: @"Name"];
    82     [tableColumn setDataCell: nameCell];
    83 
    84     tableColumn  = [fTableView tableColumnWithIdentifier: @"Progress"];
    85     [tableColumn setDataCell: progressCell];
    86 
    87     [fTableView setAutosaveTableColumns: YES];
    88     //[fTableView sizeToFit];
     90    [fTableView setTorrents: fTorrents];
     91    [[fTableView tableColumnWithIdentifier: @"Torrent"] setDataCell:
     92        [[TorrentCell alloc] init]];
    8993
    9094    [fTableView registerForDraggedTypes: [NSArray arrayWithObjects:
     
    109113    NSDictionary * dic;
    110114
     115    Torrent * torrent;
    111116    NSEnumerator * enumerator = [[fDefaults arrayForKey: @"History"] objectEnumerator];
    112117    while ((dic = [enumerator nextObject]))
     
    115120        downloadFolder = [dic objectForKey: @"DownloadFolder"];
    116121        paused         = [dic objectForKey: @"Paused"];
    117            
     122
    118123        if (!torrentPath || !downloadFolder || !paused)
    119124            continue;
    120125
    121         if (tr_torrentInit(fHandle, [torrentPath UTF8String]))
     126        torrent = [[Torrent alloc] initWithPath: torrentPath lib: fLib];
     127        if( !torrent )
    122128            continue;
    123129
    124         tr_torrentSetFolder( fHandle, tr_torrentCount( fHandle ) - 1,
    125                                 [downloadFolder UTF8String] );
     130        [fTorrents addObject: torrent];
     131
     132        [torrent setFolder: downloadFolder];
    126133
    127134        if ([paused isEqualToString: @"NO"])
    128             tr_torrentStart( fHandle, tr_torrentCount( fHandle ) - 1 );
     135            [torrent start];
    129136    }
    130137
     
    138145    //initialize badging
    139146    fBadger = [[Badger alloc] init];
    140    
     147
    141148    //update the interface every 500 ms
    142     fCount = 0;
    143149    fDownloading = 0;
    144150    fSeeding = 0;
    145151    fCompleted = 0;
    146     fStat  = nil;
    147152    [self updateUI: nil];
    148153    fTimer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target: self
     
    185190- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
    186191{
    187     int active = fDownloading + fSeeding;
     192    int active = 0;
     193    Torrent * torrent;
     194    NSEnumerator * enumerator = [fTorrents objectEnumerator];
     195    while( ( torrent = [enumerator nextObject] ) )
     196    {
     197        if( [torrent isActive] )
     198        {
     199            active++;
     200        }
     201    }
     202
    188203    if (active > 0 && [fDefaults boolForKey: @"CheckQuit"])
    189204    {
     
    200215                            nil, nil, message);
    201216        return NSTerminateLater;
    202     }                                                                           
    203    
     217    }
     218
    204219    return NSTerminateNow;
    205220}
     
    215230- (void) applicationWillTerminate: (NSNotification *) notification
    216231{
    217     int i;
    218    
     232    NSEnumerator * enumerator;
     233    Torrent * torrent;
     234
    219235    // Stop updating the interface
    220236    [fTimer invalidate];
     
    223239    //clear badge
    224240    [fBadger clearBadge];
    225     [fBadger release];                                                         
     241    [fBadger release];
    226242
    227243    // Save history
    228244    [self updateTorrentHistory];
    229    
     245
    230246    // Stop running torrents
    231     for( i = 0; i < fCount; i++ )
    232         if( fStat[i].status & ( TR_STATUS_CHECK | TR_STATUS_DOWNLOAD |
    233                     TR_STATUS_SEED ) )
    234             tr_torrentStop( fHandle, i );
     247    enumerator = [fTorrents objectEnumerator];
     248    while( ( torrent = [enumerator nextObject] ) )
     249    {
     250        [torrent stop];
     251    }
    235252
    236253    // Wait for torrents to stop (5 seconds timeout)
    237254    NSDate * start = [NSDate date];
    238     while( fCount > 0 )
    239     {
     255    while( [fTorrents count] )
     256    {
     257        torrent = [fTorrents objectAtIndex: 0];
    240258        while( [[NSDate date] timeIntervalSinceDate: start] < 5 &&
    241                 !( fStat[0].status & TR_STATUS_PAUSE ) )
     259                ![torrent isPaused] )
    242260        {
    243261            usleep( 100000 );
    244             tr_torrentStat( fHandle, &fStat );
    245         }
    246         tr_torrentClose( fHandle, 0 );
    247         fCount = tr_torrentStat( fHandle, &fStat );
    248     }
    249 
    250     tr_close( fHandle );
     262            [torrent update];
     263        }
     264        [fTorrents removeObject: torrent];
     265        [torrent release];
     266    }
    251267}
    252268
     
    258274        [fPrefsWindow center];
    259275    }
    260    
     276
    261277    [fPrefsWindow makeKeyAndOrderFront:NULL];
    262278}
     
    265281    contextInfo: (void *) info
    266282{
     283    Torrent * torrent = [fTorrents lastObject];
    267284    if (code == NSOKButton)
    268285    {
    269         tr_torrentSetFolder( fHandle, tr_torrentCount( fHandle ) - 1,
    270                          [[[s filenames] objectAtIndex: 0] UTF8String] );
    271         tr_torrentStart( fHandle, tr_torrentCount( fHandle ) - 1 );
     286        [torrent setFolder: [[s filenames] objectAtIndex: 0]];
     287        [torrent start];
    272288    }
    273289    else
    274290    {
    275         tr_torrentClose( fHandle, tr_torrentCount( fHandle ) - 1 );
     291        [torrent release];
    276292    }
    277293    [NSApp stopModal];
     
    282298{
    283299    NSString * downloadChoice, * downloadFolder, * torrentPath;
     300    Torrent * torrent;
    284301
    285302    downloadChoice = [fDefaults stringForKey: @"DownloadChoice"];
     
    289306    while ((torrentPath = [enumerator nextObject]))
    290307    {
    291         if( tr_torrentInit( fHandle, [torrentPath UTF8String] ) )
     308        torrent = [[Torrent alloc] initWithPath: torrentPath lib: fLib];
     309        if( !torrent )
    292310            continue;
     311        [fTorrents addObject: torrent];
    293312
    294313        /* Add it to the "File > Open Recent" menu */
     
    298317        if( [downloadChoice isEqualToString: @"Constant"] )
    299318        {
    300             tr_torrentSetFolder( fHandle, tr_torrentCount( fHandle ) - 1,
    301                                  [[downloadFolder stringByExpandingTildeInPath] UTF8String] );
    302             tr_torrentStart( fHandle, tr_torrentCount( fHandle ) - 1 );
     319            [torrent setFolder: [downloadFolder stringByExpandingTildeInPath]];
     320            [torrent start];
    303321        }
    304322        else if( [downloadChoice isEqualToString: @"Torrent"] )
    305323        {
    306             tr_torrentSetFolder( fHandle, tr_torrentCount( fHandle ) - 1,
    307                 [[torrentPath stringByDeletingLastPathComponent] UTF8String] );
    308             tr_torrentStart( fHandle, tr_torrentCount( fHandle ) - 1 );
     324            [torrent setFolder: [torrentPath stringByDeletingLastPathComponent]];
     325            [torrent start];
    309326        }
    310327        else
    311328        {
    312329            NSOpenPanel * panel = [NSOpenPanel openPanel];
    313            
     330
    314331            [panel setPrompt: @"Select Download Folder"];
    315332            [panel setMessage: [NSString stringWithFormat:
     
    356373    panel     = [NSOpenPanel openPanel];
    357374    fileTypes = [NSArray arrayWithObject: @"torrent"];
    358    
     375
    359376    [panel setAllowsMultipleSelection: YES];
    360377    [panel setCanChooseFiles:          YES];
     
    394411- (void) resumeAllTorrents: (id) sender
    395412{
    396     int i;
    397     for ( i = 0; i < fCount; i++)
    398     {
    399         if ( fStat[i].status & ( TR_STATUS_STOPPING
    400         | TR_STATUS_PAUSE | TR_STATUS_STOPPED ) )
    401         {
    402             [self resumeTorrentWithIndex: i];
    403         }
    404     }
     413    Torrent * torrent;
     414    NSEnumerator * enumerator = [fTorrents objectEnumerator];
     415    while( ( torrent = [enumerator nextObject] ) )
     416    {
     417        [torrent start];
     418    }
     419    [self updateUI: nil];
     420    [self updateTorrentHistory];
    405421}
    406422
    407423- (void) resumeTorrentWithIndex: (int) idx
    408424{
    409     tr_torrentStart( fHandle, idx );
    410     [self updateUI: NULL];
     425    Torrent * torrent = [fTorrents objectAtIndex: idx];
     426    [torrent start];
     427    [self updateUI: nil];
    411428    [self updateTorrentHistory];
    412429}
     
    419436- (void) stopAllTorrents: (id) sender
    420437{
    421     int i;
    422     for ( i = 0; i < fCount; i++)
    423     {
    424         if ( fStat[i].status & ( TR_STATUS_CHECK
    425         | TR_STATUS_DOWNLOAD | TR_STATUS_SEED) )
    426         {
    427             [self stopTorrentWithIndex: i];
    428         }
    429     }
     438    Torrent * torrent;
     439    NSEnumerator * enumerator = [fTorrents objectEnumerator];
     440    while( ( torrent = [enumerator nextObject] ) )
     441    {
     442        [torrent stop];
     443    }
     444    [self updateUI: nil];
     445    [self updateTorrentHistory];
    430446}
    431447
    432448- (void) stopTorrentWithIndex: (int) idx
    433449{
    434     tr_torrentStop( fHandle, idx );
    435     [self updateUI: NULL];
     450    Torrent * torrent = [fTorrents objectAtIndex: idx];
     451    [torrent stop];
     452    [self updateUI: nil];
    436453    [self updateTorrentHistory];
    437454}
     
    441458                     deleteData: (BOOL) deleteData
    442459{
    443     if ( fStat[idx].status & ( TR_STATUS_CHECK
    444         | TR_STATUS_DOWNLOAD | TR_STATUS_SEED )  )
    445     {
    446         if ([fDefaults boolForKey: @"CheckRemove"])
    447         {
    448             NSDictionary * dict = [NSDictionary dictionaryWithObjectsAndKeys:
    449                         [NSString stringWithFormat: @"%d", idx], @"Index",
    450                         [NSString stringWithFormat: @"%d", deleteTorrent], @"DeleteTorrent",
    451                         [NSString stringWithFormat: @"%d", deleteData], @"DeleteData",
    452                         nil];
    453             [dict retain];
    454            
    455             NSBeginAlertSheet(@"Confirm Remove",
    456                                 @"Remove", @"Cancel", nil,
    457                                 fWindow, self,
    458                                 @selector(removeSheetDidEnd:returnCode:contextInfo:),
    459                                 NULL, dict, @"This torrent is active. Do you really want to remove it?");
    460             return;
    461         }
    462         //stop if not stopped
    463         else
    464             [self stopTorrentWithIndex:idx];
    465     }
    466    
    467     [self confirmRemoveTorrentWithIndex: idx
    468             deleteTorrent: deleteTorrent
    469             deleteData: deleteData];
     460    Torrent * torrent = [fTorrents objectAtIndex: idx];
     461
     462    if( [torrent isActive] && [fDefaults boolForKey: @"CheckRemove"] )
     463    {
     464        NSDictionary * dict = [NSDictionary dictionaryWithObjectsAndKeys:
     465            [NSString stringWithInt: idx], @"Index",
     466            [NSString stringWithInt: deleteTorrent], @"DeleteTorrent",
     467            [NSString stringWithInt: deleteData], @"DeleteData",
     468            nil];
     469        [dict retain];
     470
     471        NSBeginAlertSheet(@"Confirm Remove",
     472            @"Remove", @"Cancel", nil, fWindow, self,
     473            @selector(removeSheetDidEnd:returnCode:contextInfo:),  NULL, dict,
     474            @"This torrent is active. Do you really want to remove it?");
     475    }
     476    else
     477    {
     478        [self confirmRemoveTorrentWithIndex: idx
     479                deleteTorrent: deleteTorrent
     480                deleteData: deleteData];
     481    }
    470482}
    471483
     
    474486{
    475487    [NSApp stopModal];
    476     if (returnCode != NSAlertDefaultReturn)
    477     {
    478         [dict release];
    479         return;
    480     }
    481    
    482     int idx = [[dict objectForKey:@"Index"] intValue];
    483    
    484     [self stopTorrentWithIndex:idx];
    485 
    486     [self confirmRemoveTorrentWithIndex: idx
    487         deleteTorrent: [[dict objectForKey:@"DeleteTorrent"] intValue]
    488         deleteData: [[dict objectForKey:@"DeleteData"] intValue]];
     488
     489    if( returnCode == NSAlertDefaultReturn )
     490    {
     491        int idx = [[dict objectForKey:@"Index"] intValue];
     492
     493        [self confirmRemoveTorrentWithIndex: idx
     494            deleteTorrent: [[dict objectForKey:@"DeleteTorrent"] intValue]
     495            deleteData: [[dict objectForKey:@"DeleteData"] intValue]];
     496    }
     497
    489498    [dict release];
    490499}
    491                      
     500
    492501- (void) confirmRemoveTorrentWithIndex: (int) idx
    493502            deleteTorrent: (BOOL) deleteTorrent
    494503            deleteData: (BOOL) deleteData
    495504{
     505    Torrent * torrent = [fTorrents objectAtIndex: idx];
     506
     507    /* Pause if not paused already */
     508    [torrent stop];
     509
    496510    if( deleteData )
    497511    {
    498         [self finderTrash: [NSString stringWithFormat: @"%@/%@",
    499             [NSString stringWithUTF8String: fStat[idx].folder],
    500             [NSString stringWithUTF8String: fStat[idx].info.name]]];
     512        [torrent trashData];
    501513    }
    502514    if( deleteTorrent )
    503515    {
    504         [self finderTrash: [NSString stringWithUTF8String:
    505             fStat[idx].info.torrent]];
    506     }
    507    
    508     tr_torrentClose( fHandle, idx );
    509     [self updateUI: NULL];
     516        [torrent trashTorrent];
     517    }
     518
     519    [fTorrents removeObject: torrent];
     520    [torrent release];
     521
     522    [self updateUI: nil];
    510523    [self updateTorrentHistory];
    511524}
     
    546559{
    547560    float dl, ul;
    548     int row, i;
    549 
    550     //Update the NSTableView
    551     if (fStat)
    552         free(fStat);
    553        
    554     fCount = tr_torrentStat( fHandle, &fStat );
    555     fDownloading = 0;
    556     fSeeding = 0;
    557     [fTableView updateUI: fStat];
     561    NSEnumerator * enumerator;
     562    Torrent * torrent;
     563
     564    /* Update the TableView */
     565    enumerator = [fTorrents objectEnumerator];
     566    while( ( torrent = [enumerator nextObject] ) )
     567    {
     568        [torrent update];
     569
     570        if( [torrent justFinished] )
     571        {
     572            /* Notifications */
     573            [self notifyGrowl: [torrent name]];
     574            if( ![fWindow isKeyWindow] )
     575            {
     576                fCompleted++;
     577            }
     578        }
     579    }
     580    [fTableView reloadData];
    558581
    559582    //Update the global DL/UL rates
    560     tr_torrentRates( fHandle, &dl, &ul );
     583    tr_torrentRates( fLib, &dl, &ul );
    561584    NSString * downloadRate = [NSString stringForSpeed: dl];
    562585    NSString * uploadRate = [NSString stringForSpeed: ul];
     
    564587    [fTotalULField setStringValue: uploadRate];
    565588
     589#if 0
    566590    //Update DL/UL totals in the Info panel
    567591    row = [fTableView selectedRow];
     
    573597            [NSString stringForFileSize: fStat[row].uploaded]];
    574598    }
    575    
    576     //check if torrents have recently ended.
    577     for (i = 0; i < fCount; i++)
    578     {
    579         if (fStat[i].status & (TR_STATUS_CHECK | TR_STATUS_DOWNLOAD))
    580             fDownloading++;
    581         else if (fStat[i].status & TR_STATUS_SEED)
    582             fSeeding++;
    583 
    584         if( !tr_getFinished( fHandle, i ) )
    585             continue;
    586 
    587         if( ![fWindow isKeyWindow] )
    588             fCompleted++;
    589         [self notifyGrowl: [NSString stringWithUTF8String:
    590             fStat[i].info.name]];
    591         tr_setFinished( fHandle, i, 0 );
    592     }
     599#endif
    593600
    594601    //badge dock
     
    602609- (void) updateTorrentHistory
    603610{
    604     if( !fStat )
    605         return;
    606 
    607     NSMutableArray * history = [NSMutableArray arrayWithCapacity: fCount];
    608 
    609     int i;
    610     for (i = 0; i < fCount; i++)
     611    NSMutableArray * history = [NSMutableArray
     612        arrayWithCapacity: [fTorrents count]];
     613
     614    NSEnumerator * enumerator = [fTorrents objectEnumerator];
     615    Torrent * torrent;
     616    while( ( torrent = [enumerator nextObject] ) )
     617    {
    611618        [history addObject: [NSDictionary dictionaryWithObjectsAndKeys:
    612             [NSString stringWithUTF8String: fStat[i].info.torrent],
    613             @"TorrentPath",
    614             [NSString stringWithUTF8String: tr_torrentGetFolder( fHandle, i )],
    615             @"DownloadFolder",
    616             fStat[i].status & (TR_STATUS_CHECK | TR_STATUS_DOWNLOAD |
    617               TR_STATUS_SEED) ? @"NO" : @"YES",
    618             @"Paused",
     619            [torrent path],                      @"TorrentPath",
     620            [torrent getFolder],                 @"DownloadFolder",
     621            [torrent isActive] ? @"NO" : @"YES", @"Paused",
    619622            nil]];
     623    }
    620624
    621625    [fDefaults setObject: history forKey: @"History"];
     
    624628- (int) numberOfRowsInTableView: (NSTableView *) t
    625629{
    626     return fCount;
     630    return [fTorrents count];
    627631}
    628632
     
    630634    (NSTableColumn *) tableColumn row: (int) rowIndex
    631635{
    632     return NULL;
    633 }
     636    return nil;
     637}
     638
    634639
    635640- (void) tableView: (NSTableView *) t willDisplayCell: (id) cell
    636641    forTableColumn: (NSTableColumn *) tableColumn row: (int) rowIndex
    637642{
    638     BOOL w;
    639 
    640     w = [fWindow isKeyWindow] && rowIndex == [fTableView selectedRow];
    641     if( [[tableColumn identifier] isEqualToString: @"Name"] )
    642     {
    643         [(NameCell *) cell setStat: &fStat[rowIndex] whiteText: w];
    644     }
    645     else if( [[tableColumn identifier] isEqualToString: @"Progress"] )
    646     {
    647         [(ProgressCell *) cell setStat: &fStat[rowIndex] whiteText: w];
    648     }
     643    [cell setTorrent: [fTorrents objectAtIndex: rowIndex]];
     644    [cell setTextColor: ( [fWindow isKeyWindow] &&
     645        rowIndex == [fTableView selectedRow] ) ?
     646        [NSColor whiteColor] : [NSColor blackColor]];
    649647}
    650648
     
    671669        return NSDragOperationNone;
    672670
    673     [fTableView setDropRow: fCount dropOperation: NSTableViewDropAbove];
     671    [fTableView setDropRow: [fTorrents count]
     672        dropOperation: NSTableViewDropAbove];
    674673    return NSDragOperationGeneric;
    675674}
     
    695694    }
    696695
     696#if 0
    697697    /* Update info window */
    698698    [fInfoTitle setStringValue: [NSString stringWithUTF8String:
     
    704704    [fInfoSize setStringValue:
    705705        [NSString stringForFileSize: fStat[row].info.totalSize]];
    706     [fInfoPieces setStringValue: [NSString stringWithFormat: @"%d",
     706    [fInfoPieces setStringValue: [NSString stringWithInt:
    707707        fStat[row].info.pieceCount]];
    708708    [fInfoPieceSize setStringValue:
     
    710710    [fInfoFolder setStringValue: [[NSString stringWithUTF8String:
    711711        tr_torrentGetFolder( fHandle, row )] lastPathComponent]];
    712        
     712
    713713    if ( fStat[row].seeders == -1 ) {
    714714        [fInfoSeeders setStringValue: [NSString stringWithUTF8String: "?"]];
    715715    } else {
    716         [fInfoSeeders setStringValue: [NSString stringWithFormat: @"%d",
     716        [fInfoSeeders setStringValue: [NSString stringWithInt:
    717717            fStat[row].seeders]];
    718718    }
     
    720720        [fInfoLeechers setStringValue: [NSString stringWithUTF8String: "?"]];
    721721    } else {
    722         [fInfoLeechers setStringValue: [NSString stringWithFormat: @"%d",
     722        [fInfoLeechers setStringValue: [NSString stringWithInt:
    723723            fStat[row].leechers]];
    724724    }
     725#endif
    725726}
    726727
     
    825826    if (action == @selector(removeTorrent:))
    826827        return [fTableView selectedRow] >= 0;
    827        
     828
     829    Torrent * torrent;
     830    NSEnumerator * enumerator;
    828831
    829832    //enable resume all item
    830833    if (action == @selector(resumeAllTorrents:))
    831         return fCount > fDownloading + fSeeding;
     834    {
     835        enumerator = [fTorrents objectEnumerator];
     836        while( ( torrent = [enumerator nextObject] ) )
     837            if( [torrent isPaused] )
     838                return YES;
     839        return NO;
     840    }
    832841
    833842    //enable pause all item
    834843    if (action == @selector(stopAllTorrents:))
    835         return fDownloading > 0 || fSeeding > 0;                               
    836    
     844    {
     845        enumerator = [fTorrents objectEnumerator];
     846        while( ( torrent = [enumerator nextObject] ) )
     847            if( [torrent isActive] )
     848                return YES;
     849        return NO;
     850    }
     851
    837852    return YES;
    838853}
     
    845860    if ([fToolbar customizationPaletteIsRunning])
    846861        return NO;
    847        
     862
    848863    //enable customize toolbar item
    849864    if (action == @selector(showHideToolbar:))
     
    852867        return YES;
    853868    }
    854        
     869
    855870    //enable show info
    856871    if (action == @selector(showInfo:))
     
    860875    }
    861876
     877    Torrent * torrent;
     878    NSEnumerator * enumerator;
     879
    862880    //enable resume all item
    863881    if (action == @selector(resumeAllTorrents:))
    864         return fCount > fDownloading + fSeeding;
     882    {
     883        enumerator = [fTorrents objectEnumerator];
     884        while( ( torrent = [enumerator nextObject] ) )
     885            if( [torrent isPaused] )
     886                return YES;
     887        return NO;
     888    }
    865889
    866890    //enable pause all item
    867891    if (action == @selector(stopAllTorrents:))
    868         return fDownloading > 0 || fSeeding > 0;                               
    869    
     892    {
     893        enumerator = [fTorrents objectEnumerator];
     894        while( ( torrent = [enumerator nextObject] ) )
     895            if( [torrent isActive] )
     896                return YES;
     897        return NO;
     898    }
     899
    870900    int row = [fTableView selectedRow];
    871        
     901    torrent = ( row < 0 ) ? nil : [fTorrents objectAtIndex: row];
     902
     903    if (action == @selector(revealFromMenu:))
     904    {
     905        return ( torrent != nil );
     906    }
     907
    872908    //enable remove items
    873909    if (action == @selector(removeTorrent:)
     
    877913    {
    878914        //append or remove ellipsis when needed
    879         if (row >= 0 && fStat[row].status & ( TR_STATUS_CHECK | TR_STATUS_DOWNLOAD)
    880                     && [[fDefaults stringForKey: @"CheckRemove"] isEqualToString:@"YES"])
     915        if( torrent && [torrent isActive] &&
     916            [fDefaults boolForKey: @"CheckRemove"] )
    881917        {
    882918            if (![[menuItem title] hasSuffix:NS_ELLIPSIS])
     
    888924                [menuItem setTitle:[[menuItem title] substringToIndex:[[menuItem title] length]-[NS_ELLIPSIS length]]];
    889925        }
    890         return row >= 0;
    891     }
    892    
    893     //enable reveal in finder item
    894     if (action == @selector(revealFromMenu:))
    895         return row >= 0;
    896        
     926        return ( torrent != nil );
     927    }
     928
    897929    //enable and change pause / remove item
    898     if (action == @selector(resumeTorrent:) || action == @selector(stopTorrent:))
    899     {
    900         if (row >= 0 && fStat[row].status & TR_STATUS_PAUSE)
     930    if( action == @selector(resumeTorrent:) ||
     931        action == @selector(stopTorrent:) )
     932    {
     933        if( !torrent || [torrent isActive] )
     934        {
     935            [menuItem setTitle: @"Pause"];
     936            [menuItem setAction: @selector( stopTorrent: )];
     937        }
     938        else
    901939        {
    902940            [menuItem setTitle: @"Resume"];
    903941            [menuItem setAction: @selector( resumeTorrent: )];
    904942        }
    905         else
    906         {
    907             [menuItem setTitle: @"Pause"];
    908             [menuItem setAction: @selector( stopTorrent: )];
    909         }
    910         return row >= 0;
    911     }
    912    
     943        return ( torrent != nil );
     944    }
     945
    913946    return YES;
    914947}
     
    917950                          (void *) messageArgument
    918951{
    919     int i;
     952    NSEnumerator * enumerator;;
     953    Torrent * torrent;
    920954
    921955    switch( messageType )
     
    924958            /* Close all connections before going to sleep and remember
    925959               we should resume when we wake up */
    926             for( i = 0; i < fCount; i++ )
     960            enumerator = [fTorrents objectEnumerator];
     961            while( ( torrent = [enumerator nextObject] ) )
    927962            {
    928                 if( fStat[i].status & ( TR_STATUS_CHECK |
    929                         TR_STATUS_DOWNLOAD | TR_STATUS_SEED ) )
     963                [torrent sleep];
     964            }
     965
     966            /* Wait for torrents to stop (5 seconds timeout) */
     967            NSDate * start = [NSDate date];
     968            enumerator = [fTorrents objectEnumerator];
     969            while( ( torrent = [enumerator nextObject] ) )
     970            {
     971                while( [[NSDate date] timeIntervalSinceDate: start] < 5 &&
     972                        ![torrent isPaused] )
    930973                {
    931                     tr_torrentStop( fHandle, i );
    932                     fResumeOnWake[i] = 1;
    933                 }
    934                 else
    935                 {
    936                     fResumeOnWake[i] = 0;
     974                    usleep( 100000 );
     975                    [torrent update];
    937976                }
    938977            }
    939978
    940             /* TODO: wait a few seconds to let the torrents
    941                stop properly */
    942            
    943979            IOAllowPowerChange( fRootPort, (long) messageArgument );
    944980            break;
     
    954990        case kIOMessageSystemHasPoweredOn:
    955991            /* Resume download after we wake up */
    956             for( i = 0; i < fCount; i++ )
     992            enumerator = [fTorrents objectEnumerator];
     993            while( ( torrent = [enumerator nextObject] ) )
    957994            {
    958                 if( fResumeOnWake[i] )
    959                 {
    960                     tr_torrentStart( fHandle, i );
    961                 }
     995                [torrent wakeUp];
    962996            }
    963997            break;
     
    9731007    rectWin  = [fWindow frame];
    9741008    rectView = [fScrollView frame];
    975     foo      = 25.0 + MAX( 1, fCount ) * ( [fTableView rowHeight] +
    976                  [fTableView intercellSpacing].height ) -
    977                  rectView.size.height;
     1009    foo      = 25.0 - rectView.size.height + MAX( 1U, [fTorrents count] ) *
     1010        ( [fTableView rowHeight] + [fTableView intercellSpacing].height );
    9781011
    9791012    rectWin.size.height += foo;
     
    10081041    if( !fHasGrowl )
    10091042        return;
    1010    
     1043
    10111044    growlScript = [NSString stringWithFormat:
    10121045        @"tell application \"System Events\"\n"
     
    10291062
    10301063- (void) growlRegister: (id) sender
    1031 {   
     1064{
    10321065    NSString * growlScript;
    10331066    NSAppleScript * appleScript;
     
    10361069    if( !fHasGrowl )
    10371070        return;
    1038    
     1071
    10391072    growlScript = [NSString stringWithFormat:
    10401073        @"tell application \"System Events\"\n"
     
    10481081         "  end if\n"
    10491082         "end tell"];
    1050          
     1083
    10511084    appleScript = [[NSAppleScript alloc] initWithSource: growlScript];
    10521085    if( ![appleScript executeAndReturnError: &error] )
     
    10591092- (void) revealFromMenu: (id) sender
    10601093{
    1061     int row = [fTableView selectedRow];
    1062     if (row >= 0)
    1063     {
    1064         [self finderReveal: [NSString stringWithFormat: @"%@/%@",
    1065             [NSString stringWithUTF8String: fStat[row].folder],
    1066             [NSString stringWithUTF8String: fStat[row].info.name]]];
    1067     }
    1068 }
    1069 
    1070 - (void) finderReveal: (NSString *) path
    1071 {
    1072     NSString * string;
    1073     NSAppleScript * appleScript;
    1074     NSDictionary * error;
    1075    
    1076     string = [NSString stringWithFormat:
    1077         @"tell application \"Finder\"\n"
    1078          "  activate\n"
    1079          "  reveal (POSIX file \"%@\")\n"
    1080          "end tell", path];
    1081 
    1082     appleScript = [[NSAppleScript alloc] initWithSource: string];
    1083     if( ![appleScript executeAndReturnError: &error] )
    1084     {
    1085         printf( "finderReveal failed\n" );
    1086     }
    1087     [appleScript release];
    1088 }
    1089 
    1090 - (void) finderTrash: (NSString *) path
    1091 {
    1092     NSString * string;
    1093     NSAppleScript * appleScript;
    1094     NSDictionary * error;
    1095 
    1096     string = [NSString stringWithFormat:
    1097         @"tell application \"Finder\"\n"
    1098          "  move (POSIX file \"%@\") to trash\n"
    1099          "end tell", path];
    1100 
    1101     appleScript = [[NSAppleScript alloc] initWithSource: string];
    1102     if( ![appleScript executeAndReturnError: &error] )
    1103     {
    1104         printf( "finderTrash failed\n" );
    1105     }
    1106     [appleScript release];
     1094    Torrent * torrent;
     1095    torrent = [fTorrents objectAtIndex: [fTableView selectedRow]];
     1096    [torrent reveal];
    11071097}
    11081098
     
    11421132    [fDefaults setObject: [NSDate date] forKey: @"VersionCheckLast"];
    11431133}
    1144    
     1134
    11451135- (void) checkForUpdateAuto: (BOOL) automatic
    11461136{
     
    11511141
    11521142- (void) URLResourceDidFinishLoading: (NSURL *) sender
    1153 {   
     1143{
    11541144    NSDictionary * dict = [NSPropertyListSerialization
    11551145                            propertyListFromData: [sender resourceDataUsingCache: NO]
  • trunk/macosx/English.lproj/MainMenu.nib/info.nib

    r106 r162  
    1616                <string>54 521 112 118 0 0 1152 842 </string>
    1717                <key>783</key>
    18                 <string>483 459 420 250 0 0 1440 878 </string>
     18                <string>411 429 420 250 0 0 1280 832 </string>
    1919                <key>796</key>
    2020                <string>484 520 420 129 0 0 1440 878 </string>
     
    2727        <integer>3</integer>
    2828        <key>IBSystem Version</key>
    29         <string>8F1111g</string>
     29        <string>8H14</string>
    3030</dict>
    3131</plist>
  • trunk/macosx/PrefsController.m

    r106 r162  
    11/******************************************************************************
    2  * Copyright (c) 2005 Eric Petit
     2 * Copyright (c) 2005-2006 Transmission authors and contributors
    33 *
    44 * Permission is hereby granted, free of charge, to any person obtaining a
     
    2222
    2323#import "PrefsController.h"
     24#import "StringAdditions.h"
    2425#import "Utils.h"
    2526
     
    203204    //if value entered is not an int or is not in range do not change
    204205    if (![[fPortField stringValue] isEqualToString:
    205             [NSString stringWithFormat: @"%d", bindPort]]
     206            [NSString stringWithInt: bindPort]]
    206207            || bindPort < MIN_PORT
    207208            || bindPort > MAX_PORT)
     
    234235    //if value entered is not an int or is less than 0 do not change
    235236    if (![[fUploadField stringValue] isEqualToString:
    236             [NSString stringWithFormat: @"%d", uploadLimit]]
    237             || uploadLimit < 0)
     237            [NSString stringWithInt: uploadLimit]] || uploadLimit < 0)
    238238    {
    239239        NSBeep();
  • trunk/macosx/StringAdditions.h

    r103 r162  
    1 //
    2 //  StringAdditions.h
    3 //  Transmission
    4 //
    5 //  Created by Mitchell Livingston on 1/16/06.
    6 //  Copyright 2006 __MyCompanyName__. All rights reserved.
    7 //
     1/******************************************************************************
     2 * Copyright (c) 2005-2006 Transmission authors and contributors
     3 *
     4 * Permission is hereby granted, free of charge, to any person obtaining a
     5 * copy of this software and associated documentation files (the "Software"),
     6 * to deal in the Software without restriction, including without limitation
     7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     8 * and/or sell copies of the Software, and to permit persons to whom the
     9 * Software is furnished to do so, subject to the following conditions:
     10 *
     11 * The above copyright notice and this permission notice shall be included in
     12 * all copies or substantial portions of the Software.
     13 *
     14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     20 * DEALINGS IN THE SOFTWARE.
     21 *****************************************************************************/
    822
    923#import <Cocoa/Cocoa.h>
     
    1125@interface NSString (StringAdditions)
    1226
     27+ (NSString *) stringWithInt: (int) value;
    1328+ (NSString *) stringForFileSize: (uint64_t) size;
    1429+ (NSString *) stringForSpeed: (float) speed;
  • trunk/macosx/StringAdditions.m

    r118 r162  
    1 //
    2 //  StringAdditions.m
    3 //  Transmission
    4 //
    5 //  Created by Mitchell Livingston on 1/16/06.
    6 //  Copyright 2006 __MyCompanyName__. All rights reserved.
    7 //
     1/******************************************************************************
     2 * Copyright (c) 2005-2006 Transmission authors and contributors
     3 *
     4 * Permission is hereby granted, free of charge, to any person obtaining a
     5 * copy of this software and associated documentation files (the "Software"),
     6 * to deal in the Software without restriction, including without limitation
     7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     8 * and/or sell copies of the Software, and to permit persons to whom the
     9 * Software is furnished to do so, subject to the following conditions:
     10 *
     11 * The above copyright notice and this permission notice shall be included in
     12 * all copies or substantial portions of the Software.
     13 *
     14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     20 * DEALINGS IN THE SOFTWARE.
     21 *****************************************************************************/
    822
    923#import "StringAdditions.h"
     
    1125
    1226@implementation NSString (StringAdditions)
     27
     28+ (NSString *) stringWithInt: (int) value
     29{
     30    return [NSString stringWithFormat: @"%d", value];
     31}
    1332
    1433+ (NSString *) stringForFileSize: (uint64_t) size
  • trunk/macosx/TorrentTableView.h

    r34 r162  
    77
    88{
    9     IBOutlet Controller  * fController;
     9    IBOutlet Controller * fController;
     10    NSArray             * fTorrents;
    1011
    11     tr_stat_t            * fStat;
    12     NSPoint                fClickPoint;
     12    NSPoint               fClickPoint;
    1313   
    1414    IBOutlet NSMenu     * fContextRow;
    1515    IBOutlet NSMenu     * fContextNoRow;
    1616}
    17 
    18 - (void) updateUI: (tr_stat_t *) stat;
     17- (void) setTorrents: (NSArray *) torrents;
    1918
    2019@end
  • trunk/macosx/TorrentTableView.m

    r34 r162  
     1/******************************************************************************
     2 * Copyright (c) 2005-2006 Transmission authors and contributors
     3 *
     4 * Permission is hereby granted, free of charge, to any person obtaining a
     5 * copy of this software and associated documentation files (the "Software"),
     6 * to deal in the Software without restriction, including without limitation
     7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     8 * and/or sell copies of the Software, and to permit persons to whom the
     9 * Software is furnished to do so, subject to the following conditions:
     10 *
     11 * The above copyright notice and this permission notice shall be included in
     12 * all copies or substantial portions of the Software.
     13 *
     14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     20 * DEALINGS IN THE SOFTWARE.
     21 *****************************************************************************/
     22
    123#import "TorrentTableView.h"
    224#import "Controller.h"
     25#import "Torrent.h"
    326
    427@implementation TorrentTableView
    528
    6 - (void) updateUI: (tr_stat_t *) stat
    7 {
    8     fStat = stat;
    9     [self reloadData];
     29- (void) setTorrents: (NSArray *) torrents
     30{
     31    fTorrents = torrents;
    1032}
    1133
    1234- (void) pauseOrResume: (int) row
    1335{
    14     if( fStat[row].status & TR_STATUS_PAUSE )
     36    Torrent * torrent = [fTorrents objectAtIndex: row];
     37
     38    if( [torrent isPaused] )
    1539    {
    1640        [fController resumeTorrentWithIndex: row];
    1741    }
    18     else if( fStat[row].status & ( TR_STATUS_CHECK |
    19               TR_STATUS_DOWNLOAD | TR_STATUS_SEED ) )
     42    else if( [torrent isActive] )
    2043    {
    2144        [fController stopTorrentWithIndex: row];
    22     }                                                                   
    23 }
    24 
    25 - (void) mouseDown: (NSEvent *) e
    26 {
    27     fClickPoint = [self convertPoint: [e locationInWindow] fromView: NULL];
    28     [self display];
     45    }
     46    else;
    2947}
    3048
     
    3452    NSRect cellRect, rect;
    3553
    36     col      = [self columnWithIdentifier: @"Name"];
     54    col      = [self columnWithIdentifier: @"Torrent"];
    3755    cellRect = [self frameOfCellAtColumn: col row: row];
    38     rect     = NSMakeRect( cellRect.origin.x + cellRect.size.width - 19,
    39                            cellRect.origin.y + cellRect.size.height - 38,
    40                            14, 14 );
     56    rect     = NSMakeRect( cellRect.origin.x + cellRect.size.width - 39,
     57                           cellRect.origin.y + 3, 14, 14 );
    4158
    4259    return rect;
     
    4865    NSRect cellRect, rect;
    4966
    50     col      = [self columnWithIdentifier: @"Name"];
     67    col      = [self columnWithIdentifier: @"Torrent"];
    5168    cellRect = [self frameOfCellAtColumn: col row: row];
    52     rect     = NSMakeRect( cellRect.origin.x + cellRect.size.width - 19,
    53                            cellRect.origin.y + cellRect.size.height - 19,
    54                            14, 14 );
     69    rect     = NSMakeRect( cellRect.origin.x + cellRect.size.width - 20,
     70                           cellRect.origin.y + 3, 14, 14 );
    5571
    5672    return rect;
     
    6985}
    7086
     87
     88- (void) mouseDown: (NSEvent *) e
     89{
     90    fClickPoint = [self convertPoint: [e locationInWindow] fromView: nil];
     91    int row = [self rowAtPoint: fClickPoint];
     92
     93    if( [e modifierFlags] & NSAlternateKeyMask )
     94    {
     95        [fController advancedChanged: self];
     96        fClickPoint = NSMakePoint( 0, 0 );
     97    }
     98    else if( ![self pointInPauseRect: fClickPoint] &&
     99             ![self pointInRevealRect: fClickPoint] )
     100    {
     101        if( row >= 0 )
     102        {
     103            [self selectRowIndexes: [NSIndexSet indexSetWithIndex: row]
     104                byExtendingSelection: NO];
     105        }
     106        else
     107        {
     108            [self deselectAll: self];
     109        }
     110    }
     111    else;
     112
     113    [self display];
     114}
     115
    71116- (void) mouseUp: (NSEvent *) e
    72117{
    73118    NSPoint point;
    74     int row, col;
    75 
    76     point = [self convertPoint: [e locationInWindow] fromView: NULL];
     119    int row;
     120    bool sameRow;
     121    Torrent * torrent;
     122
     123    point = [self convertPoint: [e locationInWindow] fromView: nil];
    77124    row   = [self rowAtPoint: point];
    78     col   = [self columnAtPoint: point];
    79 
    80     if( row < 0 )
    81     {
    82         [self deselectAll: NULL];
    83     }
    84     else if( [self pointInPauseRect: point] )
     125    sameRow = row == [self rowAtPoint: fClickPoint];
     126   
     127    if( sameRow && [self pointInPauseRect: point]
     128            && [self pointInPauseRect: fClickPoint] )
    85129    {
    86130        [self pauseOrResume: row];
    87131    }
    88     else if( [self pointInRevealRect: point] )
    89     {
    90         [fController finderReveal: [NSString stringWithFormat:
    91             @"%@/%@", [NSString stringWithUTF8String: fStat[row].folder],
    92             [NSString stringWithUTF8String: fStat[row].info.name]]];
    93         [self display];
    94     }
    95     else if( row >= 0 && col == [self columnWithIdentifier: @"Progress"]
    96              && ( [e modifierFlags] & NSAlternateKeyMask ) )
    97     {
    98         [fController advancedChanged: NULL];
    99     }
    100     else
     132    else if( sameRow && [self pointInRevealRect: point]
     133                && [self pointInRevealRect: fClickPoint] )
     134    {
     135        torrent = [fTorrents objectAtIndex: row];
     136        [torrent reveal];
     137    }
     138    else;
     139
     140    [self display];
     141    fClickPoint = NSMakePoint( 0, 0 );
     142}
     143
     144- (NSMenu *) menuForEvent: (NSEvent *) e
     145{
     146    NSPoint point;
     147    int row;
     148
     149    point = [self convertPoint: [e locationInWindow] fromView: nil];
     150    row = [self rowAtPoint: point];
     151   
     152    if( row >= 0 )
    101153    {
    102154        [self selectRowIndexes: [NSIndexSet indexSetWithIndex: row]
    103155            byExtendingSelection: NO];
    104     }
    105 
    106     fClickPoint = NSMakePoint( 0, 0 );
    107 }
    108 
    109 - (NSMenu *) menuForEvent: (NSEvent *) e
    110 {
    111     NSPoint point;
    112     int row;
    113 
    114     point = [self convertPoint: [e locationInWindow] fromView: NULL];
    115     row = [self rowAtPoint: point];
    116    
    117     [self selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
    118 
    119     return row >= 0 ? fContextRow : fContextNoRow;
     156        return fContextRow;
     157    }
     158    else
     159    {
     160        [self deselectAll: self];
     161        return fContextNoRow;
     162    }
    120163}
    121164
    122165- (void) drawRect: (NSRect) r
    123166{
    124     int i;
     167    unsigned i;
    125168    NSRect rect;
    126     NSPoint point;
    127169    NSImage * image;
     170    Torrent * torrent;
    128171
    129172    [super drawRect: r];
    130173
    131     for( i = 0; i < [self numberOfRows]; i++ )
    132     {
     174    for( i = 0; i < [fTorrents count]; i++ )
     175    {
     176        torrent = [fTorrents objectAtIndex: i];
    133177        rect  = [self pauseRectForRow: i];
    134         image = NULL;
    135         if( fStat[i].status & TR_STATUS_PAUSE )
     178        image = nil;
     179
     180        if( [torrent isPaused] )
    136181        {
    137182            image = NSPointInRect( fClickPoint, rect ) ?
     
    139184                [NSImage imageNamed: @"ResumeOff.png"];
    140185        }
    141         else if( fStat[i].status &
    142                  ( TR_STATUS_CHECK | TR_STATUS_DOWNLOAD | TR_STATUS_SEED ) )
     186        else if( [torrent isActive] )
    143187        {
    144188            image = NSPointInRect( fClickPoint, rect ) ?
     
    146190                [NSImage imageNamed: @"PauseOff.png"];
    147191        }
     192        else;
     193
    148194        if( image )
    149195        {
    150             point = NSMakePoint( rect.origin.x, rect.origin.y + 14 );
    151             [image compositeToPoint: point operation: NSCompositeSourceOver];
     196            [image setFlipped: YES];
     197            [image drawAtPoint: rect.origin fromRect:
     198                NSMakeRect( 0, 0, 14, 14 ) operation:
     199                NSCompositeSourceOver fraction: 1.0];
    152200        }
    153201
     
    156204            [NSImage imageNamed: @"RevealOn.png"] :
    157205            [NSImage imageNamed: @"RevealOff.png"];
    158         point = NSMakePoint( rect.origin.x, rect.origin.y + 14 );
    159         [image compositeToPoint: point operation: NSCompositeSourceOver];
     206        [image setFlipped: YES];
     207        [image drawAtPoint: rect.origin fromRect:
     208            NSMakeRect( 0, 0, 14, 14 ) operation:
     209            NSCompositeSourceOver fraction: 1.0];
    160210    }
    161211}
  • trunk/macosx/Transmission.xcodeproj/project.pbxproj

    r103 r162  
    99/* Begin PBXBuildFile section */
    1010                4D043A7F090AE979009FEDA8 /* TransmissionDocument.icns in Resources */ = {isa = PBXBuildFile; fileRef = 4D043A7E090AE979009FEDA8 /* TransmissionDocument.icns */; };
    11                 4D096C12089FB4E20091B166 /* NameCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D096C0F089FB4E20091B166 /* NameCell.m */; };
    12                 4D096C13089FB4E20091B166 /* ProgressCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D096C11089FB4E20091B166 /* ProgressCell.m */; };
    1311                4D118E1A08CB46B20033958F /* PrefsController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D118E1908CB46B20033958F /* PrefsController.m */; };
    1412                4D2784370905709500687951 /* Transmission.icns in Resources */ = {isa = PBXBuildFile; fileRef = 4D2784360905709500687951 /* Transmission.icns */; };
     
    1816                4D6DAAC7090CE00500F43C22 /* RevealOn.png in Resources */ = {isa = PBXBuildFile; fileRef = 4D6DAAC5090CE00500F43C22 /* RevealOn.png */; };
    1917                4D752E930913C949008EAAD4 /* Preferences.png in Resources */ = {isa = PBXBuildFile; fileRef = 4D752E920913C949008EAAD4 /* Preferences.png */; };
    20                 4D813EB508AA43AC00191DB4 /* Progress.png in Resources */ = {isa = PBXBuildFile; fileRef = 4D813EB408AA43AC00191DB4 /* Progress.png */; };
    2118                4D8CEF91095870E00063BAEA /* Network.png in Resources */ = {isa = PBXBuildFile; fileRef = 4D8CEF90095870E00063BAEA /* Network.png */; };
    2219                4DA6FDBA0911233800450CB1 /* PauseOn.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DA6FDB80911233800450CB1 /* PauseOn.png */; };
     
    2421                4DA6FDC5091141AD00450CB1 /* ResumeOff.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DA6FDC3091141AD00450CB1 /* ResumeOff.png */; };
    2522                4DA6FDC6091141AD00450CB1 /* ResumeOn.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DA6FDC4091141AD00450CB1 /* ResumeOn.png */; };
     23                4DCCBB3E09C3D71100D3CABF /* TorrentCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DCCBB3C09C3D71100D3CABF /* TorrentCell.m */; };
    2624                4DDFDD22099A5D8E00189D81 /* DownloadBadge.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DDFDD20099A5D8E00189D81 /* DownloadBadge.png */; };
    2725                4DDFDD23099A5D8E00189D81 /* UploadBadge.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DDFDD21099A5D8E00189D81 /* UploadBadge.png */; };
     
    3735                4DF7500D08A103AD007B0D70 /* Info.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500808A103AD007B0D70 /* Info.png */; };
    3836                4DF7500E08A103AD007B0D70 /* Remove.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500908A103AD007B0D70 /* Remove.png */; };
     37                4DFBC2DF09C0970D00D5C571 /* Torrent.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DFBC2DE09C0970D00D5C571 /* Torrent.m */; };
    3938                8D11072A0486CEB800E47090 /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = 29B97318FDCFA39411CA2CEA /* MainMenu.nib */; };
    4039                8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
     
    8079                32CA4F630368D1EE00C91783 /* Transmission_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Transmission_Prefix.pch; sourceTree = "<group>"; };
    8180                4D043A7E090AE979009FEDA8 /* TransmissionDocument.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = TransmissionDocument.icns; path = Images/TransmissionDocument.icns; sourceTree = "<group>"; };
    82                 4D096C0E089FB4E20091B166 /* NameCell.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = NameCell.h; sourceTree = "<group>"; };
    83                 4D096C0F089FB4E20091B166 /* NameCell.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = NameCell.m; sourceTree = "<group>"; };
    84                 4D096C10089FB4E20091B166 /* ProgressCell.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = ProgressCell.h; sourceTree = "<group>"; };
    85                 4D096C11089FB4E20091B166 /* ProgressCell.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = ProgressCell.m; sourceTree = "<group>"; };
    8681                4D118E1808CB46B20033958F /* PrefsController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = PrefsController.h; sourceTree = "<group>"; };
    8782                4D118E1908CB46B20033958F /* PrefsController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = PrefsController.m; sourceTree = "<group>"; };
     
    9388                4D6DAAC5090CE00500F43C22 /* RevealOn.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = RevealOn.png; path = Images/RevealOn.png; sourceTree = "<group>"; };
    9489                4D752E920913C949008EAAD4 /* Preferences.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Preferences.png; path = Images/Preferences.png; sourceTree = "<group>"; };
    95                 4D813EB408AA43AC00191DB4 /* Progress.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Progress.png; path = Images/Progress.png; sourceTree = "<group>"; };
    9690                4D8CEF90095870E00063BAEA /* Network.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Network.png; path = Images/Network.png; sourceTree = "<group>"; };
    9791                4DA6FDB80911233800450CB1 /* PauseOn.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = PauseOn.png; path = Images/PauseOn.png; sourceTree = "<group>"; };
     
    9993                4DA6FDC3091141AD00450CB1 /* ResumeOff.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = ResumeOff.png; path = Images/ResumeOff.png; sourceTree = "<group>"; };
    10094                4DA6FDC4091141AD00450CB1 /* ResumeOn.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = ResumeOn.png; path = Images/ResumeOn.png; sourceTree = "<group>"; };
     95                4DCCBB3C09C3D71100D3CABF /* TorrentCell.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = TorrentCell.m; sourceTree = "<group>"; };
     96                4DCCBB3D09C3D71100D3CABF /* TorrentCell.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = TorrentCell.h; sourceTree = "<group>"; };
    10197                4DDFDD20099A5D8E00189D81 /* DownloadBadge.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = DownloadBadge.png; path = Images/DownloadBadge.png; sourceTree = "<group>"; };
    10298                4DDFDD21099A5D8E00189D81 /* UploadBadge.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = UploadBadge.png; path = Images/UploadBadge.png; sourceTree = "<group>"; };
     
    115111                4DF7500808A103AD007B0D70 /* Info.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Info.png; path = Images/Info.png; sourceTree = "<group>"; };
    116112                4DF7500908A103AD007B0D70 /* Remove.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Remove.png; path = Images/Remove.png; sourceTree = "<group>"; };
     113                4DFBC2DD09C0970D00D5C571 /* Torrent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Torrent.h; sourceTree = "<group>"; };
     114                4DFBC2DE09C0970D00D5C571 /* Torrent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Torrent.m; sourceTree = "<group>"; };
    117115                8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
    118116                8D1107320486CEB800E47090 /* Transmission.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Transmission.app; sourceTree = BUILT_PRODUCTS_DIR; };
     
    136134                        isa = PBXGroup;
    137135                        children = (
    138                                 4D096C0E089FB4E20091B166 /* NameCell.h */,
    139                                 4D096C0F089FB4E20091B166 /* NameCell.m */,
    140                                 4D096C10089FB4E20091B166 /* ProgressCell.h */,
    141                                 4D096C11089FB4E20091B166 /* ProgressCell.m */,
    142136                                4DF0C5A90899190500DD8943 /* Controller.m */,
    143137                                4DF0C5AA0899190500DD8943 /* Controller.h */,
     
    150144                                4DE5CCA50980735700BE280E /* Badger.h */,
    151145                                4DE5CCA60980735700BE280E /* Badger.m */,
     146                                4DFBC2DD09C0970D00D5C571 /* Torrent.h */,
     147                                4DFBC2DE09C0970D00D5C571 /* Torrent.m */,
     148                                4DCCBB3D09C3D71100D3CABF /* TorrentCell.h */,
     149                                4DCCBB3C09C3D71100D3CABF /* TorrentCell.m */,
    152150                        );
    153151                        name = Classes;
     
    209207                                4D043A7E090AE979009FEDA8 /* TransmissionDocument.icns */,
    210208                                4DF7500808A103AD007B0D70 /* Info.png */,
    211                                 4D813EB408AA43AC00191DB4 /* Progress.png */,
    212209                                4DF7500708A103AD007B0D70 /* Open.png */,
    213210                                4DF7500908A103AD007B0D70 /* Remove.png */,
     
    305302                                4DF7500D08A103AD007B0D70 /* Info.png in Resources */,
    306303                                4DF7500E08A103AD007B0D70 /* Remove.png in Resources */,
    307                                 4D813EB508AA43AC00191DB4 /* Progress.png in Resources */,
    308304                                4D2784370905709500687951 /* Transmission.icns in Resources */,
    309305                                4D043A7F090AE979009FEDA8 /* TransmissionDocument.icns in Resources */,
     
    334330                                8D11072D0486CEB800E47090 /* main.m in Sources */,
    335331                                4DF0C5AB0899190500DD8943 /* Controller.m in Sources */,
    336                                 4D096C12089FB4E20091B166 /* NameCell.m in Sources */,
    337                                 4D096C13089FB4E20091B166 /* ProgressCell.m in Sources */,
    338332                                4D118E1A08CB46B20033958F /* PrefsController.m in Sources */,
    339333                                4D364DA0091FBB2C00377D12 /* TorrentTableView.m in Sources */,
    340334                                4DE5CC9D0980656F00BE280E /* StringAdditions.m in Sources */,
    341335                                4DE5CCA70980735700BE280E /* Badger.m in Sources */,
     336                                4DFBC2DF09C0970D00D5C571 /* Torrent.m in Sources */,
     337                                4DCCBB3E09C3D71100D3CABF /* TorrentCell.m in Sources */,
    342338                        );
    343339                        runOnlyForDeploymentPostprocessing = 0;
  • trunk/transmissioncli.c

    r13 r162  
    6060int main( int argc, char ** argv )
    6161{
    62     int           i, count;
    63     tr_handle_t * h;
    64     tr_stat_t   * s;
     62    int i, error;
     63    tr_handle_t  * h;
     64    tr_torrent_t * tor;
     65    tr_stat_t    * s;
    6566
    6667    printf( "Transmission %s - http://transmission.m0k.org/\n\n",
     
    105106
    106107    /* Open and parse torrent file */
    107     if( tr_torrentInit( h, torrentPath ) )
     108    if( !( tor = tr_torrentInit( h, torrentPath, &error ) ) )
    108109    {
    109110        printf( "Failed opening torrent file `%s'\n", torrentPath );
     
    113114    if( showInfo )
    114115    {
    115         tr_info_t * info;
    116 
    117         count = tr_torrentStat( h, &s );
    118         info  = &s[0].info;
     116        tr_info_t * info = tr_torrentInfo( tor );
    119117
    120118        /* Print torrent info (quite à la btshowmetainfo) */
     
    138136        }
    139137
    140         free( s );
    141138        goto cleanup;
    142139    }
     
    146143        int seeders, leechers;
    147144
    148         if( tr_torrentScrape( h, 0, &seeders, &leechers ) )
     145        if( tr_torrentScrape( tor, &seeders, &leechers ) )
    149146        {
    150147            printf( "Scrape failed.\n" );
     
    163160    tr_setUploadLimit( h, uploadLimit );
    164161   
    165     tr_torrentSetFolder( h, 0, "." );
    166     tr_torrentStart( h, 0 );
     162    tr_torrentSetFolder( tor, "." );
     163    tr_torrentStart( tor );
    167164
    168165    while( !mustDie )
     
    174171        sleep( 1 );
    175172
    176         count = tr_torrentStat( h, &s );
    177 
    178         if( s[0].status & TR_STATUS_CHECK )
     173        s = tr_torrentStat( tor );
     174
     175        if( s->status & TR_STATUS_CHECK )
    179176        {
    180177            chars = snprintf( string, 80,
    181                 "Checking files... %.2f %%", 100.0 * s[0].progress );
    182         }
    183         else if( s[0].status & TR_STATUS_DOWNLOAD )
     178                "Checking files... %.2f %%", 100.0 * s->progress );
     179        }
     180        else if( s->status & TR_STATUS_DOWNLOAD )
    184181        {
    185182            chars = snprintf( string, 80,
    186183                "Progress: %.2f %%, %d peer%s, dl from %d (%.2f KB/s), "
    187                 "ul to %d (%.2f KB/s)", 100.0 * s[0].progress,
    188                 s[0].peersTotal, ( s[0].peersTotal == 1 ) ? "" : "s",
    189                 s[0].peersUploading, s[0].rateDownload,
    190                 s[0].peersDownloading, s[0].rateUpload );
    191         }
    192         else if( s[0].status & TR_STATUS_SEED )
     184                "ul to %d (%.2f KB/s)", 100.0 * s->progress,
     185                s->peersTotal, ( s->peersTotal == 1 ) ? "" : "s",
     186                s->peersUploading, s->rateDownload,
     187                s->peersDownloading, s->rateUpload );
     188        }
     189        else if( s->status & TR_STATUS_SEED )
    193190        {
    194191            chars = snprintf( string, 80,
    195192                "Seeding, uploading to %d of %d peer(s), %.2f KB/s",
    196                 s[0].peersDownloading, s[0].peersTotal,
    197                 s[0].rateUpload );
     193                s->peersDownloading, s->peersTotal,
     194                s->rateUpload );
    198195        }
    199196        memset( &string[chars], ' ', 79 - chars );
     
    201198        fprintf( stderr, "\r%s", string );
    202199
    203         if( s[0].status & TR_TRACKER_ERROR )
    204         {
    205             fprintf( stderr, "\n%s\n", s[0].error );
     200        if( s->error & TR_ETRACKER )
     201        {
     202            fprintf( stderr, "\n%s\n", s->trackerError );
    206203        }
    207204        else if( verboseLevel > 0 )
     
    210207        }
    211208       
    212         if( tr_getFinished( h, 0 ) )
    213         {
    214             tr_setFinished( h, 0, 0 );
     209        if( tr_getFinished( tor ) )
     210        {
    215211            result = system(finishCall);
    216212        }
    217 
    218         free( s );
    219213    }
    220214    fprintf( stderr, "\n" );
    221215
    222216    /* Try for 5 seconds to notice the tracker that we are leaving */
    223     tr_torrentStop( h, 0 );
     217    tr_torrentStop( tor );
    224218    for( i = 0; i < 10; i++ )
    225219    {
    226         count = tr_torrentStat( h, &s );
    227         if( s[0].status & TR_STATUS_PAUSE )
     220        s = tr_torrentStat( tor );
     221        if( s->status & TR_STATUS_PAUSE )
    228222        {
    229223            /* The 'stopped' message was sent */
    230             free( s );
    231224            break;
    232225        }
    233         free( s );
    234226        usleep( 500000 );
    235227    }
    236228   
    237229cleanup:
    238     tr_torrentClose( h, 0 );
     230    tr_torrentClose( h, tor );
    239231
    240232failed:
Note: See TracChangeset for help on using the changeset viewer.