Changeset 162 for trunk/gtk


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

Merge from branches/new_api:r161

Location:
trunk/gtk
Files:
8 edited

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
Note: See TracChangeset for help on using the changeset viewer.