mirror of
https://github.com/transmission/transmission.git
synced 2025-12-25 04:45:56 +00:00
Merge from branches/new_api:r161
This commit is contained in:
18
gtk/conf.c
18
gtk/conf.c
@@ -414,16 +414,18 @@ getstateval(struct cf_torrentstate *state, char *line) {
|
||||
}
|
||||
|
||||
gboolean
|
||||
cf_savestate(int count, tr_stat_t *torrents, char **errstr) {
|
||||
cf_savestate(GList *torrents, char **errstr) {
|
||||
char *file = g_build_filename(confdir, FILE_STATE, NULL);
|
||||
char *tmpfile = g_build_filename(confdir, FILE_STATE_TMP, NULL);
|
||||
GIOChannel *io = NULL;
|
||||
GError *err;
|
||||
int fd, ii;
|
||||
int fd;
|
||||
char *torrentfile, *torrentdir, *line;
|
||||
gsize written;
|
||||
gboolean paused;
|
||||
GIOStatus res;
|
||||
tr_stat_t *sb;
|
||||
tr_info_t *in;
|
||||
|
||||
*errstr = NULL;
|
||||
|
||||
@@ -444,11 +446,12 @@ cf_savestate(int count, tr_stat_t *torrents, char **errstr) {
|
||||
g_io_channel_set_close_on_unref(io, TRUE);
|
||||
|
||||
err = NULL;
|
||||
for(ii = 0; ii < count; ii++) {
|
||||
/* XXX need a better way to query running/stopped state */
|
||||
paused = ((TR_STATUS_STOPPING | TR_STATUS_PAUSE) & torrents[ii].status);
|
||||
torrentfile = g_strescape(torrents[ii].info.torrent, "");
|
||||
torrentdir = g_strescape(torrents[ii].folder, "");
|
||||
while(NULL != torrents) {
|
||||
sb = tr_torrentStat(torrents->data);
|
||||
in = tr_torrentInfo(torrents->data);
|
||||
paused = (TR_STATUS_INACTIVE & sb->status);
|
||||
torrentfile = g_strescape(in->torrent, "");
|
||||
torrentdir = g_strescape(tr_torrentGetFolder(torrents->data), "");
|
||||
/* g_strcompress */
|
||||
line = g_strdup_printf("torrent=\"%s\" dir=\"%s\" paused=\"%s\"%c",
|
||||
torrentfile, torrentdir, (paused ? "yes" : "no"),
|
||||
@@ -466,6 +469,7 @@ cf_savestate(int count, tr_stat_t *torrents, char **errstr) {
|
||||
assert(!"unknown return code");
|
||||
goto done;
|
||||
}
|
||||
torrents = torrents->next;
|
||||
}
|
||||
if(NULL != err ||
|
||||
G_IO_STATUS_ERROR == g_io_channel_shutdown(io, TRUE, &err)) {
|
||||
|
||||
@@ -50,7 +50,7 @@ cf_saveprefs(char **errstr);
|
||||
GList *
|
||||
cf_loadstate(char **errstr);
|
||||
gboolean
|
||||
cf_savestate(int count, tr_stat_t *torrents, char **errstr);
|
||||
cf_savestate(GList *torrents, char **errstr);
|
||||
void
|
||||
cf_freestate(struct cf_torrentstate *state);
|
||||
|
||||
|
||||
@@ -49,9 +49,8 @@ struct prefdata {
|
||||
struct addcb {
|
||||
add_torrent_func_t addfunc;
|
||||
GtkWindow *parent;
|
||||
tr_handle_t *tr;
|
||||
torrents_added_func_t donefunc;
|
||||
void *donedata;
|
||||
void *data;
|
||||
gboolean autostart;
|
||||
gboolean usingaltdir;
|
||||
GtkFileChooser *altdir;
|
||||
@@ -219,6 +218,7 @@ clickdialog(GtkWidget *widget, int resp, gpointer gdata) {
|
||||
g_free(errstr);
|
||||
}
|
||||
|
||||
/* XXX would be nice to have errno strings, are they printed to stdout? */
|
||||
tr_setBindPort(data->tr, gtk_spin_button_get_value_as_int(data->port));
|
||||
setlimit(data->tr);
|
||||
}
|
||||
@@ -240,8 +240,8 @@ setlimit(tr_handle_t *tr) {
|
||||
}
|
||||
|
||||
void
|
||||
makeaddwind(add_torrent_func_t addfunc, GtkWindow *parent, tr_handle_t *tr,
|
||||
torrents_added_func_t donefunc, void *donedata) {
|
||||
makeaddwind(GtkWindow *parent, add_torrent_func_t addfunc,
|
||||
torrents_added_func_t donefunc, void *cbdata) {
|
||||
GtkWidget *wind = gtk_file_chooser_dialog_new(_("Add a Torrent"), parent,
|
||||
GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
||||
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
|
||||
@@ -260,9 +260,8 @@ makeaddwind(add_torrent_func_t addfunc, GtkWindow *parent, tr_handle_t *tr,
|
||||
|
||||
data->addfunc = addfunc;
|
||||
data->parent = parent;
|
||||
data->tr = tr;
|
||||
data->donefunc = donefunc;
|
||||
data->donedata = donedata;
|
||||
data->data = cbdata;
|
||||
data->autostart = TRUE;
|
||||
data->usingaltdir = FALSE;
|
||||
data->altdir = GTK_FILE_CHOOSER(getdir);
|
||||
@@ -326,11 +325,12 @@ addresp(GtkWidget *widget, gint resp, gpointer gdata) {
|
||||
dir = gtk_file_chooser_get_filename(data->altdir);
|
||||
files = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(widget));
|
||||
for(ii = files; NULL != ii; ii = ii->next)
|
||||
if(data->addfunc(data->tr, data->parent, ii->data, dir,
|
||||
if(data->addfunc(data->data, ii->data, dir,
|
||||
/* XXX need to group errors here */
|
||||
!data->autostart, NULL))
|
||||
added = TRUE;
|
||||
if(added)
|
||||
data->donefunc(data->donedata);
|
||||
data->donefunc(data->data);
|
||||
if(NULL != dir)
|
||||
g_free(dir);
|
||||
}
|
||||
@@ -375,18 +375,18 @@ addresp(GtkWidget *widget, gint resp, gpointer gdata) {
|
||||
} while(0)
|
||||
|
||||
void
|
||||
makeinfowind(GtkWindow *parent, tr_handle_t *tr, int id) {
|
||||
makeinfowind(GtkWindow *parent, tr_torrent_t *tor) {
|
||||
tr_stat_t *sb;
|
||||
tr_info_t *in;
|
||||
GtkWidget *wind, *label;
|
||||
int ii;
|
||||
char *str;
|
||||
const int rowcount = 14;
|
||||
GtkWidget *table = gtk_table_new(rowcount, 2, FALSE);
|
||||
|
||||
/* XXX would be nice to be able to stat just one */
|
||||
if(id >= tr_torrentStat(tr, &sb))
|
||||
assert(!"XXX ");
|
||||
str = g_strdup_printf(_("%s Properties"), sb[id].info.name);
|
||||
sb = tr_torrentStat(tor);
|
||||
in = tr_torrentInfo(tor);
|
||||
str = g_strdup_printf(_("%s Properties"), in->name);
|
||||
wind = gtk_dialog_new_with_buttons(str, parent,
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
|
||||
GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
|
||||
@@ -401,7 +401,7 @@ makeinfowind(GtkWindow *parent, tr_handle_t *tr, int id) {
|
||||
|
||||
label = gtk_label_new(NULL);
|
||||
gtk_label_set_selectable(GTK_LABEL(label), TRUE);
|
||||
str = g_markup_printf_escaped("<big>%s</big>", sb[id].info.name);
|
||||
str = g_markup_printf_escaped("<big>%s</big>", in->name);
|
||||
gtk_label_set_markup(GTK_LABEL(label), str);
|
||||
g_free(str);
|
||||
gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 2, 0, 1);
|
||||
@@ -410,30 +410,30 @@ makeinfowind(GtkWindow *parent, tr_handle_t *tr, int id) {
|
||||
|
||||
INFOSEP(table, ii);
|
||||
|
||||
if(80 == sb[id].info.trackerPort)
|
||||
if(80 == in->trackerPort)
|
||||
INFOLINEA(table, ii, _("Tracker:"), g_strdup_printf("http://%s",
|
||||
sb[id].info.trackerAddress));
|
||||
in->trackerAddress));
|
||||
else
|
||||
INFOLINEA(table, ii, _("Tracker:"), g_strdup_printf("http://%s:%i",
|
||||
sb[id].info.trackerAddress, sb[id].info.trackerPort));
|
||||
INFOLINE(table, ii, _("Announce:"), sb[id].info.trackerAnnounce);
|
||||
INFOLINEA(table, ii, _("Piece Size:"), readablesize(sb[id].info.pieceSize));
|
||||
INFOLINEF(table, ii, "%i", _("Pieces:"), sb[id].info.pieceCount);
|
||||
INFOLINEA(table, ii, _("Total Size:"), readablesize(sb[id].info.totalSize));
|
||||
if(0 > sb[id].seeders)
|
||||
in->trackerAddress, in->trackerPort));
|
||||
INFOLINE(table, ii, _("Announce:"), in->trackerAnnounce);
|
||||
INFOLINEA(table, ii, _("Piece Size:"), readablesize(in->pieceSize));
|
||||
INFOLINEF(table, ii, "%i", _("Pieces:"), in->pieceCount);
|
||||
INFOLINEA(table, ii, _("Total Size:"), readablesize(in->totalSize));
|
||||
if(0 > sb->seeders)
|
||||
INFOLINE(table, ii, _("Seeders:"), _("?"));
|
||||
else
|
||||
INFOLINEF(table, ii, "%i", _("Seeders:"), sb[id].seeders);
|
||||
if(0 > sb[id].leechers)
|
||||
INFOLINEF(table, ii, "%i", _("Seeders:"), sb->seeders);
|
||||
if(0 > sb->leechers)
|
||||
INFOLINE(table, ii, _("Leechers:"), _("?"));
|
||||
else
|
||||
INFOLINEF(table, ii, "%i", _("Leechers:"), sb[id].leechers);
|
||||
INFOLINEF(table, ii, "%i", _("Leechers:"), sb->leechers);
|
||||
|
||||
INFOSEP(table, ii);
|
||||
|
||||
INFOLINE(table, ii, _("Directory:"), sb[id].folder);
|
||||
INFOLINEA(table, ii, _("Downloaded:"), readablesize(sb[id].downloaded));
|
||||
INFOLINEA(table, ii, _("Uploaded:"), readablesize(sb[id].uploaded));
|
||||
INFOLINE(table, ii, _("Directory:"), tr_torrentGetFolder(tor));
|
||||
INFOLINEA(table, ii, _("Downloaded:"), readablesize(sb->downloaded));
|
||||
INFOLINEA(table, ii, _("Uploaded:"), readablesize(sb->uploaded));
|
||||
|
||||
INFOSEP(table, ii);
|
||||
|
||||
@@ -443,5 +443,4 @@ makeinfowind(GtkWindow *parent, tr_handle_t *tr, int id) {
|
||||
g_signal_connect(G_OBJECT(wind), "response",
|
||||
G_CALLBACK(gtk_widget_destroy), NULL);
|
||||
gtk_widget_show_all(wind);
|
||||
free(sb);
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
#define DEFAULT_UPLIMIT 20
|
||||
|
||||
typedef gboolean (*add_torrent_func_t)(tr_handle_t*, GtkWindow*, const char*, const char *, gboolean, GList **);
|
||||
typedef gboolean (*add_torrent_func_t)(void *, const char *, const char *, gboolean, GList **);
|
||||
typedef void (*torrents_added_func_t)(void *);
|
||||
|
||||
void
|
||||
@@ -49,11 +49,11 @@ setlimit(tr_handle_t *tr);
|
||||
|
||||
/* show the "add a torrent" dialog */
|
||||
void
|
||||
makeaddwind(add_torrent_func_t addfunc, GtkWindow *parent, tr_handle_t *tr,
|
||||
torrents_added_func_t donefunc, void *donedata);
|
||||
makeaddwind(GtkWindow *parent, add_torrent_func_t addfunc,
|
||||
torrents_added_func_t donefunc, void *cbdata);
|
||||
|
||||
/* show the info window for a torrent */
|
||||
void
|
||||
makeinfowind(GtkWindow *parent, tr_handle_t *tr, int index);
|
||||
makeinfowind(GtkWindow *parent, tr_torrent_t *tor);
|
||||
|
||||
#endif /* TG_PREFS_H */
|
||||
|
||||
575
gtk/main.c
575
gtk/main.c
@@ -27,6 +27,7 @@
|
||||
#include <sys/param.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -50,7 +51,7 @@
|
||||
struct cbdata {
|
||||
tr_handle_t *tr;
|
||||
GtkWindow *wind;
|
||||
GtkListStore *model;
|
||||
GtkTreeModel *model;
|
||||
GtkTreeView *view;
|
||||
GtkStatusbar *bar;
|
||||
GtkWidget **buttons;
|
||||
@@ -77,21 +78,21 @@ tr_pieces_free(gpointer);
|
||||
|
||||
void
|
||||
makewind(GtkWidget *wind, tr_handle_t *tr, GList *saved);
|
||||
GtkWidget *
|
||||
makewind_toolbar(struct cbdata *data);
|
||||
GtkWidget *
|
||||
makewind_list(struct cbdata *data);
|
||||
gboolean
|
||||
winclose(GtkWidget *widget, GdkEvent *event, gpointer gdata);
|
||||
gboolean
|
||||
exitcheck(gpointer gdata);
|
||||
void
|
||||
stoptransmission(void *tr);
|
||||
stoptransmission(tr_handle_t *tr);
|
||||
void
|
||||
setupdrag(GtkWidget *widget, struct cbdata *data);
|
||||
void
|
||||
gotdrag(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
|
||||
GtkSelectionData *sel, guint info, guint time, gpointer gdata);
|
||||
GtkWidget *
|
||||
makewind_toolbar(struct cbdata *data);
|
||||
GtkWidget *
|
||||
makewind_list(struct cbdata *data);
|
||||
static void
|
||||
stylekludge(GObject *obj, GParamSpec *spec, gpointer gdata);
|
||||
void
|
||||
@@ -113,9 +114,11 @@ void
|
||||
dopopupmenu(GdkEventButton *event, struct cbdata *data,
|
||||
GList *ids, int status);
|
||||
void
|
||||
killmenu(GtkWidget *menu, gpointer *gdata SHUTUP);
|
||||
killmenu(GtkWidget *menu, gpointer *gdata);
|
||||
void
|
||||
actionclick(GtkWidget *widget, gpointer gdata);
|
||||
void
|
||||
findtorrent(GtkTreeModel *model, tr_torrent_t *tor, GtkTreeIter *iter);
|
||||
gint
|
||||
intrevcmp(gconstpointer a, gconstpointer b);
|
||||
void
|
||||
@@ -123,18 +126,24 @@ doubleclick(GtkWidget *widget, GtkTreePath *path, GtkTreeViewColumn *col,
|
||||
gpointer gdata);
|
||||
|
||||
gboolean
|
||||
addtorrent(tr_handle_t *tr, GtkWindow *parentwind, const char *torrent,
|
||||
const char *dir, gboolean paused, GList **errs);
|
||||
addtorrent(void *vdata, const char *torrent, const char *dir, gboolean paused,
|
||||
GList **errs);
|
||||
void
|
||||
addedtorrents(void *vdata);
|
||||
gboolean
|
||||
savetorrents(tr_handle_t *tr, GtkWindow *wind, int count, tr_stat_t *stat);
|
||||
savetorrents(tr_handle_t *tr, GtkWindow *wind);
|
||||
void
|
||||
orstatus(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
|
||||
gpointer gdata);
|
||||
void
|
||||
makeidlist(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
|
||||
gpointer gdata);
|
||||
void
|
||||
maketorrentlist(tr_torrent_t *tor, void *data);
|
||||
void
|
||||
setupsighandlers(void);
|
||||
void
|
||||
fatalsig(int sig);
|
||||
|
||||
#define TR_TYPE_PIECES_NAME "tr-type-pieces"
|
||||
#define TR_TYPE_PIECES ((const GType)tr_type_pieces)
|
||||
@@ -146,7 +155,7 @@ enum listact { ACT_OPEN, ACT_START, ACT_STOP, ACT_DELETE, ACT_INFO, ACT_PREF };
|
||||
#define LIST_ACTION_FROM "torrent-list-action-from"
|
||||
enum listfrom { FROM_BUTTON, FROM_POPUP };
|
||||
|
||||
#define LIST_INDEX "torrent-list-indexes"
|
||||
#define LIST_IDS "torrent-list-ids"
|
||||
#define LIST_MENU_WIDGET "torrent-list-popup-menu-widget"
|
||||
|
||||
struct { const gchar *name; const gchar *id; enum listact act; gboolean nomenu;
|
||||
@@ -155,10 +164,10 @@ actionitems[] = {
|
||||
{N_("Add"), GTK_STOCK_ADD, ACT_OPEN, FALSE, 0,
|
||||
N_("Add a new torrent"), "XXX"},
|
||||
{N_("Start"), GTK_STOCK_EXECUTE, ACT_START, FALSE,
|
||||
(TR_STATUS_STOPPING | TR_STATUS_PAUSE),
|
||||
TR_STATUS_INACTIVE,
|
||||
N_("Start a torrent that is not running"), "XXX"},
|
||||
{N_("Stop"), GTK_STOCK_STOP, ACT_STOP, FALSE,
|
||||
~(TR_STATUS_STOPPING | TR_STATUS_PAUSE),
|
||||
TR_STATUS_ACTIVE,
|
||||
N_("Stop a torrent that is running"), "XXX"},
|
||||
{N_("Remove"), GTK_STOCK_REMOVE, ACT_DELETE, FALSE, ~0,
|
||||
N_("Remove a torrent"), "XXX"},
|
||||
@@ -169,6 +178,12 @@ actionitems[] = {
|
||||
};
|
||||
|
||||
#define CBDATA_PTR "callback-data-pointer"
|
||||
|
||||
#define SIGCOUNT_MAX 3
|
||||
|
||||
static sig_atomic_t global_sigcount = 0;
|
||||
static int global_lastsig = 0;
|
||||
|
||||
int
|
||||
main(int argc, char **argv) {
|
||||
GtkWidget *mainwind, *preferr, *stateerr;
|
||||
@@ -178,6 +193,8 @@ main(int argc, char **argv) {
|
||||
const char *pref;
|
||||
long intval;
|
||||
|
||||
setupsighandlers();
|
||||
|
||||
gtk_init(&argc, &argv);
|
||||
|
||||
bindtextdomain("transmission-gtk", LOCALEDIR);
|
||||
@@ -187,8 +204,6 @@ main(int argc, char **argv) {
|
||||
|
||||
tr = tr_init();
|
||||
|
||||
setuphandlers(stoptransmission, tr);
|
||||
|
||||
gtk_rc_parse_string(
|
||||
"style \"transmission-standard\" {\n"
|
||||
" GtkDialog::action-area-border = 6\n"
|
||||
@@ -305,8 +320,8 @@ makewind(GtkWidget *wind, tr_handle_t *tr, GList *saved) {
|
||||
loaderrs = NULL;
|
||||
for(ii = g_list_first(saved); NULL != ii; ii = ii->next) {
|
||||
ts = ii->data;
|
||||
addtorrent(tr, GTK_WINDOW(wind), ts->ts_torrent, ts->ts_directory,
|
||||
ts->ts_paused, &loaderrs);
|
||||
addtorrent(data, ts->ts_torrent, ts->ts_directory, ts->ts_paused,
|
||||
&loaderrs);
|
||||
cf_freestate(ts);
|
||||
}
|
||||
g_list_free(saved);
|
||||
@@ -319,7 +334,7 @@ makewind(GtkWidget *wind, tr_handle_t *tr, GList *saved) {
|
||||
g_list_foreach(loaderrs, (GFunc)g_free, NULL);
|
||||
g_list_free(loaderrs);
|
||||
g_free(str);
|
||||
savetorrents(tr, GTK_WINDOW(wind), -1, NULL);
|
||||
savetorrents(tr, GTK_WINDOW(wind));
|
||||
}
|
||||
|
||||
data->timer = g_timeout_add(500, updatemodel, data);
|
||||
@@ -340,35 +355,126 @@ makewind(GtkWidget *wind, tr_handle_t *tr, GList *saved) {
|
||||
gtk_widget_show(wind);
|
||||
}
|
||||
|
||||
/* XXX is this the right thing to do? */
|
||||
#define TR_TORRENT_NEEDS_STOP(t) \
|
||||
((t) & TR_STATUS_CHECK || (t) & TR_STATUS_DOWNLOAD || (t) & TR_STATUS_SEED)
|
||||
GtkWidget *
|
||||
makewind_toolbar(struct cbdata *data) {
|
||||
GtkWidget *bar = gtk_toolbar_new();
|
||||
GtkToolItem *item;
|
||||
unsigned int ii;
|
||||
|
||||
gtk_toolbar_set_tooltips(GTK_TOOLBAR(bar), TRUE);
|
||||
gtk_toolbar_set_show_arrow(GTK_TOOLBAR(bar), FALSE);
|
||||
gtk_toolbar_set_style(GTK_TOOLBAR(bar), GTK_TOOLBAR_BOTH);
|
||||
|
||||
data->buttons = g_new(GtkWidget*, ALEN(actionitems));
|
||||
|
||||
for(ii = 0; ii < ALEN(actionitems); ii++) {
|
||||
item = gtk_tool_button_new_from_stock(actionitems[ii].id);
|
||||
data->buttons[ii] = GTK_WIDGET(item);
|
||||
gtk_tool_button_set_label(GTK_TOOL_BUTTON(item),
|
||||
gettext(actionitems[ii].name));
|
||||
gtk_tool_item_set_tooltip(GTK_TOOL_ITEM(item), GTK_TOOLBAR(bar)->tooltips,
|
||||
gettext(actionitems[ii].ttext),
|
||||
actionitems[ii].tpriv);
|
||||
g_object_set_data(G_OBJECT(item), LIST_ACTION,
|
||||
GINT_TO_POINTER(actionitems[ii].act));
|
||||
g_object_set_data(G_OBJECT(item), LIST_ACTION_FROM,
|
||||
GINT_TO_POINTER(FROM_BUTTON));
|
||||
g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(actionclick), data);
|
||||
gtk_toolbar_insert(GTK_TOOLBAR(bar), item, -1);
|
||||
}
|
||||
|
||||
return bar;
|
||||
}
|
||||
|
||||
/* XXX check for unused data in model */
|
||||
enum {MC_NAME, MC_SIZE, MC_STAT, MC_ERR, MC_TERR, MC_PROG, MC_DRATE, MC_URATE,
|
||||
MC_ETA, MC_PEERS, MC_UPEERS, MC_DPEERS, MC_PIECES, MC_DOWN, MC_UP,
|
||||
MC_TORRENT, MC_ROW_COUNT};
|
||||
|
||||
GtkWidget *
|
||||
makewind_list(struct cbdata *data) {
|
||||
GType types[] = {
|
||||
/* info->name, info->totalSize, status, error, trackerError, */
|
||||
G_TYPE_STRING, G_TYPE_UINT64, G_TYPE_INT, G_TYPE_INT, G_TYPE_STRING,
|
||||
/* progress, rateDownload, rateUpload, eta, peersTotal, */
|
||||
G_TYPE_FLOAT, G_TYPE_FLOAT, G_TYPE_FLOAT, G_TYPE_INT, G_TYPE_INT,
|
||||
/* peersUploading, peersDownloading, pieces, downloaded, */
|
||||
G_TYPE_INT, G_TYPE_INT, TR_TYPE_PIECES, G_TYPE_UINT64,
|
||||
/* uploaded, the handle for the torrent */
|
||||
G_TYPE_UINT64, G_TYPE_POINTER};
|
||||
GtkListStore *store;
|
||||
GtkWidget *view;
|
||||
GtkTreeViewColumn *col;
|
||||
GtkTreeSelection *sel;
|
||||
GtkCellRenderer *namerend, *progrend;
|
||||
char *str;
|
||||
|
||||
assert(MC_ROW_COUNT == ALEN(types));
|
||||
|
||||
store = gtk_list_store_newv(MC_ROW_COUNT, types);
|
||||
view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
|
||||
/* XXX do I need to worry about reference counts anywhere else? */
|
||||
g_object_unref(G_OBJECT(store));
|
||||
data->model = GTK_TREE_MODEL(store);
|
||||
data->view = GTK_TREE_VIEW(view);
|
||||
|
||||
namerend = gtk_cell_renderer_text_new();
|
||||
col = gtk_tree_view_column_new_with_attributes(_("Name"), namerend, NULL);
|
||||
gtk_tree_view_column_set_cell_data_func(col, namerend, dfname, NULL, NULL);
|
||||
gtk_tree_view_column_set_expand(col, TRUE);
|
||||
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
|
||||
|
||||
progrend = tr_cell_renderer_torrent_new();
|
||||
/* this string is only used to determing the size of the progress bar */
|
||||
str = g_markup_printf_escaped("<big>%s</big>", _(" fnord fnord "));
|
||||
g_object_set(progrend, "label", str, NULL);
|
||||
g_free(str);
|
||||
col = gtk_tree_view_column_new_with_attributes(_("Progress"), progrend, NULL);
|
||||
gtk_tree_view_column_set_cell_data_func(col, progrend, dfprog, NULL, NULL);
|
||||
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
|
||||
|
||||
/* XXX this shouldn't be necessary */
|
||||
g_signal_connect(view, "notify", G_CALLBACK(stylekludge), progrend);
|
||||
|
||||
gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE);
|
||||
sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
|
||||
gtk_tree_selection_set_mode(GTK_TREE_SELECTION(sel), GTK_SELECTION_MULTIPLE);
|
||||
g_signal_connect(G_OBJECT(sel), "changed", G_CALLBACK(fixbuttons), data);
|
||||
g_signal_connect(G_OBJECT(view), "button-press-event",
|
||||
G_CALLBACK(listclick), data);
|
||||
g_signal_connect(G_OBJECT(view), "popup-menu", G_CALLBACK(listpopup), data);
|
||||
g_signal_connect(G_OBJECT(view), "row-activated",
|
||||
G_CALLBACK(doubleclick), data);
|
||||
gtk_widget_show_all(view);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
gboolean
|
||||
winclose(GtkWidget *widget SHUTUP, GdkEvent *event SHUTUP, gpointer gdata) {
|
||||
struct cbdata *data = gdata;
|
||||
struct exitdata *edata;
|
||||
tr_stat_t *st;
|
||||
int ii;
|
||||
GtkTreeIter iter;
|
||||
tr_torrent_t *tor;
|
||||
gboolean going;
|
||||
|
||||
if(0 >= data->timer)
|
||||
g_source_remove(data->timer);
|
||||
data->timer = -1;
|
||||
|
||||
blocksigs();
|
||||
|
||||
for(ii = tr_torrentStat(data->tr, &st); 0 < ii; ii--) {
|
||||
if(TR_TORRENT_NEEDS_STOP(st[ii-1].status)) {
|
||||
/*fprintf(stderr, "quit: stopping %i %s\n", ii, st[ii-1].info.name);*/
|
||||
tr_torrentStop(data->tr, ii - 1);
|
||||
going = gtk_tree_model_get_iter_first(data->model, &iter);
|
||||
while(going) {
|
||||
gtk_tree_model_get(data->model, &iter, MC_TORRENT, &tor, -1);
|
||||
st = tr_torrentStat(tor);
|
||||
if(TR_STATUS_ACTIVE & st->status) {
|
||||
tr_torrentStop(tor);
|
||||
going = gtk_tree_model_iter_next(data->model, &iter);
|
||||
} else {
|
||||
/*fprintf(stderr, "quit: closing %i %s\n", ii, st[ii-1].info.name);*/
|
||||
tr_torrentClose(data->tr, ii - 1);
|
||||
tr_torrentClose(data->tr, tor);
|
||||
going = gtk_list_store_remove(GTK_LIST_STORE(data->model), &iter);
|
||||
}
|
||||
}
|
||||
free(st);
|
||||
|
||||
unblocksigs();
|
||||
|
||||
/* XXX should disable widgets or something */
|
||||
|
||||
@@ -378,8 +484,6 @@ winclose(GtkWidget *widget SHUTUP, GdkEvent *event SHUTUP, gpointer gdata) {
|
||||
edata->started = time(NULL);
|
||||
edata->timer = g_timeout_add(500, exitcheck, edata);
|
||||
|
||||
/*fprintf(stderr, "quit: starting timeout at %i\n", edata->started);*/
|
||||
|
||||
/* returning FALSE means to destroy the window */
|
||||
return TRUE;
|
||||
}
|
||||
@@ -388,25 +492,27 @@ gboolean
|
||||
exitcheck(gpointer gdata) {
|
||||
struct exitdata *data = gdata;
|
||||
tr_stat_t *st;
|
||||
int ii;
|
||||
GtkTreeIter iter;
|
||||
tr_torrent_t *tor;
|
||||
gboolean go;
|
||||
|
||||
blocksigs();
|
||||
|
||||
for(ii = tr_torrentStat(data->cbdata->tr, &st); 0 < ii; ii--) {
|
||||
if(TR_STATUS_PAUSE & st[ii-1].status) {
|
||||
/*fprintf(stderr, "quit: closing %i %s\n", ii, st[ii-1].info.name);*/
|
||||
tr_torrentClose(data->cbdata->tr, ii - 1);
|
||||
go = gtk_tree_model_get_iter_first(data->cbdata->model, &iter);
|
||||
while(go) {
|
||||
gtk_tree_model_get(data->cbdata->model, &iter, MC_TORRENT, &tor, -1);
|
||||
st = tr_torrentStat(tor);
|
||||
if(!(TR_STATUS_PAUSE & st->status))
|
||||
go = gtk_tree_model_iter_next(data->cbdata->model, &iter);
|
||||
else {
|
||||
tr_torrentClose(data->cbdata->tr, tor);
|
||||
go = gtk_list_store_remove(GTK_LIST_STORE(data->cbdata->model), &iter);
|
||||
}
|
||||
}
|
||||
free(st);
|
||||
|
||||
/*fprintf(stderr, "quit: %i torrents left at %i\n",
|
||||
tr_torrentCount(data->cbdata->tr), time(NULL));*/
|
||||
/* keep going if we still have torrents and haven't hit the exit timeout */
|
||||
if(0 < tr_torrentCount(data->cbdata->tr) &&
|
||||
time(NULL) - data->started < TRACKER_EXIT_TIMEOUT) {
|
||||
assert(gtk_tree_model_get_iter_first(data->cbdata->model, &iter));
|
||||
updatemodel(data->cbdata);
|
||||
unblocksigs();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -416,11 +522,7 @@ exitcheck(gpointer gdata) {
|
||||
g_source_remove(data->timer);
|
||||
data->timer = -1;
|
||||
|
||||
/*fprintf(stderr, "quit: giving up on %i torrents\n",
|
||||
tr_torrentCount(data->cbdata->tr));*/
|
||||
stoptransmission(data->cbdata->tr);
|
||||
clearhandlers();
|
||||
unblocksigs();
|
||||
|
||||
gtk_widget_destroy(GTK_WIDGET(data->cbdata->wind));
|
||||
g_free(data->cbdata);
|
||||
@@ -431,9 +533,15 @@ exitcheck(gpointer gdata) {
|
||||
}
|
||||
|
||||
void
|
||||
stoptransmission(void *tr) {
|
||||
while(0 < tr_torrentCount(tr))
|
||||
tr_torrentClose(tr, 0);
|
||||
stoptransmission(tr_handle_t *tr) {
|
||||
GList *list, *ii;
|
||||
|
||||
list = NULL;
|
||||
tr_torrentIterate(tr, maketorrentlist, &list);
|
||||
for(ii = g_list_first(list); NULL != ii; ii = ii->next)
|
||||
tr_torrentClose(tr, ii->data);
|
||||
g_list_free(list);
|
||||
|
||||
tr_close(tr);
|
||||
}
|
||||
|
||||
@@ -500,7 +608,7 @@ gotdrag(GtkWidget *widget SHUTUP, GdkDragContext *dc, gint x SHUTUP,
|
||||
0 == g_stat(hostless, &sb))
|
||||
deslashed = hostless;
|
||||
/* finally, try to add it as a torrent */
|
||||
if(addtorrent(data->tr, data->wind, deslashed, NULL, FALSE, &errs))
|
||||
if(addtorrent(data, deslashed, NULL, FALSE, &errs))
|
||||
gotfile = TRUE;
|
||||
}
|
||||
}
|
||||
@@ -541,99 +649,6 @@ setupdrag(GtkWidget *widget, struct cbdata *data) {
|
||||
ALEN(targets), GDK_ACTION_COPY | GDK_ACTION_MOVE);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
makewind_toolbar(struct cbdata *data) {
|
||||
GtkWidget *bar = gtk_toolbar_new();
|
||||
GtkToolItem *item;
|
||||
unsigned int ii;
|
||||
|
||||
gtk_toolbar_set_tooltips(GTK_TOOLBAR(bar), TRUE);
|
||||
gtk_toolbar_set_show_arrow(GTK_TOOLBAR(bar), FALSE);
|
||||
gtk_toolbar_set_style(GTK_TOOLBAR(bar), GTK_TOOLBAR_BOTH);
|
||||
|
||||
data->buttons = g_new(GtkWidget*, ALEN(actionitems));
|
||||
|
||||
for(ii = 0; ii < ALEN(actionitems); ii++) {
|
||||
item = gtk_tool_button_new_from_stock(actionitems[ii].id);
|
||||
data->buttons[ii] = GTK_WIDGET(item);
|
||||
gtk_tool_button_set_label(GTK_TOOL_BUTTON(item), gettext(actionitems[ii].name));
|
||||
gtk_tool_item_set_tooltip(GTK_TOOL_ITEM(item), GTK_TOOLBAR(bar)->tooltips,
|
||||
gettext(actionitems[ii].ttext), actionitems[ii].tpriv);
|
||||
g_object_set_data(G_OBJECT(item), LIST_ACTION,
|
||||
GINT_TO_POINTER(actionitems[ii].act));
|
||||
g_object_set_data(G_OBJECT(item), LIST_ACTION_FROM,
|
||||
GINT_TO_POINTER(FROM_BUTTON));
|
||||
g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(actionclick), data);
|
||||
gtk_toolbar_insert(GTK_TOOLBAR(bar), item, -1);
|
||||
}
|
||||
|
||||
return bar;
|
||||
}
|
||||
|
||||
/* XXX check for unused data in model */
|
||||
enum {MC_NAME, MC_SIZE, MC_STAT, MC_ERR, MC_PROG, MC_DRATE, MC_URATE,
|
||||
MC_ETA, MC_PEERS, MC_UPEERS, MC_DPEERS, MC_PIECES, MC_DOWN, MC_UP,
|
||||
MC_ROW_INDEX, MC_ROW_COUNT};
|
||||
|
||||
GtkWidget *
|
||||
makewind_list(struct cbdata *data) {
|
||||
GType types[] = {
|
||||
/* info->name, info->totalSize, status, error, progress */
|
||||
G_TYPE_STRING, G_TYPE_UINT64, G_TYPE_INT, G_TYPE_STRING, G_TYPE_FLOAT,
|
||||
/* rateDownload, rateUpload, eta, peersTotal, peersUploading */
|
||||
G_TYPE_FLOAT, G_TYPE_FLOAT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT,
|
||||
/* peersDownloading, pieces, downloaded, uploaded */
|
||||
G_TYPE_INT, TR_TYPE_PIECES, G_TYPE_UINT64, G_TYPE_UINT64,
|
||||
/* index into the torrent array */
|
||||
G_TYPE_INT};
|
||||
GtkListStore *model;
|
||||
GtkWidget *view;
|
||||
GtkTreeViewColumn *col;
|
||||
GtkTreeSelection *sel;
|
||||
GtkCellRenderer *namerend, *progrend;
|
||||
char *str;
|
||||
|
||||
assert(MC_ROW_COUNT == ALEN(types));
|
||||
|
||||
model = gtk_list_store_newv(MC_ROW_COUNT, types);
|
||||
view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
|
||||
/* XXX do I need to worry about reference counts anywhere else? */
|
||||
g_object_unref(G_OBJECT(model));
|
||||
data->model = model;
|
||||
data->view = GTK_TREE_VIEW(view);
|
||||
|
||||
namerend = gtk_cell_renderer_text_new();
|
||||
col = gtk_tree_view_column_new_with_attributes(_("Name"), namerend, NULL);
|
||||
gtk_tree_view_column_set_cell_data_func(col, namerend, dfname, NULL, NULL);
|
||||
gtk_tree_view_column_set_expand(col, TRUE);
|
||||
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
|
||||
|
||||
progrend = tr_cell_renderer_torrent_new();
|
||||
/* this string is only used to determing the size of the progress bar */
|
||||
str = g_markup_printf_escaped("<big>%s</big>", _(" fnord fnord "));
|
||||
g_object_set(progrend, "label", str, NULL);
|
||||
g_free(str);
|
||||
col = gtk_tree_view_column_new_with_attributes(_("Progress"), progrend, NULL);
|
||||
gtk_tree_view_column_set_cell_data_func(col, progrend, dfprog, NULL, NULL);
|
||||
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
|
||||
|
||||
/* XXX this shouldn't be necessary */
|
||||
g_signal_connect(view, "notify", G_CALLBACK(stylekludge), progrend);
|
||||
|
||||
gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE);
|
||||
sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
|
||||
gtk_tree_selection_set_mode(GTK_TREE_SELECTION(sel), GTK_SELECTION_MULTIPLE);
|
||||
g_signal_connect(G_OBJECT(sel), "changed", G_CALLBACK(fixbuttons), data);
|
||||
g_signal_connect(G_OBJECT(view), "button-press-event",
|
||||
G_CALLBACK(listclick), data);
|
||||
g_signal_connect(G_OBJECT(view), "popup-menu", G_CALLBACK(listpopup), data);
|
||||
g_signal_connect(G_OBJECT(view), "row-activated",
|
||||
G_CALLBACK(doubleclick), data);
|
||||
gtk_widget_show_all(view);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
/* kludge to have the progress bars notice theme changes */
|
||||
static void
|
||||
stylekludge(GObject *obj, GParamSpec *spec, gpointer gdata) {
|
||||
@@ -666,15 +681,14 @@ fixbuttons(GtkTreeSelection *sel, gpointer gdata) {
|
||||
void
|
||||
dfname(GtkTreeViewColumn *col SHUTUP, GtkCellRenderer *rend,
|
||||
GtkTreeModel *model, GtkTreeIter *iter, gpointer gdata SHUTUP) {
|
||||
char *name, *mb, *err, *str, *top, *bottom;
|
||||
char *name, *mb, *terr, *str, *top, *bottom;
|
||||
guint64 size;
|
||||
gfloat prog;
|
||||
int status, eta, tpeers, upeers, dpeers;
|
||||
int status, err, eta, tpeers, upeers, dpeers;
|
||||
|
||||
/* XXX should I worry about gtk_tree_model_get failing? */
|
||||
gtk_tree_model_get(model, iter, MC_NAME, &name, MC_STAT, &status,
|
||||
MC_SIZE, &size, MC_PROG, &prog, MC_ETA, &eta, MC_PEERS, &tpeers,
|
||||
MC_UPEERS, &upeers, MC_DPEERS, &dpeers, -1);
|
||||
MC_ERR, &err, MC_SIZE, &size, MC_PROG, &prog, MC_ETA, &eta,
|
||||
MC_PEERS, &tpeers, MC_UPEERS, &upeers, MC_DPEERS, &dpeers, -1);
|
||||
|
||||
if(0 > tpeers)
|
||||
tpeers = 0;
|
||||
@@ -707,10 +721,10 @@ dfname(GtkTreeViewColumn *col SHUTUP, GtkCellRenderer *rend,
|
||||
assert("XXX unknown status");
|
||||
}
|
||||
|
||||
if(status & TR_TRACKER_ERROR) {
|
||||
gtk_tree_model_get(model, iter, MC_ERR, &err, -1);
|
||||
bottom = g_strconcat(_("Error: "), err, NULL);
|
||||
g_free(err);
|
||||
if(TR_NOERROR != err) {
|
||||
gtk_tree_model_get(model, iter, MC_TERR, &terr, -1);
|
||||
bottom = g_strconcat(_("Error: "), terr, NULL);
|
||||
g_free(terr);
|
||||
}
|
||||
else if(status & TR_STATUS_DOWNLOAD)
|
||||
bottom = g_strdup_printf(ngettext("Downloading from %i of %i peer",
|
||||
@@ -736,7 +750,6 @@ dfprog(GtkTreeViewColumn *col SHUTUP, GtkCellRenderer *rend,
|
||||
gfloat prog, dl, ul;
|
||||
guint64 down, up;
|
||||
|
||||
/* XXX should I worry about gtk_tree_model_get failing? */
|
||||
gtk_tree_model_get(model, iter, MC_PROG, &prog, MC_DRATE, &dl, MC_URATE, &ul,
|
||||
MC_DOWN, &down, MC_UP, &up, -1);
|
||||
if(0.0 > prog)
|
||||
@@ -763,36 +776,34 @@ dfprog(GtkTreeViewColumn *col SHUTUP, GtkCellRenderer *rend,
|
||||
gboolean
|
||||
updatemodel(gpointer gdata) {
|
||||
struct cbdata *data = gdata;
|
||||
tr_torrent_t *tor;
|
||||
tr_stat_t *st;
|
||||
int ii, max;
|
||||
tr_info_t *in;
|
||||
GtkTreeIter iter;
|
||||
float up, down;
|
||||
char *upstr, *downstr, *str;
|
||||
|
||||
blocksigs();
|
||||
|
||||
max = tr_torrentStat(data->tr, &st);
|
||||
for(ii = 0; ii < max; ii++) {
|
||||
if(!(ii ? gtk_tree_model_iter_next(GTK_TREE_MODEL(data->model), &iter) :
|
||||
gtk_tree_model_get_iter_first(GTK_TREE_MODEL(data->model), &iter)))
|
||||
gtk_list_store_append(data->model, &iter);
|
||||
/* XXX find out if setting the same data emits changed signal */
|
||||
gtk_list_store_set(
|
||||
data->model, &iter, MC_ROW_INDEX, ii,
|
||||
MC_NAME, st[ii].info.name, MC_SIZE, st[ii].info.totalSize,
|
||||
MC_STAT, st[ii].status, MC_ERR, st[ii].error, MC_PROG, st[ii].progress,
|
||||
MC_DRATE, st[ii].rateDownload, MC_URATE, st[ii].rateUpload,
|
||||
MC_ETA, st[ii].eta, MC_PEERS, st[ii].peersTotal,
|
||||
MC_UPEERS, st[ii].peersUploading, MC_DPEERS, st[ii].peersDownloading,
|
||||
MC_DOWN, st[ii].downloaded, MC_UP, st[ii].uploaded, -1);
|
||||
if(0 < global_sigcount) {
|
||||
stoptransmission(data->tr);
|
||||
global_sigcount = SIGCOUNT_MAX;
|
||||
raise(global_lastsig);
|
||||
}
|
||||
free(st);
|
||||
|
||||
/* remove any excess rows */
|
||||
if(ii ? gtk_tree_model_iter_next(GTK_TREE_MODEL(data->model), &iter) :
|
||||
gtk_tree_model_get_iter_first(GTK_TREE_MODEL(data->model), &iter))
|
||||
while(gtk_list_store_remove(data->model, &iter))
|
||||
;
|
||||
if(gtk_tree_model_get_iter_first(data->model, &iter)) {
|
||||
do {
|
||||
gtk_tree_model_get(data->model, &iter, MC_TORRENT, &tor, -1);
|
||||
st = tr_torrentStat(tor);
|
||||
in = tr_torrentInfo(tor);
|
||||
/* XXX find out if setting the same data emits changed signal */
|
||||
gtk_list_store_set(GTK_LIST_STORE(data->model), &iter, MC_NAME, in->name,
|
||||
MC_SIZE, in->totalSize, MC_STAT, st->status, MC_ERR, st->error,
|
||||
MC_TERR, st->trackerError, MC_PROG, st->progress,
|
||||
MC_DRATE, st->rateDownload, MC_URATE, st->rateUpload, MC_ETA, st->eta,
|
||||
MC_PEERS, st->peersTotal, MC_UPEERS, st->peersUploading,
|
||||
MC_DPEERS, st->peersDownloading, MC_DOWN, st->downloaded,
|
||||
MC_UP, st->uploaded, -1);
|
||||
} while(gtk_tree_model_iter_next(data->model, &iter));
|
||||
}
|
||||
|
||||
/* update the status bar */
|
||||
tr_torrentRates(data->tr, &up, &down);
|
||||
@@ -809,8 +820,6 @@ updatemodel(gpointer gdata) {
|
||||
/* the status of the selected item may have changed, so update the buttons */
|
||||
fixbuttons(NULL, data);
|
||||
|
||||
unblocksigs();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -821,7 +830,8 @@ listclick(GtkWidget *widget SHUTUP, GdkEventButton *event, gpointer gdata) {
|
||||
GtkTreeSelection *sel = gtk_tree_view_get_selection(data->view);
|
||||
GtkTreePath *path;
|
||||
GtkTreeIter iter;
|
||||
int index, status;
|
||||
int status;
|
||||
gpointer tor;
|
||||
GList *ids;
|
||||
|
||||
if(GDK_BUTTON_PRESS == event->type && 3 == event->button) {
|
||||
@@ -831,19 +841,18 @@ listclick(GtkWidget *widget SHUTUP, GdkEventButton *event, gpointer gdata) {
|
||||
/* no row was clicked, do the popup with no torrent IDs or status */
|
||||
dopopupmenu(event, data, NULL, 0);
|
||||
else {
|
||||
if(gtk_tree_model_get_iter(GTK_TREE_MODEL(data->model), &iter, path)) {
|
||||
if(gtk_tree_model_get_iter(data->model, &iter, path)) {
|
||||
/* get ID and status for the right-clicked row */
|
||||
gtk_tree_model_get(GTK_TREE_MODEL(data->model), &iter,
|
||||
MC_ROW_INDEX, &index, MC_STAT, &status, -1);
|
||||
gtk_tree_model_get(data->model, &iter, MC_TORRENT, &tor,
|
||||
MC_STAT, &status, -1);
|
||||
/* get a list of selected IDs */
|
||||
ids = NULL;
|
||||
gtk_tree_selection_selected_foreach(sel, makeidlist, &ids);
|
||||
/* is the clicked row selected? */
|
||||
if(NULL == g_list_find(ids, GINT_TO_POINTER(index))) {
|
||||
if(NULL == g_list_find(ids, tor)) {
|
||||
/* no, do the popup for just the clicked row */
|
||||
g_list_free(ids);
|
||||
dopopupmenu(event, data, g_list_append(NULL, GINT_TO_POINTER(index)),
|
||||
status);
|
||||
dopopupmenu(event, data, g_list_append(NULL, tor), status);
|
||||
} else {
|
||||
/* yes, do the popup for all the selected rows */
|
||||
gtk_tree_selection_selected_foreach(sel, orstatus, &status);
|
||||
@@ -862,14 +871,12 @@ gboolean
|
||||
listpopup(GtkWidget *widget SHUTUP, gpointer gdata) {
|
||||
struct cbdata *data = gdata;
|
||||
GtkTreeSelection *sel = gtk_tree_view_get_selection(data->view);
|
||||
GtkTreeModel *model;
|
||||
GList *ids;
|
||||
int status;
|
||||
|
||||
if(0 >= gtk_tree_selection_count_selected_rows(sel))
|
||||
dopopupmenu(NULL, data, NULL, 0);
|
||||
else {
|
||||
assert(model == GTK_TREE_MODEL(data->model));
|
||||
status = 0;
|
||||
gtk_tree_selection_selected_foreach(sel, orstatus, &status);
|
||||
ids = NULL;
|
||||
@@ -900,7 +907,7 @@ dopopupmenu(GdkEventButton *event, struct cbdata *data,
|
||||
g_object_set_data(G_OBJECT(item), LIST_ACTION_FROM,
|
||||
GINT_TO_POINTER(FROM_POPUP));
|
||||
/* set a glist of selected torrent's IDs */
|
||||
g_object_set_data(G_OBJECT(item), LIST_INDEX, ids);
|
||||
g_object_set_data(G_OBJECT(item), LIST_IDS, ids);
|
||||
/* set the menu widget, so the activate handler can destroy it */
|
||||
g_object_set_data(G_OBJECT(item), LIST_MENU_WIDGET, menu);
|
||||
g_signal_connect(G_OBJECT(item), "activate",
|
||||
@@ -909,7 +916,7 @@ dopopupmenu(GdkEventButton *event, struct cbdata *data,
|
||||
}
|
||||
|
||||
/* set up the glist to be freed when the menu is destroyed */
|
||||
g_object_set_data_full(G_OBJECT(menu), LIST_INDEX, ids,
|
||||
g_object_set_data_full(G_OBJECT(menu), LIST_IDS, ids,
|
||||
(GDestroyNotify)g_list_free);
|
||||
|
||||
/* destroy the menu if the user doesn't select anything */
|
||||
@@ -935,11 +942,12 @@ actionclick(GtkWidget *widget, gpointer gdata) {
|
||||
GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), LIST_ACTION));
|
||||
enum listfrom from =
|
||||
GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), LIST_ACTION_FROM));
|
||||
int index, count;
|
||||
unsigned int actindex;
|
||||
tr_stat_t *sb;
|
||||
GList *ids, *ii;
|
||||
tr_torrent_t *tor;
|
||||
gboolean updatesave;
|
||||
GtkTreeIter iter;
|
||||
|
||||
/* destroy the popup menu, if any */
|
||||
if(FROM_POPUP == from)
|
||||
@@ -947,7 +955,7 @@ actionclick(GtkWidget *widget, gpointer gdata) {
|
||||
|
||||
switch(act) {
|
||||
case ACT_OPEN:
|
||||
makeaddwind(addtorrent, data->wind, data->tr, addedtorrents, data);
|
||||
makeaddwind(data->wind, addtorrent, addedtorrents, data);
|
||||
return;
|
||||
case ACT_PREF:
|
||||
if(!data->prefsopen)
|
||||
@@ -961,10 +969,9 @@ actionclick(GtkWidget *widget, gpointer gdata) {
|
||||
case FROM_BUTTON:
|
||||
ids = NULL;
|
||||
gtk_tree_selection_selected_foreach(sel, makeidlist, &ids);
|
||||
/* XXX should I assert(0 <= index) to insure a row was selected? */
|
||||
break;
|
||||
case FROM_POPUP:
|
||||
ids = g_object_get_data(G_OBJECT(widget), LIST_INDEX);
|
||||
ids = g_object_get_data(G_OBJECT(widget), LIST_IDS);
|
||||
break;
|
||||
default:
|
||||
assert(!"unknown action source");
|
||||
@@ -976,60 +983,71 @@ actionclick(GtkWidget *widget, gpointer gdata) {
|
||||
break;
|
||||
assert(actindex < ALEN(actionitems));
|
||||
|
||||
blocksigs();
|
||||
updatesave = FALSE;
|
||||
count = tr_torrentStat(data->tr, &sb);
|
||||
|
||||
for(ii = g_list_sort(ids, intrevcmp); NULL != ii; ii = ii->next) {
|
||||
index = GPOINTER_TO_INT(ii->data);
|
||||
if(index >= count) {
|
||||
assert(!"illegal torrent id");
|
||||
continue;
|
||||
}
|
||||
tor = ii->data;
|
||||
sb = tr_torrentStat(tor);
|
||||
|
||||
/* check if this action is valid for this torrent */
|
||||
if(actionitems[actindex].nomenu ||
|
||||
(actionitems[actindex].avail &&
|
||||
!(actionitems[actindex].avail & sb[index].status)))
|
||||
!(actionitems[actindex].avail & sb->status)))
|
||||
continue;
|
||||
|
||||
switch(act) {
|
||||
case ACT_START:
|
||||
tr_torrentStart(data->tr, index);
|
||||
tr_torrentStart(tor);
|
||||
updatesave = TRUE;
|
||||
break;
|
||||
case ACT_STOP:
|
||||
tr_torrentStop(data->tr, index);
|
||||
tr_torrentStop(tor);
|
||||
updatesave = TRUE;
|
||||
break;
|
||||
case ACT_DELETE:
|
||||
if(TR_TORRENT_NEEDS_STOP(sb[index].status))
|
||||
tr_torrentStop(data->tr, index);
|
||||
tr_torrentClose(data->tr, index);
|
||||
if(TR_STATUS_ACTIVE & sb->status)
|
||||
tr_torrentStop(tor);
|
||||
tr_torrentClose(data->tr, tor);
|
||||
findtorrent(data->model, tor, &iter);
|
||||
gtk_list_store_remove(GTK_LIST_STORE(data->model), &iter);
|
||||
updatesave = TRUE;
|
||||
/* XXX should only unselect deleted rows */
|
||||
gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(data->view));
|
||||
gtk_tree_selection_unselect_all(
|
||||
gtk_tree_view_get_selection(data->view));
|
||||
break;
|
||||
case ACT_INFO:
|
||||
makeinfowind(data->wind, data->tr, index);
|
||||
makeinfowind(data->wind, tor);
|
||||
break;
|
||||
default:
|
||||
assert(!"unknown type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(sb);
|
||||
|
||||
if(updatesave) {
|
||||
savetorrents(data->tr, data->wind, -1, NULL);
|
||||
savetorrents(data->tr, data->wind);
|
||||
updatemodel(data);
|
||||
}
|
||||
|
||||
unblocksigs();
|
||||
|
||||
if(FROM_BUTTON == from)
|
||||
g_list_free(ids);
|
||||
}
|
||||
|
||||
void
|
||||
findtorrent(GtkTreeModel *model, tr_torrent_t *tor, GtkTreeIter *iter) {
|
||||
gpointer ptr;
|
||||
|
||||
if(gtk_tree_model_get_iter_first(model, iter)) {
|
||||
do {
|
||||
gtk_tree_model_get(model, iter, MC_TORRENT, &ptr, -1);
|
||||
if(tor == ptr)
|
||||
return;
|
||||
} while(gtk_tree_model_iter_next(model, iter));
|
||||
}
|
||||
|
||||
assert(!"torrent not found");
|
||||
}
|
||||
|
||||
gint
|
||||
intrevcmp(gconstpointer a, gconstpointer b) {
|
||||
int aint = GPOINTER_TO_INT(a);
|
||||
@@ -1048,56 +1066,73 @@ doubleclick(GtkWidget *widget SHUTUP, GtkTreePath *path,
|
||||
GtkTreeViewColumn *col SHUTUP, gpointer gdata) {
|
||||
struct cbdata *data = gdata;
|
||||
GtkTreeIter iter;
|
||||
int index;
|
||||
tr_torrent_t *tor;
|
||||
|
||||
if(gtk_tree_model_get_iter(GTK_TREE_MODEL(data->model), &iter, path)) {
|
||||
gtk_tree_model_get(GTK_TREE_MODEL(data->model), &iter,
|
||||
MC_ROW_INDEX, &index, -1);
|
||||
makeinfowind(data->wind, data->tr, index);
|
||||
if(gtk_tree_model_get_iter(data->model, &iter, path)) {
|
||||
gtk_tree_model_get(data->model, &iter, MC_TORRENT, &tor, -1);
|
||||
makeinfowind(data->wind, tor);
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
addtorrent(tr_handle_t *tr, GtkWindow *parentwind, const char *torrent,
|
||||
const char *dir, gboolean paused, GList **errs) {
|
||||
addtorrent(void *vdata, const char *torrent, const char *dir, gboolean paused,
|
||||
GList **errs) {
|
||||
const struct { const int err; const char *msg; } errstrs[] = {
|
||||
{TR_EINVALID, N_("not a valid torrent file")},
|
||||
{TR_EDUPLICATE, N_("torrent is already open")},
|
||||
};
|
||||
struct cbdata *data = vdata;
|
||||
tr_torrent_t *new;
|
||||
char *wd;
|
||||
int err;
|
||||
unsigned int ii;
|
||||
GtkTreeIter iter;
|
||||
|
||||
if(NULL == dir && NULL != (dir = cf_getpref(PREF_DIR))) {
|
||||
if(!mkdir_p(dir, 0777)) {
|
||||
errmsg(parentwind, _("Failed to create the directory %s:\n%s"),
|
||||
errmsg(data->wind, _("Failed to create the directory %s:\n%s"),
|
||||
dir, strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
blocksigs();
|
||||
|
||||
if(0 != tr_torrentInit(tr, torrent)) {
|
||||
unblocksigs();
|
||||
/* XXX would be nice to have errno strings, are they printed to stdout? */
|
||||
if(NULL == errs)
|
||||
errmsg(parentwind, _("Failed to load the torrent file %s"), torrent);
|
||||
else
|
||||
*errs = g_list_append(*errs, g_strdup(torrent));
|
||||
if(NULL == (new = tr_torrentInit(data->tr, torrent, &err))) {
|
||||
for(ii = 0; ii < ALEN(errstrs); ii++)
|
||||
if(err == errstrs[ii].err)
|
||||
break;
|
||||
if(NULL == errs) {
|
||||
if(ii == ALEN(errstrs))
|
||||
errmsg(data->wind, _("Failed to load the torrent file %s"), torrent);
|
||||
else
|
||||
errmsg(data->wind, _("Failed to load the torrent file %s: %s"),
|
||||
torrent, gettext(errstrs[ii].msg));
|
||||
} else {
|
||||
if(ii == ALEN(errstrs))
|
||||
*errs = g_list_append(*errs, g_strdup(torrent));
|
||||
else
|
||||
*errs = g_list_append(*errs, g_strdup_printf(_("%s (%s)"),
|
||||
torrent, gettext(errstrs[ii].msg)));
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gtk_list_store_append(GTK_LIST_STORE(data->model), &iter);
|
||||
gtk_list_store_set(GTK_LIST_STORE(data->model), &iter, MC_TORRENT, new, -1);
|
||||
|
||||
if(NULL != dir)
|
||||
tr_torrentSetFolder(tr, tr_torrentCount(tr) - 1, dir);
|
||||
tr_torrentSetFolder(new, dir);
|
||||
else {
|
||||
wd = g_new(char, MAXPATHLEN + 1);
|
||||
if(NULL == getcwd(wd, MAXPATHLEN + 1))
|
||||
tr_torrentSetFolder(tr, tr_torrentCount(tr) - 1, ".");
|
||||
tr_torrentSetFolder(new, ".");
|
||||
else {
|
||||
tr_torrentSetFolder(tr, tr_torrentCount(tr) - 1, wd);
|
||||
tr_torrentSetFolder(new, wd);
|
||||
free(wd);
|
||||
}
|
||||
}
|
||||
|
||||
if(!paused)
|
||||
tr_torrentStart(tr, tr_torrentCount(tr) - 1);
|
||||
|
||||
unblocksigs();
|
||||
tr_torrentStart(new);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -1107,32 +1142,25 @@ addedtorrents(void *vdata) {
|
||||
struct cbdata *data = vdata;
|
||||
|
||||
updatemodel(data);
|
||||
savetorrents(data->tr, data->wind, -1, NULL);
|
||||
savetorrents(data->tr, data->wind);
|
||||
}
|
||||
|
||||
gboolean
|
||||
savetorrents(tr_handle_t *tr, GtkWindow *wind, int count, tr_stat_t *stat) {
|
||||
savetorrents(tr_handle_t *tr, GtkWindow *wind) {
|
||||
GList *torrents;
|
||||
char *errstr;
|
||||
tr_stat_t *st;
|
||||
gboolean ret;
|
||||
|
||||
assert(NULL != tr || 0 <= count);
|
||||
torrents = NULL;
|
||||
tr_torrentIterate(tr, maketorrentlist, &torrents);
|
||||
|
||||
if(0 <= count)
|
||||
ret = cf_savestate(count, stat, &errstr);
|
||||
else {
|
||||
blocksigs();
|
||||
count = tr_torrentStat(tr, &st);
|
||||
unblocksigs();
|
||||
ret = cf_savestate(count, st, &errstr);
|
||||
free(st);
|
||||
}
|
||||
|
||||
if(!ret) {
|
||||
if(!(ret = cf_savestate(torrents, &errstr))) {
|
||||
errmsg(wind, "%s", errstr);
|
||||
g_free(errstr);
|
||||
}
|
||||
|
||||
g_list_free(torrents);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1151,8 +1179,41 @@ void
|
||||
makeidlist(GtkTreeModel *model, GtkTreePath *path SHUTUP, GtkTreeIter *iter,
|
||||
gpointer gdata) {
|
||||
GList **ids = gdata;
|
||||
int index;
|
||||
gpointer ptr;
|
||||
|
||||
gtk_tree_model_get(model, iter, MC_ROW_INDEX, &index, -1);
|
||||
*ids = g_list_append(*ids, GINT_TO_POINTER(index));
|
||||
gtk_tree_model_get(model, iter, MC_TORRENT, &ptr, -1);
|
||||
*ids = g_list_append(*ids, ptr);
|
||||
}
|
||||
|
||||
void
|
||||
maketorrentlist(tr_torrent_t *tor, void *data) {
|
||||
GList **list = data;
|
||||
|
||||
*list = g_list_append(*list, tor);
|
||||
}
|
||||
|
||||
void
|
||||
setupsighandlers(void) {
|
||||
int sigs[] = {SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGUSR1, SIGUSR2};
|
||||
struct sigaction sa;
|
||||
unsigned int ii;
|
||||
|
||||
bzero(&sa, sizeof(sa));
|
||||
sa.sa_handler = fatalsig;
|
||||
for(ii = 0; ii < ALEN(sigs); ii++)
|
||||
sigaction(sigs[ii], &sa, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
fatalsig(int sig) {
|
||||
struct sigaction sa;
|
||||
|
||||
global_lastsig = sig;
|
||||
|
||||
if(SIGCOUNT_MAX <= ++global_sigcount) {
|
||||
bzero(&sa, sizeof(sa));
|
||||
sa.sa_handler = SIG_DFL;
|
||||
sigaction(sig, &sa, NULL);
|
||||
raise(sig);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2006-02-10 00:50-0500\n"
|
||||
"POT-Creation-Date: 2006-03-20 11:30-0800\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -43,7 +43,7 @@ msgid ""
|
||||
"%s"
|
||||
msgstr ""
|
||||
|
||||
#: conf.c:115 dialogs.c:191 main.c:1065
|
||||
#: conf.c:115 dialogs.c:190 main.c:1093
|
||||
#, c-format
|
||||
msgid ""
|
||||
"Failed to create the directory %s:\n"
|
||||
@@ -69,52 +69,52 @@ msgid ""
|
||||
"%s"
|
||||
msgstr ""
|
||||
|
||||
#: conf.c:233 conf.c:432
|
||||
#: conf.c:233 conf.c:434
|
||||
#, c-format
|
||||
msgid ""
|
||||
"Failed to open or lock the file %s:\n"
|
||||
"%s"
|
||||
msgstr ""
|
||||
|
||||
#: conf.c:253 conf.c:472
|
||||
#: conf.c:253 conf.c:476
|
||||
#, c-format
|
||||
msgid ""
|
||||
"Error while writing to the file %s:\n"
|
||||
"%s"
|
||||
msgstr ""
|
||||
|
||||
#: conf.c:260 conf.c:479
|
||||
#: conf.c:260 conf.c:483
|
||||
#, c-format
|
||||
msgid ""
|
||||
"Failed to rename the file %s to %s:\n"
|
||||
"%s"
|
||||
msgstr ""
|
||||
|
||||
#: dialogs.c:76
|
||||
#: dialogs.c:75
|
||||
#, c-format
|
||||
msgid "%s Preferences"
|
||||
msgstr ""
|
||||
|
||||
#: dialogs.c:84
|
||||
#: dialogs.c:83
|
||||
msgid "_Limit upload speed"
|
||||
msgstr ""
|
||||
|
||||
#: dialogs.c:87
|
||||
#: dialogs.c:86
|
||||
msgid "Choose download directory"
|
||||
msgstr ""
|
||||
|
||||
#. limit label and entry
|
||||
#: dialogs.c:121
|
||||
#: dialogs.c:120
|
||||
msgid "Maximum _upload speed:"
|
||||
msgstr ""
|
||||
|
||||
#. directory label and chooser
|
||||
#: dialogs.c:135
|
||||
#: dialogs.c:134
|
||||
msgid "_Download directory:"
|
||||
msgstr ""
|
||||
|
||||
#. port label and entry
|
||||
#: dialogs.c:144
|
||||
#: dialogs.c:143
|
||||
msgid "Listening _port:"
|
||||
msgstr ""
|
||||
|
||||
@@ -134,11 +134,11 @@ msgstr ""
|
||||
msgid "Choose a download directory"
|
||||
msgstr ""
|
||||
|
||||
#: dialogs.c:285
|
||||
#: dialogs.c:284
|
||||
msgid "Torrent files"
|
||||
msgstr ""
|
||||
|
||||
#: dialogs.c:287
|
||||
#: dialogs.c:286
|
||||
msgid "All files"
|
||||
msgstr ""
|
||||
|
||||
@@ -191,59 +191,59 @@ msgstr ""
|
||||
msgid "Uploaded:"
|
||||
msgstr ""
|
||||
|
||||
#: main.c:155
|
||||
#: main.c:164
|
||||
msgid "Add"
|
||||
msgstr ""
|
||||
|
||||
#: main.c:156
|
||||
#: main.c:165
|
||||
msgid "Add a new torrent"
|
||||
msgstr ""
|
||||
|
||||
#: main.c:157
|
||||
#: main.c:166
|
||||
msgid "Start"
|
||||
msgstr ""
|
||||
|
||||
#: main.c:159
|
||||
#: main.c:168
|
||||
msgid "Start a torrent that is not running"
|
||||
msgstr ""
|
||||
|
||||
#: main.c:160
|
||||
#: main.c:169
|
||||
msgid "Stop"
|
||||
msgstr ""
|
||||
|
||||
#: main.c:162
|
||||
#: main.c:171
|
||||
msgid "Stop a torrent that is running"
|
||||
msgstr ""
|
||||
|
||||
#: main.c:163
|
||||
#: main.c:172
|
||||
msgid "Remove"
|
||||
msgstr ""
|
||||
|
||||
#: main.c:164
|
||||
#: main.c:173
|
||||
msgid "Remove a torrent"
|
||||
msgstr ""
|
||||
|
||||
#: main.c:165
|
||||
#: main.c:174
|
||||
msgid "Properties"
|
||||
msgstr ""
|
||||
|
||||
#: main.c:166
|
||||
#: main.c:175
|
||||
msgid "Show additional information about a torrent"
|
||||
msgstr ""
|
||||
|
||||
#: main.c:167
|
||||
#: main.c:176
|
||||
msgid "Preferences"
|
||||
msgstr ""
|
||||
|
||||
#: main.c:168
|
||||
#: main.c:177
|
||||
msgid "Customize application behavior"
|
||||
msgstr ""
|
||||
|
||||
#: main.c:186
|
||||
#: main.c:203
|
||||
msgid "Transmission"
|
||||
msgstr ""
|
||||
|
||||
#: main.c:316 main.c:518 main.c:1077
|
||||
#: main.c:331 main.c:626 main.c:1105
|
||||
#, c-format
|
||||
msgid "Failed to load the torrent file %s"
|
||||
msgid_plural ""
|
||||
@@ -252,75 +252,98 @@ msgid_plural ""
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: main.c:606
|
||||
#: main.c:422
|
||||
msgid "Name"
|
||||
msgstr ""
|
||||
|
||||
#. this string is only used to determing the size of the progress bar
|
||||
#: main.c:613
|
||||
#: main.c:429
|
||||
msgid " fnord fnord "
|
||||
msgstr ""
|
||||
|
||||
#: main.c:616
|
||||
#: main.c:432
|
||||
msgid "Progress"
|
||||
msgstr ""
|
||||
|
||||
#: main.c:691
|
||||
#: main.c:703
|
||||
#, c-format
|
||||
msgid "Checking existing files (%.1f%%)"
|
||||
msgstr ""
|
||||
|
||||
#: main.c:693
|
||||
#: main.c:706
|
||||
#, c-format
|
||||
msgid "Finishing in --:--:-- (%.1f%%)"
|
||||
msgstr ""
|
||||
|
||||
#: main.c:708
|
||||
#, c-format
|
||||
msgid "Finishing in %02i:%02i:%02i (%.1f%%)"
|
||||
msgstr ""
|
||||
|
||||
#: main.c:696
|
||||
#: main.c:712
|
||||
#, c-format
|
||||
msgid "Seeding, uploading to %d of %d peer"
|
||||
msgid_plural "Seeding, uploading to %d of %d peers"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: main.c:700
|
||||
#: main.c:716
|
||||
msgid "Stopping..."
|
||||
msgstr ""
|
||||
|
||||
#: main.c:702
|
||||
#: main.c:718
|
||||
#, c-format
|
||||
msgid "Stopped (%.1f%%)"
|
||||
msgstr ""
|
||||
|
||||
#: main.c:710
|
||||
#: main.c:726
|
||||
msgid "Error: "
|
||||
msgstr ""
|
||||
|
||||
#: main.c:714
|
||||
#: main.c:730
|
||||
#, c-format
|
||||
msgid "Downloading from %i of %i peer"
|
||||
msgid_plural "Downloading from %i of %i peers"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: main.c:748
|
||||
#: main.c:763
|
||||
#, c-format
|
||||
msgid ""
|
||||
"Ratio: %s\n"
|
||||
"UL: %s/s"
|
||||
msgstr ""
|
||||
|
||||
#: main.c:751
|
||||
#: main.c:766
|
||||
#, c-format
|
||||
msgid ""
|
||||
"DL: %s/s\n"
|
||||
"UL: %s/s"
|
||||
msgstr ""
|
||||
|
||||
#: main.c:799
|
||||
#: main.c:812
|
||||
#, c-format
|
||||
msgid " Total DL: %s/s Total UL: %s/s"
|
||||
msgstr ""
|
||||
|
||||
#: main.c:1081
|
||||
msgid "not a valid torrent file"
|
||||
msgstr ""
|
||||
|
||||
#: main.c:1082
|
||||
msgid "torrent is already open"
|
||||
msgstr ""
|
||||
|
||||
#: main.c:1107
|
||||
#, c-format
|
||||
msgid "Failed to load the torrent file %s: %s"
|
||||
msgstr ""
|
||||
|
||||
#: main.c:1113
|
||||
#, c-format
|
||||
msgid "%s (%s)"
|
||||
msgstr ""
|
||||
|
||||
#: util.c:63
|
||||
msgid "B"
|
||||
msgstr ""
|
||||
@@ -349,11 +372,11 @@ msgstr ""
|
||||
msgid "EiB"
|
||||
msgstr ""
|
||||
|
||||
#: util.c:87
|
||||
#: util.c:88
|
||||
msgid "N/A"
|
||||
msgstr ""
|
||||
|
||||
#. this is a UTF-8 infinity symbol
|
||||
#: util.c:91
|
||||
#: util.c:92
|
||||
msgid "∞"
|
||||
msgstr ""
|
||||
|
||||
67
gtk/util.c
67
gtk/util.c
@@ -38,8 +38,6 @@
|
||||
|
||||
#define BESTDECIMAL(d) (10.0 > (d) ? 2 : (100.0 > (d) ? 1 : 0))
|
||||
|
||||
static void
|
||||
sigexithandler(int sig);
|
||||
static void
|
||||
errcb(GtkWidget *wind, int resp, gpointer data);
|
||||
|
||||
@@ -185,71 +183,6 @@ urldecode(const char *str, int len) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int exit_sigs[] = {SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGUSR1, SIGUSR2};
|
||||
static callbackfunc_t exit_func = NULL;
|
||||
static void *exit_data = NULL;
|
||||
static int exit_block_level = 0;
|
||||
|
||||
void
|
||||
setuphandlers(callbackfunc_t func, void *data) {
|
||||
struct sigaction sa;
|
||||
unsigned int ii;
|
||||
|
||||
exit_data = data;
|
||||
exit_func = func;
|
||||
|
||||
bzero(&sa, sizeof(sa));
|
||||
sa.sa_handler = sigexithandler;
|
||||
for(ii = 0; ii < ALEN(exit_sigs); ii++)
|
||||
sigaction(exit_sigs[ii], &sa, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
clearhandlers(void) {
|
||||
struct sigaction sa;
|
||||
unsigned int ii;
|
||||
|
||||
bzero(&sa, sizeof(sa));
|
||||
sa.sa_handler = SIG_DFL;
|
||||
for(ii = 0; ii < ALEN(exit_sigs); ii++)
|
||||
sigaction(exit_sigs[ii], &sa, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
sigexithandler(int sig) {
|
||||
exit_func(exit_data);
|
||||
clearhandlers();
|
||||
raise(sig);
|
||||
}
|
||||
|
||||
void
|
||||
blocksigs(void) {
|
||||
sigset_t mask;
|
||||
unsigned int ii;
|
||||
|
||||
if(0 < (exit_block_level++))
|
||||
return;
|
||||
|
||||
sigemptyset(&mask);
|
||||
for(ii = 0; ii < ALEN(exit_sigs); ii++)
|
||||
sigaddset(&mask, exit_sigs[ii]);
|
||||
sigprocmask(SIG_BLOCK, &mask, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
unblocksigs(void) {
|
||||
sigset_t mask;
|
||||
unsigned int ii;
|
||||
|
||||
if(0 < (--exit_block_level))
|
||||
return;
|
||||
|
||||
sigemptyset(&mask);
|
||||
for(ii = 0; ii < ALEN(exit_sigs); ii++)
|
||||
sigaddset(&mask, exit_sigs[ii]);
|
||||
sigprocmask(SIG_UNBLOCK, &mask, NULL);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
errmsg(GtkWindow *wind, const char *format, ...) {
|
||||
GtkWidget *dialog;
|
||||
|
||||
17
gtk/util.h
17
gtk/util.h
@@ -70,23 +70,6 @@ joinstrlist(GList *list, char *sep);
|
||||
char *
|
||||
urldecode(const char *str, int len);
|
||||
|
||||
/* set up a handler for various fatal signals */
|
||||
void
|
||||
setuphandlers(callbackfunc_t func, void *data);
|
||||
|
||||
/* clear the handlers for fatal signals */
|
||||
void
|
||||
clearhandlers(void);
|
||||
|
||||
/* blocks and unblocks delivery of fatal signals. calls to these
|
||||
functions can be nested as long as unblocksigs() is called exactly
|
||||
as many times as blocksigs(). only the first blocksigs() will
|
||||
block signals and only the last unblocksigs() will unblock them. */
|
||||
void
|
||||
blocksigs(void);
|
||||
void
|
||||
unblocksigs(void);
|
||||
|
||||
/* if wind is NULL then you must call gtk_widget_show on the returned widget */
|
||||
|
||||
GtkWidget *
|
||||
|
||||
@@ -138,7 +138,7 @@ static inline void sortPeers( tr_peer_t ** all, int allCount,
|
||||
|
||||
void tr_chokingPulse( tr_choking_t * c )
|
||||
{
|
||||
int i, peersTotalCount, unchoked, mustOptimistic = 1;
|
||||
int peersTotalCount, unchoked, mustOptimistic = 1;
|
||||
tr_peer_t ** canChoke, ** canUnchoke;
|
||||
tr_peer_t ** canChokeZero, ** canUnchokeZero;
|
||||
tr_peer_t ** canChokeNonZero, ** canUnchokeNonZero;
|
||||
@@ -152,9 +152,8 @@ void tr_chokingPulse( tr_choking_t * c )
|
||||
|
||||
/* Lock all torrents and get the total number of peers */
|
||||
peersTotalCount = 0;
|
||||
for( i = 0; i < c->h->torrentCount; i++ )
|
||||
for( tor = c->h->torrentList; tor; tor = tor->next )
|
||||
{
|
||||
tor = c->h->torrents[i];
|
||||
tr_lockLock( &tor->lock );
|
||||
peersTotalCount += tor->peerCount;
|
||||
}
|
||||
@@ -165,15 +164,14 @@ void tr_chokingPulse( tr_choking_t * c )
|
||||
canUnchokeCount = 0;
|
||||
unchoked = 0;
|
||||
|
||||
for( i = 0; i < c->h->torrentCount; i++ )
|
||||
for( tor = c->h->torrentList; tor; tor = tor->next )
|
||||
{
|
||||
tr_peer_t * peer;
|
||||
int j;
|
||||
int i;
|
||||
|
||||
tor = c->h->torrents[i];
|
||||
for( j = 0; j < tor->peerCount; j++ )
|
||||
for( i = 0; i < tor->peerCount; i++ )
|
||||
{
|
||||
peer = tor->peers[j];
|
||||
peer = tor->peers[i];
|
||||
|
||||
if( !tr_peerIsConnected( peer ) )
|
||||
continue;
|
||||
@@ -320,9 +318,9 @@ void tr_chokingPulse( tr_choking_t * c )
|
||||
free( canUnchokeNonZero );
|
||||
|
||||
/* Unlock all torrents */
|
||||
for( i = 0; i < c->h->torrentCount; i++ )
|
||||
for( tor = c->h->torrentList; tor; tor = tor->next )
|
||||
{
|
||||
tr_lockUnlock( &c->h->torrents[i]->lock );
|
||||
tr_lockUnlock( &tor->lock );
|
||||
}
|
||||
|
||||
tr_lockUnlock( &c->lock );
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2005 Eric Petit
|
||||
* Copyright (c) 2005-2006 Transmission authors and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
@@ -82,7 +82,6 @@
|
||||
|
||||
#define TR_MAX_PEER_COUNT 60
|
||||
|
||||
typedef struct tr_torrent_s tr_torrent_t;
|
||||
typedef struct tr_completion_s tr_completion_t;
|
||||
|
||||
#include "platform.h"
|
||||
@@ -108,11 +107,13 @@ struct tr_torrent_s
|
||||
tr_fd_t * fdlimit;
|
||||
|
||||
int status;
|
||||
int finished;
|
||||
char error[128];
|
||||
int error;
|
||||
char trackerError[128];
|
||||
int finished;
|
||||
|
||||
char * id;
|
||||
char * key;
|
||||
int * bindPort;
|
||||
|
||||
/* An escaped string used to include the hash in HTTP queries */
|
||||
char hashString[3*SHA_DIGEST_LENGTH+1];
|
||||
@@ -126,14 +127,6 @@ struct tr_torrent_s
|
||||
int blockSize;
|
||||
int blockCount;
|
||||
|
||||
#if 0
|
||||
/* Status for each block
|
||||
-1 = we have it
|
||||
n = we are downloading it from n peers */
|
||||
char * blockHave;
|
||||
int blockHaveCount;
|
||||
uint8_t * bitfield;
|
||||
#endif
|
||||
tr_completion_t * completion;
|
||||
|
||||
volatile char die;
|
||||
@@ -150,6 +143,12 @@ struct tr_torrent_s
|
||||
uint64_t date;
|
||||
uint64_t downloaded;
|
||||
uint64_t uploaded;
|
||||
|
||||
tr_stat_t stats[2];
|
||||
int statCur;
|
||||
|
||||
tr_torrent_t * prev;
|
||||
tr_torrent_t * next;
|
||||
};
|
||||
|
||||
#include "utils.h"
|
||||
@@ -158,7 +157,7 @@ struct tr_torrent_s
|
||||
struct tr_handle_s
|
||||
{
|
||||
int torrentCount;
|
||||
tr_torrent_t * torrents[TR_MAX_TORRENT_COUNT];
|
||||
tr_torrent_t * torrentList;
|
||||
|
||||
tr_ratecontrol_t * upload;
|
||||
tr_ratecontrol_t * download;
|
||||
|
||||
@@ -57,13 +57,13 @@ struct tr_tracker_s
|
||||
static void sendQuery ( tr_tracker_t * tc );
|
||||
static void recvAnswer ( tr_tracker_t * tc );
|
||||
|
||||
tr_tracker_t * tr_trackerInit( tr_handle_t * h, tr_torrent_t * tor )
|
||||
tr_tracker_t * tr_trackerInit( tr_torrent_t * tor )
|
||||
{
|
||||
tr_tracker_t * tc;
|
||||
|
||||
tc = calloc( 1, sizeof( tr_tracker_t ) );
|
||||
tc->tor = tor;
|
||||
tc->id = h->id;
|
||||
tc->id = tor->id;
|
||||
|
||||
tc->started = 1;
|
||||
|
||||
@@ -74,7 +74,7 @@ tr_tracker_t * tr_trackerInit( tr_handle_t * h, tr_torrent_t * tor )
|
||||
tc->size = 1024;
|
||||
tc->buf = malloc( tc->size );
|
||||
|
||||
tc->bindPort = h->bindPort;
|
||||
tc->bindPort = *(tor->bindPort);
|
||||
tc->newPort = -1;
|
||||
|
||||
return tc;
|
||||
@@ -374,13 +374,12 @@ static void recvAnswer( tr_tracker_t * tc )
|
||||
if( ( bePeers = tr_bencDictFind( &beAll, "failure reason" ) ) )
|
||||
{
|
||||
tr_err( "Tracker: %s", bePeers->val.s.s );
|
||||
tor->status |= TR_TRACKER_ERROR;
|
||||
snprintf( tor->error, sizeof( tor->error ),
|
||||
tor->error |= TR_ETRACKER;
|
||||
snprintf( tor->trackerError, sizeof( tor->trackerError ),
|
||||
"%s", bePeers->val.s.s );
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
tor->status &= ~TR_TRACKER_ERROR;
|
||||
tor->error &= ~TR_ETRACKER;
|
||||
|
||||
if( !tc->interval )
|
||||
{
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
typedef struct tr_tracker_s tr_tracker_t;
|
||||
|
||||
tr_tracker_t * tr_trackerInit ( tr_handle_t *, tr_torrent_t * );
|
||||
tr_tracker_t * tr_trackerInit ( tr_torrent_t * );
|
||||
void tr_trackerChangePort( tr_tracker_t *, int );
|
||||
int tr_trackerPulse ( tr_tracker_t * );
|
||||
void tr_trackerCompleted ( tr_tracker_t * );
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2005 Eric Petit
|
||||
* Copyright (c) 2005-2006 Transmission authors and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
@@ -25,7 +25,7 @@
|
||||
/***********************************************************************
|
||||
* Local prototypes
|
||||
**********************************************************************/
|
||||
static void torrentReallyStop( tr_handle_t * h, int t );
|
||||
static void torrentReallyStop( tr_torrent_t * );
|
||||
static void downloadLoop( void * );
|
||||
static void acceptLoop( void * );
|
||||
static void acceptStop( tr_handle_t * h );
|
||||
@@ -85,7 +85,8 @@ tr_handle_t * tr_init()
|
||||
**********************************************************************/
|
||||
void tr_setBindPort( tr_handle_t * h, int port )
|
||||
{
|
||||
int ii, sock;
|
||||
int sock;
|
||||
tr_torrent_t * tor;
|
||||
|
||||
if( h->bindPort == port )
|
||||
return;
|
||||
@@ -106,14 +107,14 @@ void tr_setBindPort( tr_handle_t * h, int port )
|
||||
|
||||
h->bindPort = port;
|
||||
|
||||
for( ii = 0; ii < h->torrentCount; ii++ )
|
||||
for( tor = h->torrentList; tor; tor = tor->next )
|
||||
{
|
||||
tr_lockLock( &h->torrents[ii]->lock );
|
||||
if( NULL != h->torrents[ii]->tracker )
|
||||
tr_lockLock( &tor->lock );
|
||||
if( NULL != tor->tracker )
|
||||
{
|
||||
tr_trackerChangePort( h->torrents[ii]->tracker, port );
|
||||
tr_trackerChangePort( tor->tracker, port );
|
||||
}
|
||||
tr_lockUnlock( &h->torrents[ii]->lock );
|
||||
tr_lockUnlock( &tor->lock );
|
||||
}
|
||||
|
||||
if( h->bindSocket > -1 )
|
||||
@@ -146,13 +147,11 @@ void tr_setUploadLimit( tr_handle_t * h, int limit )
|
||||
void tr_torrentRates( tr_handle_t * h, float * dl, float * ul )
|
||||
{
|
||||
tr_torrent_t * tor;
|
||||
int i;
|
||||
|
||||
*dl = 0.0;
|
||||
*ul = 0.0;
|
||||
for( i = 0; i < h->torrentCount; i++ )
|
||||
for( tor = h->torrentList; tor; tor = tor->next )
|
||||
{
|
||||
tor = h->torrents[i];
|
||||
tr_lockLock( &tor->lock );
|
||||
if( tor->status & TR_STATUS_DOWNLOAD )
|
||||
*dl += tr_rcRate( tor->download );
|
||||
@@ -167,44 +166,41 @@ void tr_torrentRates( tr_handle_t * h, float * dl, float * ul )
|
||||
* Allocates a tr_torrent_t structure, then relies on tr_metainfoParse
|
||||
* to fill it.
|
||||
**********************************************************************/
|
||||
int tr_torrentInit( tr_handle_t * h, const char * path )
|
||||
tr_torrent_t * tr_torrentInit( tr_handle_t * h, const char * path,
|
||||
int * error )
|
||||
{
|
||||
tr_torrent_t * tor;
|
||||
tr_torrent_t * tor, * tor_tmp;
|
||||
tr_info_t * inf;
|
||||
int i;
|
||||
char * s1, * s2;
|
||||
|
||||
if( h->torrentCount >= TR_MAX_TORRENT_COUNT )
|
||||
{
|
||||
tr_err( "Maximum number of torrents reached" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
tor = calloc( sizeof( tr_torrent_t ), 1 );
|
||||
inf = &tor->info;
|
||||
|
||||
/* Parse torrent file */
|
||||
if( tr_metainfoParse( inf, path ) )
|
||||
{
|
||||
*error = TR_EINVALID;
|
||||
free( tor );
|
||||
return 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Make sure this torrent is not already open */
|
||||
for( i = 0; i < h->torrentCount; i++ )
|
||||
for( tor_tmp = h->torrentList; tor_tmp; tor_tmp = tor_tmp->next )
|
||||
{
|
||||
if( !memcmp( tor->info.hash, h->torrents[i]->info.hash,
|
||||
if( !memcmp( tor->info.hash, tor_tmp->info.hash,
|
||||
SHA_DIGEST_LENGTH ) )
|
||||
{
|
||||
tr_err( "Torrent already open" );
|
||||
*error = TR_EDUPLICATE;
|
||||
free( tor );
|
||||
return 1;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
tor->status = TR_STATUS_PAUSE;
|
||||
tor->id = h->id;
|
||||
tor->key = h->key;
|
||||
tor->bindPort = &h->bindPort;
|
||||
tor->finished = 0;
|
||||
|
||||
|
||||
@@ -246,65 +242,60 @@ int tr_torrentInit( tr_handle_t * h, const char * path )
|
||||
|
||||
/* We have a new torrent */
|
||||
tr_lockLock( &h->acceptLock );
|
||||
h->torrents[h->torrentCount] = tor;
|
||||
tor->prev = NULL;
|
||||
tor->next = h->torrentList;
|
||||
if( tor->next )
|
||||
{
|
||||
tor->next->prev = tor;
|
||||
}
|
||||
h->torrentList = tor;
|
||||
(h->torrentCount)++;
|
||||
tr_lockUnlock( &h->acceptLock );
|
||||
|
||||
return 0;
|
||||
return tor;
|
||||
}
|
||||
|
||||
tr_info_t * tr_torrentInfo( tr_torrent_t * tor )
|
||||
{
|
||||
return &tor->info;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_torrentScrape
|
||||
***********************************************************************
|
||||
* Allocates a tr_torrent_t structure, then relies on tr_metainfoParse
|
||||
* to fill it.
|
||||
**********************************************************************/
|
||||
int tr_torrentScrape( tr_handle_t * h, int t, int * s, int * l )
|
||||
int tr_torrentScrape( tr_torrent_t * tor, int * s, int * l )
|
||||
{
|
||||
return tr_trackerScrape( h->torrents[t], s, l );
|
||||
return tr_trackerScrape( tor, s, l );
|
||||
}
|
||||
|
||||
void tr_torrentSetFolder( tr_handle_t * h, int t, const char * path )
|
||||
void tr_torrentSetFolder( tr_torrent_t * tor, const char * path )
|
||||
{
|
||||
tr_torrent_t * tor = h->torrents[t];
|
||||
|
||||
tor->destination = strdup( path );
|
||||
}
|
||||
|
||||
char * tr_torrentGetFolder( tr_handle_t * h, int t )
|
||||
char * tr_torrentGetFolder( tr_torrent_t * tor )
|
||||
{
|
||||
tr_torrent_t * tor = h->torrents[t];
|
||||
|
||||
return tor->destination;
|
||||
}
|
||||
|
||||
void tr_torrentStart( tr_handle_t * h, int t )
|
||||
void tr_torrentStart( tr_torrent_t * tor )
|
||||
{
|
||||
tr_torrent_t * tor = h->torrents[t];
|
||||
|
||||
if( tor->status & ( TR_STATUS_STOPPING | TR_STATUS_STOPPED ) )
|
||||
{
|
||||
/* Join the thread first */
|
||||
torrentReallyStop( h, t );
|
||||
torrentReallyStop( tor );
|
||||
}
|
||||
|
||||
tor->status = TR_STATUS_CHECK;
|
||||
tor->tracker = tr_trackerInit( h, tor );
|
||||
|
||||
if( 0 > h->bindPort )
|
||||
{
|
||||
tr_setBindPort( h, TR_DEFAULT_PORT );
|
||||
}
|
||||
tor->status = TR_STATUS_CHECK;
|
||||
tor->tracker = tr_trackerInit( tor );
|
||||
|
||||
tor->date = tr_date();
|
||||
tor->die = 0;
|
||||
tr_threadCreate( &tor->thread, downloadLoop, tor );
|
||||
}
|
||||
|
||||
void tr_torrentStop( tr_handle_t * h, int t )
|
||||
void tr_torrentStop( tr_torrent_t * tor )
|
||||
{
|
||||
tr_torrent_t * tor = h->torrents[t];
|
||||
|
||||
tr_lockLock( &tor->lock );
|
||||
tr_trackerStopped( tor->tracker );
|
||||
tr_rcReset( tor->download );
|
||||
@@ -319,10 +310,8 @@ void tr_torrentStop( tr_handle_t * h, int t )
|
||||
***********************************************************************
|
||||
* Joins the download thread and frees/closes everything related to it.
|
||||
**********************************************************************/
|
||||
static void torrentReallyStop( tr_handle_t * h, int t )
|
||||
static void torrentReallyStop( tr_torrent_t * tor )
|
||||
{
|
||||
tr_torrent_t * tor = h->torrents[t];
|
||||
|
||||
tor->die = 1;
|
||||
tr_threadJoin( &tor->thread );
|
||||
tr_dbg( "Thread joined" );
|
||||
@@ -345,129 +334,131 @@ int tr_torrentCount( tr_handle_t * h )
|
||||
return h->torrentCount;
|
||||
}
|
||||
|
||||
int tr_getFinished( tr_handle_t * h, int i)
|
||||
void tr_torrentIterate( tr_handle_t * h, tr_callback_t func, void * d )
|
||||
{
|
||||
return h->torrents[i]->finished;
|
||||
}
|
||||
void tr_setFinished( tr_handle_t * h, int i, int val)
|
||||
{
|
||||
h->torrents[i]->finished = val;
|
||||
tr_torrent_t * tor;
|
||||
|
||||
for( tor = h->torrentList; tor; tor = tor->next )
|
||||
{
|
||||
func( tor, d );
|
||||
}
|
||||
}
|
||||
|
||||
int tr_torrentStat( tr_handle_t * h, tr_stat_t ** stat )
|
||||
int tr_getFinished( tr_torrent_t * tor )
|
||||
{
|
||||
if( tor->finished )
|
||||
{
|
||||
tor->finished = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
tr_stat_t * tr_torrentStat( tr_torrent_t * tor )
|
||||
{
|
||||
tr_stat_t * s;
|
||||
tr_torrent_t * tor;
|
||||
tr_info_t * inf;
|
||||
int i, j, k, piece;
|
||||
tr_info_t * inf = &tor->info;
|
||||
int i;
|
||||
|
||||
if( h->torrentCount < 1 )
|
||||
tor->statCur = ( tor->statCur + 1 ) % 2;
|
||||
s = &tor->stats[tor->statCur];
|
||||
|
||||
if( ( tor->status & TR_STATUS_STOPPED ) ||
|
||||
( ( tor->status & TR_STATUS_STOPPING ) &&
|
||||
tr_date() > tor->stopDate + 60000 ) )
|
||||
{
|
||||
*stat = NULL;
|
||||
return 0;
|
||||
torrentReallyStop( tor );
|
||||
tor->status = TR_STATUS_PAUSE;
|
||||
}
|
||||
|
||||
s = malloc( h->torrentCount * sizeof( tr_stat_t ) );
|
||||
tr_lockLock( &tor->lock );
|
||||
|
||||
for( i = 0; i < h->torrentCount; i++ )
|
||||
s->status = tor->status;
|
||||
memcpy( s->trackerError, tor->trackerError,
|
||||
sizeof( s->trackerError ) );
|
||||
|
||||
s->peersTotal = 0;
|
||||
s->peersUploading = 0;
|
||||
s->peersDownloading = 0;
|
||||
|
||||
for( i = 0; i < tor->peerCount; i++ )
|
||||
{
|
||||
tor = h->torrents[i];
|
||||
inf = &tor->info;
|
||||
|
||||
if( ( tor->status & TR_STATUS_STOPPED ) ||
|
||||
( ( tor->status & TR_STATUS_STOPPING ) &&
|
||||
tr_date() > tor->stopDate + 60000 ) )
|
||||
if( tr_peerIsConnected( tor->peers[i] ) )
|
||||
{
|
||||
torrentReallyStop( h, i );
|
||||
tor->status = TR_STATUS_PAUSE;
|
||||
(s->peersTotal)++;
|
||||
if( tr_peerIsUploading( tor->peers[i] ) )
|
||||
{
|
||||
(s->peersUploading)++;
|
||||
}
|
||||
if( tr_peerIsDownloading( tor->peers[i] ) )
|
||||
{
|
||||
(s->peersDownloading)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s->progress = tr_cpCompletionAsFloat( tor->completion );
|
||||
if( tor->status & TR_STATUS_DOWNLOAD )
|
||||
s->rateDownload = tr_rcRate( tor->download );
|
||||
else
|
||||
/* tr_rcRate() doesn't make the difference between 'piece'
|
||||
messages and other messages, which causes a non-zero
|
||||
download rate even tough we are not downloading. So we
|
||||
force it to zero not to confuse the user. */
|
||||
s->rateDownload = 0.0;
|
||||
s->rateUpload = tr_rcRate( tor->upload );
|
||||
|
||||
s->seeders = tr_trackerSeeders(tor);
|
||||
s->leechers = tr_trackerLeechers(tor);
|
||||
|
||||
if( s->rateDownload < 0.1 )
|
||||
{
|
||||
s->eta = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
s->eta = (float) ( 1.0 - s->progress ) *
|
||||
(float) inf->totalSize / s->rateDownload / 1024.0;
|
||||
if( s->eta > 99 * 3600 + 59 * 60 + 59 )
|
||||
{
|
||||
s->eta = -1;
|
||||
}
|
||||
}
|
||||
|
||||
s->downloaded = tor->downloaded;
|
||||
s->uploaded = tor->uploaded;
|
||||
|
||||
tr_lockUnlock( &tor->lock );
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void tr_torrentAvailability( tr_torrent_t * tor, int8_t * tab, int size )
|
||||
{
|
||||
int i, j, piece;
|
||||
|
||||
tr_lockLock( &tor->lock );
|
||||
for( i = 0; i < size; i++ )
|
||||
{
|
||||
piece = i * tor->info.pieceCount / size;
|
||||
|
||||
if( tr_cpPieceIsComplete( tor->completion, piece ) )
|
||||
{
|
||||
tab[i] = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
tr_lockLock( &tor->lock );
|
||||
|
||||
memcpy( &s[i].info, &tor->info, sizeof( tr_info_t ) );
|
||||
s[i].status = tor->status;
|
||||
memcpy( s[i].error, tor->error, sizeof( s[i].error ) );
|
||||
|
||||
s[i].peersTotal = 0;
|
||||
s[i].peersUploading = 0;
|
||||
s[i].peersDownloading = 0;
|
||||
|
||||
tab[i] = 0;
|
||||
for( j = 0; j < tor->peerCount; j++ )
|
||||
{
|
||||
if( tr_peerIsConnected( tor->peers[j] ) )
|
||||
if( tr_peerBitfield( tor->peers[j] ) &&
|
||||
tr_bitfieldHas( tr_peerBitfield( tor->peers[j] ), piece ) )
|
||||
{
|
||||
(s[i].peersTotal)++;
|
||||
if( tr_peerIsUploading( tor->peers[j] ) )
|
||||
{
|
||||
(s[i].peersUploading)++;
|
||||
}
|
||||
if( tr_peerIsDownloading( tor->peers[j] ) )
|
||||
{
|
||||
(s[i].peersDownloading)++;
|
||||
}
|
||||
(tab[i])++;
|
||||
}
|
||||
}
|
||||
|
||||
s[i].progress = tr_cpCompletionAsFloat( tor->completion );
|
||||
if( tor->status & TR_STATUS_DOWNLOAD )
|
||||
s[i].rateDownload = tr_rcRate( tor->download );
|
||||
else
|
||||
/* tr_rcRate() doesn't make the difference between 'piece'
|
||||
messages and other messages, which causes a non-zero
|
||||
download rate even tough we are not downloading. So we
|
||||
force it to zero not to confuse the user. */
|
||||
s[i].rateDownload = 0.0;
|
||||
s[i].rateUpload = tr_rcRate( tor->upload );
|
||||
|
||||
s[i].seeders = tr_trackerSeeders(tor);
|
||||
s[i].leechers = tr_trackerLeechers(tor);
|
||||
|
||||
if( s[i].rateDownload < 0.1 )
|
||||
{
|
||||
s[i].eta = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
s[i].eta = (float) ( 1.0 - s[i].progress ) *
|
||||
(float) inf->totalSize / s[i].rateDownload / 1024.0;
|
||||
if( s[i].eta > 99 * 3600 + 59 * 60 + 59 )
|
||||
{
|
||||
s[i].eta = -1;
|
||||
}
|
||||
}
|
||||
|
||||
for( j = 0; j < 120; j++ )
|
||||
{
|
||||
piece = j * inf->pieceCount / 120;
|
||||
|
||||
if( tr_cpPieceIsComplete( tor->completion, piece ) )
|
||||
{
|
||||
s[i].pieces[j] = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
s[i].pieces[j] = 0;
|
||||
|
||||
for( k = 0; k < tor->peerCount; k++ )
|
||||
{
|
||||
if( tr_peerBitfield( tor->peers[k] ) &&
|
||||
tr_bitfieldHas( tr_peerBitfield( tor->peers[k] ), piece ) )
|
||||
{
|
||||
(s[i].pieces[j])++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s[i].downloaded = tor->downloaded;
|
||||
s[i].uploaded = tor->uploaded;
|
||||
|
||||
s[i].folder = tor->destination;
|
||||
|
||||
tr_lockUnlock( &tor->lock );
|
||||
}
|
||||
|
||||
*stat = s;
|
||||
return h->torrentCount;
|
||||
tr_lockUnlock( &tor->lock );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
@@ -475,15 +466,14 @@ int tr_torrentStat( tr_handle_t * h, tr_stat_t ** stat )
|
||||
***********************************************************************
|
||||
* Frees memory allocated by tr_torrentInit.
|
||||
**********************************************************************/
|
||||
void tr_torrentClose( tr_handle_t * h, int t )
|
||||
void tr_torrentClose( tr_handle_t * h, tr_torrent_t * tor )
|
||||
{
|
||||
tr_torrent_t * tor = h->torrents[t];
|
||||
tr_info_t * inf = &tor->info;
|
||||
tr_info_t * inf = &tor->info;
|
||||
|
||||
if( tor->status & ( TR_STATUS_STOPPING | TR_STATUS_STOPPED ) )
|
||||
{
|
||||
/* Join the thread first */
|
||||
torrentReallyStop( h, t );
|
||||
torrentReallyStop( tor );
|
||||
}
|
||||
|
||||
tr_lockLock( &h->acceptLock );
|
||||
@@ -502,10 +492,20 @@ void tr_torrentClose( tr_handle_t * h, int t )
|
||||
}
|
||||
free( inf->pieces );
|
||||
free( inf->files );
|
||||
free( tor );
|
||||
|
||||
memmove( &h->torrents[t], &h->torrents[t+1],
|
||||
( h->torrentCount - t ) * sizeof( void * ) );
|
||||
if( tor->prev )
|
||||
{
|
||||
tor->prev->next = tor->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
h->torrentList = tor->next;
|
||||
}
|
||||
if( tor->next )
|
||||
{
|
||||
tor->next->prev = tor->prev;
|
||||
}
|
||||
free( tor );
|
||||
|
||||
tr_lockUnlock( &h->acceptLock );
|
||||
}
|
||||
@@ -595,8 +595,9 @@ static void acceptLoop( void * _h )
|
||||
{
|
||||
tr_handle_t * h = _h;
|
||||
uint64_t date1, date2, lastchoke = 0;
|
||||
int ii, jj;
|
||||
int ii;
|
||||
uint8_t * hash;
|
||||
tr_torrent_t * tor;
|
||||
|
||||
tr_dbg( "Accept thread started" );
|
||||
|
||||
@@ -640,17 +641,17 @@ static void acceptLoop( void * _h )
|
||||
}
|
||||
if( NULL != ( hash = tr_peerHash( h->acceptPeers[ii] ) ) )
|
||||
{
|
||||
for( jj = 0; jj < h->torrentCount; jj++ )
|
||||
for( tor = h->torrentList; tor; tor = tor->next )
|
||||
{
|
||||
tr_lockLock( &h->torrents[jj]->lock );
|
||||
if( 0 == memcmp( h->torrents[jj]->info.hash, hash,
|
||||
tr_lockLock( &tor->lock );
|
||||
if( 0 == memcmp( tor->info.hash, hash,
|
||||
SHA_DIGEST_LENGTH ) )
|
||||
{
|
||||
tr_peerAttach( h->torrents[jj], h->acceptPeers[ii] );
|
||||
tr_lockUnlock( &h->torrents[jj]->lock );
|
||||
tr_peerAttach( tor, h->acceptPeers[ii] );
|
||||
tr_lockUnlock( &tor->lock );
|
||||
goto removePeer;
|
||||
}
|
||||
tr_lockUnlock( &h->torrents[jj]->lock );
|
||||
tr_lockUnlock( &tor->lock );
|
||||
}
|
||||
tr_peerDestroy( h->fdlimit, h->acceptPeers[ii] );
|
||||
goto removePeer;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2005 Eric Petit
|
||||
* Copyright (c) 2005-2006 Transmission authors and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
@@ -29,16 +29,16 @@ extern "C" {
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#define SHA_DIGEST_LENGTH 20
|
||||
#define SHA_DIGEST_LENGTH 20
|
||||
#ifdef __BEOS__
|
||||
# include <StorageDefs.h>
|
||||
# define MAX_PATH_LENGTH B_FILE_NAME_LENGTH
|
||||
# define MAX_PATH_LENGTH B_FILE_NAME_LENGTH
|
||||
#else
|
||||
# define MAX_PATH_LENGTH 1024
|
||||
# define MAX_PATH_LENGTH 1024
|
||||
#endif
|
||||
#define TR_MAX_TORRENT_COUNT 50
|
||||
|
||||
#define TR_DEFAULT_PORT 9090
|
||||
#define TR_DEFAULT_PORT 9090
|
||||
#define TR_NOERROR 0
|
||||
|
||||
/***********************************************************************
|
||||
* tr_init
|
||||
@@ -47,7 +47,6 @@ extern "C" {
|
||||
* be passed to all functions below.
|
||||
**********************************************************************/
|
||||
typedef struct tr_handle_s tr_handle_t;
|
||||
|
||||
tr_handle_t * tr_init();
|
||||
|
||||
/***********************************************************************
|
||||
@@ -74,6 +73,22 @@ void tr_setBindPort( tr_handle_t *, int );
|
||||
**********************************************************************/
|
||||
void tr_setUploadLimit( tr_handle_t *, int );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_torrentCount
|
||||
***********************************************************************
|
||||
* Returns the count of open torrents
|
||||
**********************************************************************/
|
||||
int tr_torrentCount( tr_handle_t * h );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_torrentIterate
|
||||
***********************************************************************
|
||||
* Iterates on open torrents
|
||||
**********************************************************************/
|
||||
typedef struct tr_torrent_s tr_torrent_t;
|
||||
typedef void (*tr_callback_t) ( tr_torrent_t *, void * );
|
||||
void tr_torrentIterate( tr_handle_t *, tr_callback_t, void * );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_torrentRates
|
||||
***********************************************************************
|
||||
@@ -82,27 +97,29 @@ void tr_setUploadLimit( tr_handle_t *, int );
|
||||
void tr_torrentRates( tr_handle_t *, float *, float * );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_getFinished
|
||||
* tr_close
|
||||
***********************************************************************
|
||||
* Tests to see if torrent is finished
|
||||
* Frees memory allocated by tr_init.
|
||||
**********************************************************************/
|
||||
int tr_getFinished( tr_handle_t *, int );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_setFinished
|
||||
***********************************************************************
|
||||
* Sets the boolean value finished in the torrent back to false
|
||||
**********************************************************************/
|
||||
void tr_setFinished( tr_handle_t *, int, int );
|
||||
void tr_close( tr_handle_t * );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_torrentInit
|
||||
***********************************************************************
|
||||
* Opens and parses torrent file at 'path'. If the file exists and is a
|
||||
* valid torrent file, returns 0 and adds it to the list of torrents
|
||||
* (but doesn't start it). Returns a non-zero value otherwise.
|
||||
* valid torrent file, returns an handle and adds it to the list of
|
||||
* torrents (but doesn't start it). Returns NULL and sets *error
|
||||
* otherwise.
|
||||
**********************************************************************/
|
||||
int tr_torrentInit( tr_handle_t *, const char * path );
|
||||
#define TR_EINVALID 1
|
||||
#define TR_EUNSUPPORTED 2
|
||||
#define TR_EDUPLICATE 3
|
||||
#define TR_EOTHER 666
|
||||
tr_torrent_t * tr_torrentInit( tr_handle_t *, const char * path,
|
||||
int * error );
|
||||
|
||||
typedef struct tr_info_s tr_info_t;
|
||||
tr_info_t * tr_torrentInfo( tr_torrent_t * );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_torrentScrape
|
||||
@@ -113,7 +130,7 @@ int tr_torrentInit( tr_handle_t *, const char * path );
|
||||
* replied with some error. tr_torrentScrape may block up to 20 seconds
|
||||
* before returning.
|
||||
**********************************************************************/
|
||||
int tr_torrentScrape( tr_handle_t *, int, int * s, int * l );
|
||||
int tr_torrentScrape( tr_torrent_t *, int * s, int * l );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_torrentStart
|
||||
@@ -121,9 +138,9 @@ int tr_torrentScrape( tr_handle_t *, int, int * s, int * l );
|
||||
* Starts downloading. The download is launched in a seperate thread,
|
||||
* therefore tr_torrentStart returns immediately.
|
||||
**********************************************************************/
|
||||
void tr_torrentSetFolder( tr_handle_t *, int, const char * );
|
||||
char * tr_torrentGetFolder( tr_handle_t *, int );
|
||||
void tr_torrentStart( tr_handle_t *, int );
|
||||
void tr_torrentSetFolder( tr_torrent_t *, const char * );
|
||||
char * tr_torrentGetFolder( tr_torrent_t * );
|
||||
void tr_torrentStart( tr_torrent_t * );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_torrentStop
|
||||
@@ -136,24 +153,38 @@ void tr_torrentStart( tr_handle_t *, int );
|
||||
* - by tr_torrentClose if you choose to remove the torrent without
|
||||
* waiting any further.
|
||||
**********************************************************************/
|
||||
void tr_torrentStop( tr_handle_t *, int );
|
||||
void tr_torrentStop( tr_torrent_t * );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_getFinished
|
||||
***********************************************************************
|
||||
* The first call after a torrent is completed returns 1. Returns 0
|
||||
* in other cases.
|
||||
**********************************************************************/
|
||||
int tr_getFinished( tr_torrent_t * );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_torrentStat
|
||||
***********************************************************************
|
||||
* Allocates an array of tr_stat_t structures, containing information
|
||||
* about the current status of all open torrents (see the contents
|
||||
* of tr_stat_s below). Returns the count of open torrents and sets
|
||||
* (*s) to the address of the array, or NULL if no torrent is open.
|
||||
* In the former case, the array belongs to the caller who is
|
||||
* responsible of freeing it.
|
||||
* The interface should call this function every 0.5 second or so in
|
||||
* order to update itself.
|
||||
* Returns a pointer to an tr_stat_t structure with updated information
|
||||
* on the torrent. The structure belongs to libtransmission (do not
|
||||
* free it) and is guaranteed to be unchanged until the next call to
|
||||
* tr_torrentStat.
|
||||
* The interface should call this function every second or so in order
|
||||
* to update itself.
|
||||
**********************************************************************/
|
||||
typedef struct tr_stat_s tr_stat_t;
|
||||
tr_stat_t * tr_torrentStat( tr_torrent_t * );
|
||||
|
||||
int tr_torrentCount( tr_handle_t * h );
|
||||
int tr_torrentStat( tr_handle_t *, tr_stat_t ** s );
|
||||
/***********************************************************************
|
||||
* tr_torrentAvailability
|
||||
***********************************************************************
|
||||
* Use this to draw an advanced progress bar which is 'size' pixels
|
||||
* wide. Fills 'tab' which you must have allocated: each byte is set
|
||||
* to either -1 if we have the piece, otherwise it is set to the number
|
||||
* of connected peers who have the piece.
|
||||
**********************************************************************/
|
||||
void tr_torrentAvailability( tr_torrent_t *, int8_t * tab, int size );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_torrentClose
|
||||
@@ -161,18 +192,11 @@ int tr_torrentStat( tr_handle_t *, tr_stat_t ** s );
|
||||
* Frees memory allocated by tr_torrentInit. If the torrent was running,
|
||||
* you must call tr_torrentStop() before closing it.
|
||||
**********************************************************************/
|
||||
void tr_torrentClose( tr_handle_t *, int );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_close
|
||||
***********************************************************************
|
||||
* Frees memory allocated by tr_init.
|
||||
**********************************************************************/
|
||||
void tr_close( tr_handle_t * );
|
||||
void tr_torrentClose( tr_handle_t *, tr_torrent_t * );
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* tr_stat_s
|
||||
* tr_info_s
|
||||
**********************************************************************/
|
||||
typedef struct tr_file_s
|
||||
{
|
||||
@@ -180,8 +204,7 @@ typedef struct tr_file_s
|
||||
char name[MAX_PATH_LENGTH]; /* Path to the file */
|
||||
}
|
||||
tr_file_t;
|
||||
|
||||
typedef struct tr_info_s
|
||||
struct tr_info_s
|
||||
{
|
||||
/* Path to torrent */
|
||||
char torrent[MAX_PATH_LENGTH];
|
||||
@@ -204,13 +227,13 @@ typedef struct tr_info_s
|
||||
/* Files info */
|
||||
int fileCount;
|
||||
tr_file_t * files;
|
||||
}
|
||||
tr_info_t;
|
||||
};
|
||||
|
||||
/***********************************************************************
|
||||
* tr_stat_s
|
||||
**********************************************************************/
|
||||
struct tr_stat_s
|
||||
{
|
||||
tr_info_t info;
|
||||
|
||||
#define TR_STATUS_CHECK 0x001 /* Checking files */
|
||||
#define TR_STATUS_DOWNLOAD 0x002 /* Downloading */
|
||||
#define TR_STATUS_SEED 0x004 /* Seeding */
|
||||
@@ -218,9 +241,15 @@ struct tr_stat_s
|
||||
#define TR_STATUS_STOPPED 0x010 /* Sent 'stopped' but thread still
|
||||
running (for internal use only) */
|
||||
#define TR_STATUS_PAUSE 0x020 /* Paused */
|
||||
#define TR_TRACKER_ERROR 0x100
|
||||
|
||||
#define TR_STATUS_ACTIVE (TR_STATUS_CHECK|TR_STATUS_DOWNLOAD|TR_STATUS_SEED)
|
||||
#define TR_STATUS_INACTIVE (TR_STATUS_STOPPING|TR_STATUS_STOPPED|TR_STATUS_PAUSE)
|
||||
int status;
|
||||
char error[128];
|
||||
|
||||
#define TR_ETRACKER 1
|
||||
#define TR_EINOUT 2
|
||||
int error;
|
||||
char trackerError[128];
|
||||
|
||||
float progress;
|
||||
float rateDownload;
|
||||
@@ -229,14 +258,11 @@ struct tr_stat_s
|
||||
int peersTotal;
|
||||
int peersUploading;
|
||||
int peersDownloading;
|
||||
char pieces[120];
|
||||
int seeders;
|
||||
int leechers;
|
||||
|
||||
uint64_t downloaded;
|
||||
uint64_t uploaded;
|
||||
|
||||
char * folder;
|
||||
};
|
||||
|
||||
#ifdef __TRANSMISSION__
|
||||
|
||||
@@ -1,11 +1,27 @@
|
||||
//
|
||||
// Badger.m
|
||||
// Transmission
|
||||
//
|
||||
// Created by Mitchell Livingston on 12/21/05.
|
||||
//
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2005-2006 Transmission authors and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#import "Badger.h"
|
||||
#import "StringAdditions.h"
|
||||
|
||||
@interface Badger (Private)
|
||||
|
||||
@@ -87,7 +103,7 @@
|
||||
badgeRect.origin.y += badgeBottomExtra;
|
||||
|
||||
//place badge text
|
||||
[self badgeString: [NSString stringWithFormat: @"%d", completed]
|
||||
[self badgeString: [NSString stringWithInt: completed]
|
||||
forRect: badgeRect];
|
||||
|
||||
[dockIcon unlockFocus];
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2005 Eric Petit
|
||||
* Copyright (c) 2005-2006 Transmission authors and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
@@ -32,10 +32,9 @@
|
||||
|
||||
@interface Controller : NSObject
|
||||
{
|
||||
tr_handle_t * fHandle;
|
||||
tr_handle_t * fLib;
|
||||
int fCount, fSeeding, fDownloading, fCompleted;
|
||||
tr_stat_t * fStat;
|
||||
int fResumeOnWake[TR_MAX_TORRENT_COUNT];
|
||||
NSMutableArray * fTorrents;
|
||||
|
||||
NSToolbar * fToolbar;
|
||||
|
||||
@@ -127,8 +126,6 @@
|
||||
- (void) linkForums: (id) sender;
|
||||
- (void) notifyGrowl: (NSString *) file;
|
||||
- (void) revealFromMenu: (id) sender;
|
||||
- (void) finderReveal: (NSString *) path;
|
||||
- (void) finderTrash: (NSString *) path;
|
||||
- (void) growlRegister: (id) sender;
|
||||
|
||||
- (void) checkForUpdate: (id) sender;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2005 Eric Petit
|
||||
* Copyright (c) 2005-2006 Transmission authors and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
@@ -22,8 +22,9 @@
|
||||
|
||||
#import <IOKit/IOMessage.h>
|
||||
|
||||
#import "NameCell.h"
|
||||
#import "ProgressCell.h"
|
||||
#import "Controller.h"
|
||||
#import "Torrent.h"
|
||||
#import "TorrentCell.h"
|
||||
#import "StringAdditions.h"
|
||||
#import "Utils.h"
|
||||
#import "TorrentTableView.h"
|
||||
@@ -52,21 +53,33 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
|
||||
@implementation Controller
|
||||
|
||||
- (id) init
|
||||
{
|
||||
fLib = tr_init();
|
||||
fTorrents = [[NSMutableArray alloc] initWithCapacity: 10];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
[fTorrents release];
|
||||
tr_close( fLib );
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void) awakeFromNib
|
||||
{
|
||||
[fWindow setContentMinSize: NSMakeSize( 400, 120 )];
|
||||
|
||||
fHandle = tr_init();
|
||||
|
||||
[fPrefsController setPrefsWindow: fHandle];
|
||||
[fPrefsController setPrefsWindow: fLib];
|
||||
fDefaults = [NSUserDefaults standardUserDefaults];
|
||||
|
||||
|
||||
[fInfoPanel setFrameAutosaveName:@"InfoPanel"];
|
||||
|
||||
//check advanced bar menu item
|
||||
[fAdvancedBarItem setState: [fDefaults
|
||||
boolForKey:@"UseAdvancedBar"] ? NSOnState : NSOffState];
|
||||
|
||||
|
||||
fToolbar = [[NSToolbar alloc] initWithIdentifier: @"Transmission Toolbar"];
|
||||
[fToolbar setDelegate: self];
|
||||
[fToolbar setAllowsUserCustomization: YES];
|
||||
@@ -74,18 +87,9 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
[fWindow setToolbar: fToolbar];
|
||||
[fWindow setDelegate: self];
|
||||
|
||||
NSTableColumn * tableColumn;
|
||||
NameCell * nameCell = [[NameCell alloc] init];
|
||||
ProgressCell * progressCell = [[ProgressCell alloc] init];
|
||||
|
||||
tableColumn = [fTableView tableColumnWithIdentifier: @"Name"];
|
||||
[tableColumn setDataCell: nameCell];
|
||||
|
||||
tableColumn = [fTableView tableColumnWithIdentifier: @"Progress"];
|
||||
[tableColumn setDataCell: progressCell];
|
||||
|
||||
[fTableView setAutosaveTableColumns: YES];
|
||||
//[fTableView sizeToFit];
|
||||
[fTableView setTorrents: fTorrents];
|
||||
[[fTableView tableColumnWithIdentifier: @"Torrent"] setDataCell:
|
||||
[[TorrentCell alloc] init]];
|
||||
|
||||
[fTableView registerForDraggedTypes: [NSArray arrayWithObjects:
|
||||
NSFilenamesPboardType, NULL]];
|
||||
@@ -108,24 +112,27 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
NSString * torrentPath, * downloadFolder, * paused;
|
||||
NSDictionary * dic;
|
||||
|
||||
Torrent * torrent;
|
||||
NSEnumerator * enumerator = [[fDefaults arrayForKey: @"History"] objectEnumerator];
|
||||
while ((dic = [enumerator nextObject]))
|
||||
{
|
||||
torrentPath = [dic objectForKey: @"TorrentPath"];
|
||||
downloadFolder = [dic objectForKey: @"DownloadFolder"];
|
||||
paused = [dic objectForKey: @"Paused"];
|
||||
|
||||
|
||||
if (!torrentPath || !downloadFolder || !paused)
|
||||
continue;
|
||||
|
||||
if (tr_torrentInit(fHandle, [torrentPath UTF8String]))
|
||||
torrent = [[Torrent alloc] initWithPath: torrentPath lib: fLib];
|
||||
if( !torrent )
|
||||
continue;
|
||||
|
||||
tr_torrentSetFolder( fHandle, tr_torrentCount( fHandle ) - 1,
|
||||
[downloadFolder UTF8String] );
|
||||
[fTorrents addObject: torrent];
|
||||
|
||||
[torrent setFolder: downloadFolder];
|
||||
|
||||
if ([paused isEqualToString: @"NO"])
|
||||
tr_torrentStart( fHandle, tr_torrentCount( fHandle ) - 1 );
|
||||
[torrent start];
|
||||
}
|
||||
|
||||
//check and register Growl if it is installed for this user or all users
|
||||
@@ -137,13 +144,11 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
|
||||
//initialize badging
|
||||
fBadger = [[Badger alloc] init];
|
||||
|
||||
|
||||
//update the interface every 500 ms
|
||||
fCount = 0;
|
||||
fDownloading = 0;
|
||||
fSeeding = 0;
|
||||
fCompleted = 0;
|
||||
fStat = nil;
|
||||
[self updateUI: nil];
|
||||
fTimer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target: self
|
||||
selector: @selector( updateUI: ) userInfo: nil repeats: YES];
|
||||
@@ -184,7 +189,17 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
|
||||
{
|
||||
int active = fDownloading + fSeeding;
|
||||
int active = 0;
|
||||
Torrent * torrent;
|
||||
NSEnumerator * enumerator = [fTorrents objectEnumerator];
|
||||
while( ( torrent = [enumerator nextObject] ) )
|
||||
{
|
||||
if( [torrent isActive] )
|
||||
{
|
||||
active++;
|
||||
}
|
||||
}
|
||||
|
||||
if (active > 0 && [fDefaults boolForKey: @"CheckQuit"])
|
||||
{
|
||||
NSString * message = active == 1
|
||||
@@ -199,8 +214,8 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
@selector(quitSheetDidEnd:returnCode:contextInfo:),
|
||||
nil, nil, message);
|
||||
return NSTerminateLater;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return NSTerminateNow;
|
||||
}
|
||||
|
||||
@@ -214,40 +229,41 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
|
||||
- (void) applicationWillTerminate: (NSNotification *) notification
|
||||
{
|
||||
int i;
|
||||
|
||||
NSEnumerator * enumerator;
|
||||
Torrent * torrent;
|
||||
|
||||
// Stop updating the interface
|
||||
[fTimer invalidate];
|
||||
[fUpdateTimer invalidate];
|
||||
|
||||
//clear badge
|
||||
[fBadger clearBadge];
|
||||
[fBadger release];
|
||||
[fBadger release];
|
||||
|
||||
// Save history
|
||||
[self updateTorrentHistory];
|
||||
|
||||
|
||||
// Stop running torrents
|
||||
for( i = 0; i < fCount; i++ )
|
||||
if( fStat[i].status & ( TR_STATUS_CHECK | TR_STATUS_DOWNLOAD |
|
||||
TR_STATUS_SEED ) )
|
||||
tr_torrentStop( fHandle, i );
|
||||
enumerator = [fTorrents objectEnumerator];
|
||||
while( ( torrent = [enumerator nextObject] ) )
|
||||
{
|
||||
[torrent stop];
|
||||
}
|
||||
|
||||
// Wait for torrents to stop (5 seconds timeout)
|
||||
NSDate * start = [NSDate date];
|
||||
while( fCount > 0 )
|
||||
while( [fTorrents count] )
|
||||
{
|
||||
torrent = [fTorrents objectAtIndex: 0];
|
||||
while( [[NSDate date] timeIntervalSinceDate: start] < 5 &&
|
||||
!( fStat[0].status & TR_STATUS_PAUSE ) )
|
||||
![torrent isPaused] )
|
||||
{
|
||||
usleep( 100000 );
|
||||
tr_torrentStat( fHandle, &fStat );
|
||||
[torrent update];
|
||||
}
|
||||
tr_torrentClose( fHandle, 0 );
|
||||
fCount = tr_torrentStat( fHandle, &fStat );
|
||||
[fTorrents removeObject: torrent];
|
||||
[torrent release];
|
||||
}
|
||||
|
||||
tr_close( fHandle );
|
||||
}
|
||||
|
||||
- (void) showPreferenceWindow: (id) sender
|
||||
@@ -257,22 +273,22 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
{
|
||||
[fPrefsWindow center];
|
||||
}
|
||||
|
||||
|
||||
[fPrefsWindow makeKeyAndOrderFront:NULL];
|
||||
}
|
||||
|
||||
- (void) folderChoiceClosed: (NSOpenPanel *) s returnCode: (int) code
|
||||
contextInfo: (void *) info
|
||||
{
|
||||
Torrent * torrent = [fTorrents lastObject];
|
||||
if (code == NSOKButton)
|
||||
{
|
||||
tr_torrentSetFolder( fHandle, tr_torrentCount( fHandle ) - 1,
|
||||
[[[s filenames] objectAtIndex: 0] UTF8String] );
|
||||
tr_torrentStart( fHandle, tr_torrentCount( fHandle ) - 1 );
|
||||
[torrent setFolder: [[s filenames] objectAtIndex: 0]];
|
||||
[torrent start];
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_torrentClose( fHandle, tr_torrentCount( fHandle ) - 1 );
|
||||
[torrent release];
|
||||
}
|
||||
[NSApp stopModal];
|
||||
}
|
||||
@@ -281,6 +297,7 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
openFiles: (NSArray *) filenames
|
||||
{
|
||||
NSString * downloadChoice, * downloadFolder, * torrentPath;
|
||||
Torrent * torrent;
|
||||
|
||||
downloadChoice = [fDefaults stringForKey: @"DownloadChoice"];
|
||||
downloadFolder = [fDefaults stringForKey: @"DownloadFolder"];
|
||||
@@ -288,8 +305,10 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
NSEnumerator * enumerator = [filenames objectEnumerator];
|
||||
while ((torrentPath = [enumerator nextObject]))
|
||||
{
|
||||
if( tr_torrentInit( fHandle, [torrentPath UTF8String] ) )
|
||||
torrent = [[Torrent alloc] initWithPath: torrentPath lib: fLib];
|
||||
if( !torrent )
|
||||
continue;
|
||||
[fTorrents addObject: torrent];
|
||||
|
||||
/* Add it to the "File > Open Recent" menu */
|
||||
[[NSDocumentController sharedDocumentController]
|
||||
@@ -297,20 +316,18 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
|
||||
if( [downloadChoice isEqualToString: @"Constant"] )
|
||||
{
|
||||
tr_torrentSetFolder( fHandle, tr_torrentCount( fHandle ) - 1,
|
||||
[[downloadFolder stringByExpandingTildeInPath] UTF8String] );
|
||||
tr_torrentStart( fHandle, tr_torrentCount( fHandle ) - 1 );
|
||||
[torrent setFolder: [downloadFolder stringByExpandingTildeInPath]];
|
||||
[torrent start];
|
||||
}
|
||||
else if( [downloadChoice isEqualToString: @"Torrent"] )
|
||||
{
|
||||
tr_torrentSetFolder( fHandle, tr_torrentCount( fHandle ) - 1,
|
||||
[[torrentPath stringByDeletingLastPathComponent] UTF8String] );
|
||||
tr_torrentStart( fHandle, tr_torrentCount( fHandle ) - 1 );
|
||||
[torrent setFolder: [torrentPath stringByDeletingLastPathComponent]];
|
||||
[torrent start];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSOpenPanel * panel = [NSOpenPanel openPanel];
|
||||
|
||||
|
||||
[panel setPrompt: @"Select Download Folder"];
|
||||
[panel setMessage: [NSString stringWithFormat:
|
||||
@"Select the download folder for %@",
|
||||
@@ -355,7 +372,7 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
|
||||
panel = [NSOpenPanel openPanel];
|
||||
fileTypes = [NSArray arrayWithObject: @"torrent"];
|
||||
|
||||
|
||||
[panel setAllowsMultipleSelection: YES];
|
||||
[panel setCanChooseFiles: YES];
|
||||
[panel setCanChooseDirectories: NO];
|
||||
@@ -393,21 +410,21 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
|
||||
- (void) resumeAllTorrents: (id) sender
|
||||
{
|
||||
int i;
|
||||
for ( i = 0; i < fCount; i++)
|
||||
Torrent * torrent;
|
||||
NSEnumerator * enumerator = [fTorrents objectEnumerator];
|
||||
while( ( torrent = [enumerator nextObject] ) )
|
||||
{
|
||||
if ( fStat[i].status & ( TR_STATUS_STOPPING
|
||||
| TR_STATUS_PAUSE | TR_STATUS_STOPPED ) )
|
||||
{
|
||||
[self resumeTorrentWithIndex: i];
|
||||
}
|
||||
[torrent start];
|
||||
}
|
||||
[self updateUI: nil];
|
||||
[self updateTorrentHistory];
|
||||
}
|
||||
|
||||
- (void) resumeTorrentWithIndex: (int) idx
|
||||
{
|
||||
tr_torrentStart( fHandle, idx );
|
||||
[self updateUI: NULL];
|
||||
Torrent * torrent = [fTorrents objectAtIndex: idx];
|
||||
[torrent start];
|
||||
[self updateUI: nil];
|
||||
[self updateTorrentHistory];
|
||||
}
|
||||
|
||||
@@ -418,21 +435,21 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
|
||||
- (void) stopAllTorrents: (id) sender
|
||||
{
|
||||
int i;
|
||||
for ( i = 0; i < fCount; i++)
|
||||
Torrent * torrent;
|
||||
NSEnumerator * enumerator = [fTorrents objectEnumerator];
|
||||
while( ( torrent = [enumerator nextObject] ) )
|
||||
{
|
||||
if ( fStat[i].status & ( TR_STATUS_CHECK
|
||||
| TR_STATUS_DOWNLOAD | TR_STATUS_SEED) )
|
||||
{
|
||||
[self stopTorrentWithIndex: i];
|
||||
}
|
||||
[torrent stop];
|
||||
}
|
||||
[self updateUI: nil];
|
||||
[self updateTorrentHistory];
|
||||
}
|
||||
|
||||
- (void) stopTorrentWithIndex: (int) idx
|
||||
{
|
||||
tr_torrentStop( fHandle, idx );
|
||||
[self updateUI: NULL];
|
||||
Torrent * torrent = [fTorrents objectAtIndex: idx];
|
||||
[torrent stop];
|
||||
[self updateUI: nil];
|
||||
[self updateTorrentHistory];
|
||||
}
|
||||
|
||||
@@ -440,73 +457,69 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
deleteTorrent: (BOOL) deleteTorrent
|
||||
deleteData: (BOOL) deleteData
|
||||
{
|
||||
if ( fStat[idx].status & ( TR_STATUS_CHECK
|
||||
| TR_STATUS_DOWNLOAD | TR_STATUS_SEED ) )
|
||||
Torrent * torrent = [fTorrents objectAtIndex: idx];
|
||||
|
||||
if( [torrent isActive] && [fDefaults boolForKey: @"CheckRemove"] )
|
||||
{
|
||||
if ([fDefaults boolForKey: @"CheckRemove"])
|
||||
{
|
||||
NSDictionary * dict = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSString stringWithFormat: @"%d", idx], @"Index",
|
||||
[NSString stringWithFormat: @"%d", deleteTorrent], @"DeleteTorrent",
|
||||
[NSString stringWithFormat: @"%d", deleteData], @"DeleteData",
|
||||
nil];
|
||||
[dict retain];
|
||||
|
||||
NSBeginAlertSheet(@"Confirm Remove",
|
||||
@"Remove", @"Cancel", nil,
|
||||
fWindow, self,
|
||||
@selector(removeSheetDidEnd:returnCode:contextInfo:),
|
||||
NULL, dict, @"This torrent is active. Do you really want to remove it?");
|
||||
return;
|
||||
}
|
||||
//stop if not stopped
|
||||
else
|
||||
[self stopTorrentWithIndex:idx];
|
||||
NSDictionary * dict = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSString stringWithInt: idx], @"Index",
|
||||
[NSString stringWithInt: deleteTorrent], @"DeleteTorrent",
|
||||
[NSString stringWithInt: deleteData], @"DeleteData",
|
||||
nil];
|
||||
[dict retain];
|
||||
|
||||
NSBeginAlertSheet(@"Confirm Remove",
|
||||
@"Remove", @"Cancel", nil, fWindow, self,
|
||||
@selector(removeSheetDidEnd:returnCode:contextInfo:), NULL, dict,
|
||||
@"This torrent is active. Do you really want to remove it?");
|
||||
}
|
||||
else
|
||||
{
|
||||
[self confirmRemoveTorrentWithIndex: idx
|
||||
deleteTorrent: deleteTorrent
|
||||
deleteData: deleteData];
|
||||
}
|
||||
|
||||
[self confirmRemoveTorrentWithIndex: idx
|
||||
deleteTorrent: deleteTorrent
|
||||
deleteData: deleteData];
|
||||
}
|
||||
|
||||
- (void) removeSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode
|
||||
contextInfo:(NSDictionary *)dict
|
||||
{
|
||||
[NSApp stopModal];
|
||||
if (returnCode != NSAlertDefaultReturn)
|
||||
{
|
||||
[dict release];
|
||||
return;
|
||||
}
|
||||
|
||||
int idx = [[dict objectForKey:@"Index"] intValue];
|
||||
|
||||
[self stopTorrentWithIndex:idx];
|
||||
|
||||
[self confirmRemoveTorrentWithIndex: idx
|
||||
deleteTorrent: [[dict objectForKey:@"DeleteTorrent"] intValue]
|
||||
deleteData: [[dict objectForKey:@"DeleteData"] intValue]];
|
||||
if( returnCode == NSAlertDefaultReturn )
|
||||
{
|
||||
int idx = [[dict objectForKey:@"Index"] intValue];
|
||||
|
||||
[self confirmRemoveTorrentWithIndex: idx
|
||||
deleteTorrent: [[dict objectForKey:@"DeleteTorrent"] intValue]
|
||||
deleteData: [[dict objectForKey:@"DeleteData"] intValue]];
|
||||
}
|
||||
|
||||
[dict release];
|
||||
}
|
||||
|
||||
|
||||
- (void) confirmRemoveTorrentWithIndex: (int) idx
|
||||
deleteTorrent: (BOOL) deleteTorrent
|
||||
deleteData: (BOOL) deleteData
|
||||
{
|
||||
Torrent * torrent = [fTorrents objectAtIndex: idx];
|
||||
|
||||
/* Pause if not paused already */
|
||||
[torrent stop];
|
||||
|
||||
if( deleteData )
|
||||
{
|
||||
[self finderTrash: [NSString stringWithFormat: @"%@/%@",
|
||||
[NSString stringWithUTF8String: fStat[idx].folder],
|
||||
[NSString stringWithUTF8String: fStat[idx].info.name]]];
|
||||
[torrent trashData];
|
||||
}
|
||||
if( deleteTorrent )
|
||||
{
|
||||
[self finderTrash: [NSString stringWithUTF8String:
|
||||
fStat[idx].info.torrent]];
|
||||
[torrent trashTorrent];
|
||||
}
|
||||
|
||||
tr_torrentClose( fHandle, idx );
|
||||
[self updateUI: NULL];
|
||||
|
||||
[fTorrents removeObject: torrent];
|
||||
[torrent release];
|
||||
|
||||
[self updateUI: nil];
|
||||
[self updateTorrentHistory];
|
||||
}
|
||||
|
||||
@@ -545,24 +558,35 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
- (void) updateUI: (NSTimer *) t
|
||||
{
|
||||
float dl, ul;
|
||||
int row, i;
|
||||
NSEnumerator * enumerator;
|
||||
Torrent * torrent;
|
||||
|
||||
//Update the NSTableView
|
||||
if (fStat)
|
||||
free(fStat);
|
||||
|
||||
fCount = tr_torrentStat( fHandle, &fStat );
|
||||
fDownloading = 0;
|
||||
fSeeding = 0;
|
||||
[fTableView updateUI: fStat];
|
||||
/* Update the TableView */
|
||||
enumerator = [fTorrents objectEnumerator];
|
||||
while( ( torrent = [enumerator nextObject] ) )
|
||||
{
|
||||
[torrent update];
|
||||
|
||||
if( [torrent justFinished] )
|
||||
{
|
||||
/* Notifications */
|
||||
[self notifyGrowl: [torrent name]];
|
||||
if( ![fWindow isKeyWindow] )
|
||||
{
|
||||
fCompleted++;
|
||||
}
|
||||
}
|
||||
}
|
||||
[fTableView reloadData];
|
||||
|
||||
//Update the global DL/UL rates
|
||||
tr_torrentRates( fHandle, &dl, &ul );
|
||||
tr_torrentRates( fLib, &dl, &ul );
|
||||
NSString * downloadRate = [NSString stringForSpeed: dl];
|
||||
NSString * uploadRate = [NSString stringForSpeed: ul];
|
||||
[fTotalDLField setStringValue: downloadRate];
|
||||
[fTotalULField setStringValue: uploadRate];
|
||||
|
||||
#if 0
|
||||
//Update DL/UL totals in the Info panel
|
||||
row = [fTableView selectedRow];
|
||||
if( row >= 0 )
|
||||
@@ -572,24 +596,7 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
[fInfoUploaded setStringValue:
|
||||
[NSString stringForFileSize: fStat[row].uploaded]];
|
||||
}
|
||||
|
||||
//check if torrents have recently ended.
|
||||
for (i = 0; i < fCount; i++)
|
||||
{
|
||||
if (fStat[i].status & (TR_STATUS_CHECK | TR_STATUS_DOWNLOAD))
|
||||
fDownloading++;
|
||||
else if (fStat[i].status & TR_STATUS_SEED)
|
||||
fSeeding++;
|
||||
|
||||
if( !tr_getFinished( fHandle, i ) )
|
||||
continue;
|
||||
|
||||
if( ![fWindow isKeyWindow] )
|
||||
fCompleted++;
|
||||
[self notifyGrowl: [NSString stringWithUTF8String:
|
||||
fStat[i].info.name]];
|
||||
tr_setFinished( fHandle, i, 0 );
|
||||
}
|
||||
#endif
|
||||
|
||||
//badge dock
|
||||
[fBadger updateBadgeWithCompleted: fCompleted
|
||||
@@ -601,51 +608,42 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
|
||||
- (void) updateTorrentHistory
|
||||
{
|
||||
if( !fStat )
|
||||
return;
|
||||
NSMutableArray * history = [NSMutableArray
|
||||
arrayWithCapacity: [fTorrents count]];
|
||||
|
||||
NSMutableArray * history = [NSMutableArray arrayWithCapacity: fCount];
|
||||
|
||||
int i;
|
||||
for (i = 0; i < fCount; i++)
|
||||
NSEnumerator * enumerator = [fTorrents objectEnumerator];
|
||||
Torrent * torrent;
|
||||
while( ( torrent = [enumerator nextObject] ) )
|
||||
{
|
||||
[history addObject: [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSString stringWithUTF8String: fStat[i].info.torrent],
|
||||
@"TorrentPath",
|
||||
[NSString stringWithUTF8String: tr_torrentGetFolder( fHandle, i )],
|
||||
@"DownloadFolder",
|
||||
fStat[i].status & (TR_STATUS_CHECK | TR_STATUS_DOWNLOAD |
|
||||
TR_STATUS_SEED) ? @"NO" : @"YES",
|
||||
@"Paused",
|
||||
[torrent path], @"TorrentPath",
|
||||
[torrent getFolder], @"DownloadFolder",
|
||||
[torrent isActive] ? @"NO" : @"YES", @"Paused",
|
||||
nil]];
|
||||
}
|
||||
|
||||
[fDefaults setObject: history forKey: @"History"];
|
||||
}
|
||||
|
||||
- (int) numberOfRowsInTableView: (NSTableView *) t
|
||||
{
|
||||
return fCount;
|
||||
return [fTorrents count];
|
||||
}
|
||||
|
||||
- (id) tableView: (NSTableView *) t objectValueForTableColumn:
|
||||
(NSTableColumn *) tableColumn row: (int) rowIndex
|
||||
{
|
||||
return NULL;
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
- (void) tableView: (NSTableView *) t willDisplayCell: (id) cell
|
||||
forTableColumn: (NSTableColumn *) tableColumn row: (int) rowIndex
|
||||
{
|
||||
BOOL w;
|
||||
|
||||
w = [fWindow isKeyWindow] && rowIndex == [fTableView selectedRow];
|
||||
if( [[tableColumn identifier] isEqualToString: @"Name"] )
|
||||
{
|
||||
[(NameCell *) cell setStat: &fStat[rowIndex] whiteText: w];
|
||||
}
|
||||
else if( [[tableColumn identifier] isEqualToString: @"Progress"] )
|
||||
{
|
||||
[(ProgressCell *) cell setStat: &fStat[rowIndex] whiteText: w];
|
||||
}
|
||||
[cell setTorrent: [fTorrents objectAtIndex: rowIndex]];
|
||||
[cell setTextColor: ( [fWindow isKeyWindow] &&
|
||||
rowIndex == [fTableView selectedRow] ) ?
|
||||
[NSColor whiteColor] : [NSColor blackColor]];
|
||||
}
|
||||
|
||||
- (BOOL) tableView: (NSTableView *) t acceptDrop:
|
||||
@@ -670,7 +668,8 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
count] == 0)
|
||||
return NSDragOperationNone;
|
||||
|
||||
[fTableView setDropRow: fCount dropOperation: NSTableViewDropAbove];
|
||||
[fTableView setDropRow: [fTorrents count]
|
||||
dropOperation: NSTableViewDropAbove];
|
||||
return NSDragOperationGeneric;
|
||||
}
|
||||
|
||||
@@ -694,6 +693,7 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Update info window */
|
||||
[fInfoTitle setStringValue: [NSString stringWithUTF8String:
|
||||
fStat[row].info.name]];
|
||||
@@ -703,25 +703,26 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
fStat[row].info.trackerAnnounce]];
|
||||
[fInfoSize setStringValue:
|
||||
[NSString stringForFileSize: fStat[row].info.totalSize]];
|
||||
[fInfoPieces setStringValue: [NSString stringWithFormat: @"%d",
|
||||
[fInfoPieces setStringValue: [NSString stringWithInt:
|
||||
fStat[row].info.pieceCount]];
|
||||
[fInfoPieceSize setStringValue:
|
||||
[NSString stringForFileSize: fStat[row].info.pieceSize]];
|
||||
[fInfoFolder setStringValue: [[NSString stringWithUTF8String:
|
||||
tr_torrentGetFolder( fHandle, row )] lastPathComponent]];
|
||||
|
||||
|
||||
if ( fStat[row].seeders == -1 ) {
|
||||
[fInfoSeeders setStringValue: [NSString stringWithUTF8String: "?"]];
|
||||
} else {
|
||||
[fInfoSeeders setStringValue: [NSString stringWithFormat: @"%d",
|
||||
[fInfoSeeders setStringValue: [NSString stringWithInt:
|
||||
fStat[row].seeders]];
|
||||
}
|
||||
if ( fStat[row].leechers == -1 ) {
|
||||
[fInfoLeechers setStringValue: [NSString stringWithUTF8String: "?"]];
|
||||
} else {
|
||||
[fInfoLeechers setStringValue: [NSString stringWithFormat: @"%d",
|
||||
[fInfoLeechers setStringValue: [NSString stringWithInt:
|
||||
fStat[row].leechers]];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
- (NSToolbarItem *) toolbar: (NSToolbar *) t itemForItemIdentifier:
|
||||
@@ -824,16 +825,30 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
//enable remove item
|
||||
if (action == @selector(removeTorrent:))
|
||||
return [fTableView selectedRow] >= 0;
|
||||
|
||||
|
||||
Torrent * torrent;
|
||||
NSEnumerator * enumerator;
|
||||
|
||||
//enable resume all item
|
||||
if (action == @selector(resumeAllTorrents:))
|
||||
return fCount > fDownloading + fSeeding;
|
||||
{
|
||||
enumerator = [fTorrents objectEnumerator];
|
||||
while( ( torrent = [enumerator nextObject] ) )
|
||||
if( [torrent isPaused] )
|
||||
return YES;
|
||||
return NO;
|
||||
}
|
||||
|
||||
//enable pause all item
|
||||
if (action == @selector(stopAllTorrents:))
|
||||
return fDownloading > 0 || fSeeding > 0;
|
||||
|
||||
{
|
||||
enumerator = [fTorrents objectEnumerator];
|
||||
while( ( torrent = [enumerator nextObject] ) )
|
||||
if( [torrent isActive] )
|
||||
return YES;
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
@@ -844,14 +859,14 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
//disable menus if customize sheet is active
|
||||
if ([fToolbar customizationPaletteIsRunning])
|
||||
return NO;
|
||||
|
||||
|
||||
//enable customize toolbar item
|
||||
if (action == @selector(showHideToolbar:))
|
||||
{
|
||||
[menuItem setTitle: [fToolbar isVisible] ? @"Hide Toolbar" : @"Show Toolbar"];
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
//enable show info
|
||||
if (action == @selector(showInfo:))
|
||||
{
|
||||
@@ -859,16 +874,37 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
return YES;
|
||||
}
|
||||
|
||||
Torrent * torrent;
|
||||
NSEnumerator * enumerator;
|
||||
|
||||
//enable resume all item
|
||||
if (action == @selector(resumeAllTorrents:))
|
||||
return fCount > fDownloading + fSeeding;
|
||||
{
|
||||
enumerator = [fTorrents objectEnumerator];
|
||||
while( ( torrent = [enumerator nextObject] ) )
|
||||
if( [torrent isPaused] )
|
||||
return YES;
|
||||
return NO;
|
||||
}
|
||||
|
||||
//enable pause all item
|
||||
if (action == @selector(stopAllTorrents:))
|
||||
return fDownloading > 0 || fSeeding > 0;
|
||||
|
||||
{
|
||||
enumerator = [fTorrents objectEnumerator];
|
||||
while( ( torrent = [enumerator nextObject] ) )
|
||||
if( [torrent isActive] )
|
||||
return YES;
|
||||
return NO;
|
||||
}
|
||||
|
||||
int row = [fTableView selectedRow];
|
||||
|
||||
torrent = ( row < 0 ) ? nil : [fTorrents objectAtIndex: row];
|
||||
|
||||
if (action == @selector(revealFromMenu:))
|
||||
{
|
||||
return ( torrent != nil );
|
||||
}
|
||||
|
||||
//enable remove items
|
||||
if (action == @selector(removeTorrent:)
|
||||
|| action == @selector(removeTorrentDeleteFile:)
|
||||
@@ -876,8 +912,8 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
|| action == @selector(removeTorrentDeleteBoth:))
|
||||
{
|
||||
//append or remove ellipsis when needed
|
||||
if (row >= 0 && fStat[row].status & ( TR_STATUS_CHECK | TR_STATUS_DOWNLOAD)
|
||||
&& [[fDefaults stringForKey: @"CheckRemove"] isEqualToString:@"YES"])
|
||||
if( torrent && [torrent isActive] &&
|
||||
[fDefaults boolForKey: @"CheckRemove"] )
|
||||
{
|
||||
if (![[menuItem title] hasSuffix:NS_ELLIPSIS])
|
||||
[menuItem setTitle:[[menuItem title] stringByAppendingString:NS_ELLIPSIS]];
|
||||
@@ -887,59 +923,59 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
if ([[menuItem title] hasSuffix:NS_ELLIPSIS])
|
||||
[menuItem setTitle:[[menuItem title] substringToIndex:[[menuItem title] length]-[NS_ELLIPSIS length]]];
|
||||
}
|
||||
return row >= 0;
|
||||
return ( torrent != nil );
|
||||
}
|
||||
|
||||
//enable reveal in finder item
|
||||
if (action == @selector(revealFromMenu:))
|
||||
return row >= 0;
|
||||
|
||||
|
||||
//enable and change pause / remove item
|
||||
if (action == @selector(resumeTorrent:) || action == @selector(stopTorrent:))
|
||||
if( action == @selector(resumeTorrent:) ||
|
||||
action == @selector(stopTorrent:) )
|
||||
{
|
||||
if (row >= 0 && fStat[row].status & TR_STATUS_PAUSE)
|
||||
{
|
||||
[menuItem setTitle: @"Resume"];
|
||||
[menuItem setAction: @selector( resumeTorrent: )];
|
||||
}
|
||||
else
|
||||
if( !torrent || [torrent isActive] )
|
||||
{
|
||||
[menuItem setTitle: @"Pause"];
|
||||
[menuItem setAction: @selector( stopTorrent: )];
|
||||
}
|
||||
return row >= 0;
|
||||
else
|
||||
{
|
||||
[menuItem setTitle: @"Resume"];
|
||||
[menuItem setAction: @selector( resumeTorrent: )];
|
||||
}
|
||||
return ( torrent != nil );
|
||||
}
|
||||
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void) sleepCallBack: (natural_t) messageType argument:
|
||||
(void *) messageArgument
|
||||
{
|
||||
int i;
|
||||
NSEnumerator * enumerator;;
|
||||
Torrent * torrent;
|
||||
|
||||
switch( messageType )
|
||||
{
|
||||
case kIOMessageSystemWillSleep:
|
||||
/* Close all connections before going to sleep and remember
|
||||
we should resume when we wake up */
|
||||
for( i = 0; i < fCount; i++ )
|
||||
enumerator = [fTorrents objectEnumerator];
|
||||
while( ( torrent = [enumerator nextObject] ) )
|
||||
{
|
||||
if( fStat[i].status & ( TR_STATUS_CHECK |
|
||||
TR_STATUS_DOWNLOAD | TR_STATUS_SEED ) )
|
||||
[torrent sleep];
|
||||
}
|
||||
|
||||
/* Wait for torrents to stop (5 seconds timeout) */
|
||||
NSDate * start = [NSDate date];
|
||||
enumerator = [fTorrents objectEnumerator];
|
||||
while( ( torrent = [enumerator nextObject] ) )
|
||||
{
|
||||
while( [[NSDate date] timeIntervalSinceDate: start] < 5 &&
|
||||
![torrent isPaused] )
|
||||
{
|
||||
tr_torrentStop( fHandle, i );
|
||||
fResumeOnWake[i] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
fResumeOnWake[i] = 0;
|
||||
usleep( 100000 );
|
||||
[torrent update];
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: wait a few seconds to let the torrents
|
||||
stop properly */
|
||||
|
||||
IOAllowPowerChange( fRootPort, (long) messageArgument );
|
||||
break;
|
||||
|
||||
@@ -953,12 +989,10 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
|
||||
case kIOMessageSystemHasPoweredOn:
|
||||
/* Resume download after we wake up */
|
||||
for( i = 0; i < fCount; i++ )
|
||||
enumerator = [fTorrents objectEnumerator];
|
||||
while( ( torrent = [enumerator nextObject] ) )
|
||||
{
|
||||
if( fResumeOnWake[i] )
|
||||
{
|
||||
tr_torrentStart( fHandle, i );
|
||||
}
|
||||
[torrent wakeUp];
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -972,9 +1006,8 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
|
||||
rectWin = [fWindow frame];
|
||||
rectView = [fScrollView frame];
|
||||
foo = 25.0 + MAX( 1, fCount ) * ( [fTableView rowHeight] +
|
||||
[fTableView intercellSpacing].height ) -
|
||||
rectView.size.height;
|
||||
foo = 25.0 - rectView.size.height + MAX( 1U, [fTorrents count] ) *
|
||||
( [fTableView rowHeight] + [fTableView intercellSpacing].height );
|
||||
|
||||
rectWin.size.height += foo;
|
||||
rectWin.origin.y -= foo;
|
||||
@@ -1007,7 +1040,7 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
|
||||
if( !fHasGrowl )
|
||||
return;
|
||||
|
||||
|
||||
growlScript = [NSString stringWithFormat:
|
||||
@"tell application \"System Events\"\n"
|
||||
" if exists application process \"GrowlHelperApp\" then\n"
|
||||
@@ -1028,14 +1061,14 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
}
|
||||
|
||||
- (void) growlRegister: (id) sender
|
||||
{
|
||||
{
|
||||
NSString * growlScript;
|
||||
NSAppleScript * appleScript;
|
||||
NSDictionary * error;
|
||||
|
||||
if( !fHasGrowl )
|
||||
return;
|
||||
|
||||
|
||||
growlScript = [NSString stringWithFormat:
|
||||
@"tell application \"System Events\"\n"
|
||||
" if exists application process \"GrowlHelperApp\" then\n"
|
||||
@@ -1047,7 +1080,7 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
" end tell\n"
|
||||
" end if\n"
|
||||
"end tell"];
|
||||
|
||||
|
||||
appleScript = [[NSAppleScript alloc] initWithSource: growlScript];
|
||||
if( ![appleScript executeAndReturnError: &error] )
|
||||
{
|
||||
@@ -1058,52 +1091,9 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
|
||||
- (void) revealFromMenu: (id) sender
|
||||
{
|
||||
int row = [fTableView selectedRow];
|
||||
if (row >= 0)
|
||||
{
|
||||
[self finderReveal: [NSString stringWithFormat: @"%@/%@",
|
||||
[NSString stringWithUTF8String: fStat[row].folder],
|
||||
[NSString stringWithUTF8String: fStat[row].info.name]]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) finderReveal: (NSString *) path
|
||||
{
|
||||
NSString * string;
|
||||
NSAppleScript * appleScript;
|
||||
NSDictionary * error;
|
||||
|
||||
string = [NSString stringWithFormat:
|
||||
@"tell application \"Finder\"\n"
|
||||
" activate\n"
|
||||
" reveal (POSIX file \"%@\")\n"
|
||||
"end tell", path];
|
||||
|
||||
appleScript = [[NSAppleScript alloc] initWithSource: string];
|
||||
if( ![appleScript executeAndReturnError: &error] )
|
||||
{
|
||||
printf( "finderReveal failed\n" );
|
||||
}
|
||||
[appleScript release];
|
||||
}
|
||||
|
||||
- (void) finderTrash: (NSString *) path
|
||||
{
|
||||
NSString * string;
|
||||
NSAppleScript * appleScript;
|
||||
NSDictionary * error;
|
||||
|
||||
string = [NSString stringWithFormat:
|
||||
@"tell application \"Finder\"\n"
|
||||
" move (POSIX file \"%@\") to trash\n"
|
||||
"end tell", path];
|
||||
|
||||
appleScript = [[NSAppleScript alloc] initWithSource: string];
|
||||
if( ![appleScript executeAndReturnError: &error] )
|
||||
{
|
||||
printf( "finderTrash failed\n" );
|
||||
}
|
||||
[appleScript release];
|
||||
Torrent * torrent;
|
||||
torrent = [fTorrents objectAtIndex: [fTableView selectedRow]];
|
||||
[torrent reveal];
|
||||
}
|
||||
|
||||
- (void) checkForUpdate: (id) sender
|
||||
@@ -1141,7 +1131,7 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
[self checkForUpdateAuto: YES];
|
||||
[fDefaults setObject: [NSDate date] forKey: @"VersionCheckLast"];
|
||||
}
|
||||
|
||||
|
||||
- (void) checkForUpdateAuto: (BOOL) automatic
|
||||
{
|
||||
fCheckIsAutomatic = automatic;
|
||||
@@ -1150,7 +1140,7 @@ static void sleepCallBack( void * controller, io_service_t y,
|
||||
}
|
||||
|
||||
- (void) URLResourceDidFinishLoading: (NSURL *) sender
|
||||
{
|
||||
{
|
||||
NSDictionary * dict = [NSPropertyListSerialization
|
||||
propertyListFromData: [sender resourceDataUsingCache: NO]
|
||||
mutabilityOption: NSPropertyListImmutable
|
||||
|
||||
4
macosx/English.lproj/MainMenu.nib/info.nib
generated
4
macosx/English.lproj/MainMenu.nib/info.nib
generated
@@ -15,7 +15,7 @@
|
||||
<key>589</key>
|
||||
<string>54 521 112 118 0 0 1152 842 </string>
|
||||
<key>783</key>
|
||||
<string>483 459 420 250 0 0 1440 878 </string>
|
||||
<string>411 429 420 250 0 0 1280 832 </string>
|
||||
<key>796</key>
|
||||
<string>484 520 420 129 0 0 1440 878 </string>
|
||||
<key>825</key>
|
||||
@@ -26,6 +26,6 @@
|
||||
<key>IBOldestOS</key>
|
||||
<integer>3</integer>
|
||||
<key>IBSystem Version</key>
|
||||
<string>8F1111g</string>
|
||||
<string>8H14</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
BIN
macosx/English.lproj/MainMenu.nib/keyedobjects.nib
generated
BIN
macosx/English.lproj/MainMenu.nib/keyedobjects.nib
generated
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 3.5 KiB |
@@ -1,168 +0,0 @@
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2005 Eric Petit
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#import "NameCell.h"
|
||||
#import "StringAdditions.h"
|
||||
#import "Utils.h"
|
||||
|
||||
@implementation NameCell
|
||||
|
||||
- (id) init
|
||||
{
|
||||
if ((self = [super init]))
|
||||
fIcons = [[NSMutableDictionary alloc] initWithCapacity: 10];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
[fIcons release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSImage *) iconForFileType: (NSString *) type
|
||||
{
|
||||
NSImage * icon;
|
||||
if (!(icon = [fIcons objectForKey: type]))
|
||||
{
|
||||
/* Unknown file type, get its icon and cache it */
|
||||
icon = [[NSWorkspace sharedWorkspace] iconForFileType: type];
|
||||
[icon setFlipped: YES];
|
||||
|
||||
[fIcons setObject: icon forKey: type];
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
- (void) setStat: (tr_stat_t *) stat whiteText: (BOOL) w
|
||||
{
|
||||
fWhiteText = w;
|
||||
|
||||
fNameString = [NSString stringWithUTF8String: stat->info.name];
|
||||
fSizeString = [NSString stringWithFormat: @" (%@)",
|
||||
[NSString stringForFileSize: stat->info.totalSize]];
|
||||
|
||||
fCurrentIcon = [self iconForFileType: stat->info.fileCount > 1 ?
|
||||
NSFileTypeForHFSTypeCode('fldr') : [fNameString pathExtension]];
|
||||
|
||||
fTimeString = @"";
|
||||
fPeersString = @"";
|
||||
|
||||
if( stat->status & TR_STATUS_PAUSE )
|
||||
{
|
||||
fTimeString = [NSString stringWithFormat:
|
||||
@"Paused (%.2f %%)", 100 * stat->progress];
|
||||
}
|
||||
else if( stat->status & TR_STATUS_CHECK )
|
||||
{
|
||||
fTimeString = [NSString stringWithFormat:
|
||||
@"Checking existing files (%.2f %%)", 100 * stat->progress];
|
||||
}
|
||||
else if( stat->status & TR_STATUS_DOWNLOAD )
|
||||
{
|
||||
if( stat->eta < 0 )
|
||||
{
|
||||
fTimeString = [NSString stringWithFormat:
|
||||
@"Finishing in --:--:-- (%.2f %%)", 100 * stat->progress];
|
||||
}
|
||||
else
|
||||
{
|
||||
fTimeString = [NSString stringWithFormat:
|
||||
@"Finishing in %02d:%02d:%02d (%.2f %%)",
|
||||
stat->eta / 3600, ( stat->eta / 60 ) % 60,
|
||||
stat->eta % 60, 100 * stat->progress];
|
||||
}
|
||||
fPeersString = [NSString stringWithFormat:
|
||||
@"Downloading from %d of %d peer%s",
|
||||
stat->peersUploading, stat->peersTotal,
|
||||
( stat->peersTotal == 1 ) ? "" : "s"];
|
||||
}
|
||||
else if( stat->status & TR_STATUS_SEED )
|
||||
{
|
||||
fTimeString = [NSString stringWithFormat:
|
||||
@"Seeding, uploading to %d of %d peer%s",
|
||||
stat->peersDownloading, stat->peersTotal,
|
||||
( stat->peersTotal == 1 ) ? "" : "s"];
|
||||
}
|
||||
else if( stat->status & TR_STATUS_STOPPING )
|
||||
{
|
||||
fTimeString = @"Stopping...";
|
||||
}
|
||||
|
||||
if( ( stat->status & ( TR_STATUS_DOWNLOAD | TR_STATUS_SEED ) ) &&
|
||||
( stat->status & TR_TRACKER_ERROR ) )
|
||||
{
|
||||
fPeersString = [NSString stringWithFormat: @"%@%@",
|
||||
@"Error: ", [NSString stringWithUTF8String: stat->error]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) drawWithFrame: (NSRect) cellFrame inView: (NSView *) view
|
||||
{
|
||||
NSString * string;
|
||||
NSPoint pen;
|
||||
NSMutableDictionary * attributes;
|
||||
|
||||
if( ![view lockFocusIfCanDraw] )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pen = cellFrame.origin;
|
||||
float cellWidth = cellFrame.size.width;
|
||||
|
||||
pen.x += 5;
|
||||
pen.y += 5;
|
||||
[fCurrentIcon drawAtPoint: pen fromRect:
|
||||
NSMakeRect(0,0,[fCurrentIcon size].width,[fCurrentIcon size].height)
|
||||
operation: NSCompositeSourceOver fraction: 1.0];
|
||||
|
||||
attributes = [NSMutableDictionary dictionaryWithCapacity: 2];
|
||||
[attributes setObject: fWhiteText ? [NSColor whiteColor] :
|
||||
[NSColor blackColor] forKey: NSForegroundColorAttributeName];
|
||||
|
||||
[attributes setObject: [NSFont messageFontOfSize: 12.0]
|
||||
forKey: NSFontAttributeName];
|
||||
|
||||
pen.x += 37;
|
||||
string = [[fNameString stringFittingInWidth: cellWidth -
|
||||
72 - [fSizeString sizeWithAttributes: attributes].width
|
||||
withAttributes: attributes] stringByAppendingString: fSizeString];
|
||||
[string drawAtPoint: pen withAttributes: attributes];
|
||||
|
||||
[attributes setObject: [NSFont messageFontOfSize: 10.0]
|
||||
forKey: NSFontAttributeName];
|
||||
|
||||
pen.x += 5; pen.y += 20;
|
||||
[fTimeString drawAtPoint: pen withAttributes: attributes];
|
||||
|
||||
pen.x += 0; pen.y += 15;
|
||||
string = [fPeersString stringFittingInWidth: cellFrame.size.width -
|
||||
77 withAttributes: attributes];
|
||||
[string drawAtPoint: pen withAttributes: attributes];
|
||||
|
||||
[view unlockFocus];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,5 +1,5 @@
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2005 Eric Petit
|
||||
* Copyright (c) 2005-2006 Transmission authors and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
@@ -21,6 +21,7 @@
|
||||
*****************************************************************************/
|
||||
|
||||
#import "PrefsController.h"
|
||||
#import "StringAdditions.h"
|
||||
#import "Utils.h"
|
||||
|
||||
#define MIN_PORT 1
|
||||
@@ -202,7 +203,7 @@
|
||||
|
||||
//if value entered is not an int or is not in range do not change
|
||||
if (![[fPortField stringValue] isEqualToString:
|
||||
[NSString stringWithFormat: @"%d", bindPort]]
|
||||
[NSString stringWithInt: bindPort]]
|
||||
|| bindPort < MIN_PORT
|
||||
|| bindPort > MAX_PORT)
|
||||
{
|
||||
@@ -233,8 +234,7 @@
|
||||
|
||||
//if value entered is not an int or is less than 0 do not change
|
||||
if (![[fUploadField stringValue] isEqualToString:
|
||||
[NSString stringWithFormat: @"%d", uploadLimit]]
|
||||
|| uploadLimit < 0)
|
||||
[NSString stringWithInt: uploadLimit]] || uploadLimit < 0)
|
||||
{
|
||||
NSBeep();
|
||||
uploadLimit = [fDefaults integerForKey: @"UploadLimit"];
|
||||
|
||||
@@ -1,312 +0,0 @@
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2005 Eric Petit
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#import "ProgressCell.h"
|
||||
#import "StringAdditions.h"
|
||||
|
||||
@implementation ProgressCell
|
||||
|
||||
/***********************************************************************
|
||||
* Static tables
|
||||
***********************************************************************
|
||||
* We use these tables to optimize the drawing. They contain packed
|
||||
* RGBA pixels for every color we might need.
|
||||
**********************************************************************/
|
||||
#if 0
|
||||
/* Coefficients for the "3D effect" */
|
||||
static float kBarCoeffs[] =
|
||||
{ 0.49, 0.73, 0.84, 0.85, 0.84, 0.79, 0.78,
|
||||
0.82, 0.86, 0.91, 0.93, 0.95, 0.96, 0.71 };
|
||||
#endif
|
||||
|
||||
/* 255, 100, 80 */
|
||||
static uint32_t kRed[] =
|
||||
{ 0x7C3127FF, 0xBA493AFF, 0xD65443FF, 0xD85544FF, 0xD65443FF,
|
||||
0xC94F3FFF, 0xC64E3EFF, 0xD15241FF, 0xDB5644FF, 0xE85B48FF,
|
||||
0xED5D4AFF, 0xF25F4CFF, 0xF4604CFF, 0xB54738FF };
|
||||
|
||||
/* 160, 220, 255 */
|
||||
static uint32_t kBlue1[] =
|
||||
{ 0x4E6B7CFF, 0x74A0BAFF, 0x86B8D6FF, 0x88BBD8FF, 0x86B8D6FF,
|
||||
0x7EADC9FF, 0x7CABC6FF, 0x83B4D1FF, 0x89BDDBFF, 0x91C8E8FF,
|
||||
0x94CCEDFF, 0x98D1F2FF, 0x99D3F4FF, 0x719CB5FF };
|
||||
|
||||
/* 120, 190, 255 */
|
||||
static uint32_t kBlue2[] =
|
||||
{ 0x3A5D7CFF, 0x578ABAFF, 0x649FD6FF, 0x66A1D8FF, 0x649FD6FF,
|
||||
0x5E96C9FF, 0x5D94C6FF, 0x629BD1FF, 0x67A3DBFF, 0x6DACE8FF,
|
||||
0x6FB0EDFF, 0x72B4F2FF, 0x73B6F4FF, 0x5586B5FF };
|
||||
|
||||
/* 80, 160, 255 */
|
||||
static uint32_t kBlue3[] =
|
||||
{ 0x274E7CFF, 0x3A74BAFF, 0x4386D6FF, 0x4488D8FF, 0x4386D6FF,
|
||||
0x3F7EC9FF, 0x3E7CC6FF, 0x4183D1FF, 0x4489DBFF, 0x4891E8FF,
|
||||
0x4A94EDFF, 0x4C98F2FF, 0x4C99F4FF, 0x3871B5FF };
|
||||
|
||||
/* 30, 70, 180 */
|
||||
static uint32_t kBlue4[] =
|
||||
{ 0x0E2258FF, 0x153383FF, 0x193A97FF, 0x193B99FF, 0x193A97FF,
|
||||
0x17378EFF, 0x17368CFF, 0x183993FF, 0x193C9AFF, 0x1B3FA3FF,
|
||||
0x1B41A7FF, 0x1C42ABFF, 0x1C43ACFF, 0x15317FFF };
|
||||
|
||||
/* 130, 130, 130 */
|
||||
static uint32_t kGray[] =
|
||||
{ 0x3F3F3FFF, 0x5E5E5EFF, 0x6D6D6DFF, 0x6E6E6EFF, 0x6D6D6DFF,
|
||||
0x666666FF, 0x656565FF, 0x6A6A6AFF, 0x6F6F6FFF, 0x767676FF,
|
||||
0x787878FF, 0x7B7B7BFF, 0x7C7C7CFF, 0x5C5C5CFF };
|
||||
|
||||
/* 0, 255, 0 */
|
||||
static uint32_t kGreen[] =
|
||||
{ 0x007C00FF, 0x00BA00FF, 0x00D600FF, 0x00D800FF, 0x00D600FF,
|
||||
0x00C900FF, 0x00C600FF, 0x00D100FF, 0x00DB00FF, 0x00E800FF,
|
||||
0x00ED00FF, 0x00F200FF, 0x00F400FF, 0x00B500FF };
|
||||
|
||||
/***********************************************************************
|
||||
* init
|
||||
***********************************************************************
|
||||
* Prepares the NSBitmapImageReps we are going to need in order to
|
||||
* draw.
|
||||
**********************************************************************/
|
||||
- (id) init
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
/* Load the background image for the progress bar and get it as a
|
||||
32-bit bitmap */
|
||||
fBackgroundBmp = [[[NSImage imageNamed: @"Progress.png"]
|
||||
representations] objectAtIndex: 0];
|
||||
|
||||
/* Allocate another bitmap of the same size. We will draw the
|
||||
progress bar in it */
|
||||
fProgressBmp = [[NSBitmapImageRep alloc]
|
||||
initWithBitmapDataPlanes: NULL pixelsWide:
|
||||
[fBackgroundBmp size].width pixelsHigh:
|
||||
[fBackgroundBmp size].height bitsPerSample: 8
|
||||
samplesPerPixel: 4 hasAlpha: YES isPlanar: NO
|
||||
colorSpaceName: NSCalibratedRGBColorSpace
|
||||
bytesPerRow: 0 bitsPerPixel: 0];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* setStat
|
||||
***********************************************************************
|
||||
* Readies ourselves to draw updated info.
|
||||
**********************************************************************/
|
||||
- (void) setStat: (tr_stat_t *) stat whiteText: (BOOL) w
|
||||
{
|
||||
int i;
|
||||
uint8_t * in, * out;
|
||||
|
||||
fStat = stat;
|
||||
fWhiteText = w;
|
||||
|
||||
/* Update the strings to be displayed */
|
||||
if( fStat->progress == 1.0 )
|
||||
fDlString = [@"Ratio: " stringByAppendingString:
|
||||
[NSString stringForRatio: fStat->downloaded
|
||||
upload: fStat->uploaded]];
|
||||
else
|
||||
fDlString = [@"DL: " stringByAppendingString:
|
||||
[NSString stringForSpeed: fStat->rateDownload]];
|
||||
fUlString = [@"UL: " stringByAppendingString:
|
||||
[NSString stringForSpeed: fStat->rateUpload]];
|
||||
|
||||
/* Reset our bitmap to the background image... */
|
||||
in = [fBackgroundBmp bitmapData];
|
||||
out = [fProgressBmp bitmapData];
|
||||
for( i = 0; i < [fProgressBmp size].height; i++ )
|
||||
{
|
||||
memcpy( out, in, [fProgressBmp size].width * 4 );
|
||||
in += [fBackgroundBmp bytesPerRow];
|
||||
out += [fProgressBmp bytesPerRow];
|
||||
}
|
||||
|
||||
/* ...and redraw the progress bar on the top of it */
|
||||
if( [[NSUserDefaults standardUserDefaults]
|
||||
boolForKey:@"UseAdvancedBar"])
|
||||
{
|
||||
[self buildAdvancedBar];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self buildSimpleBar];
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* buildSimpleBar
|
||||
**********************************************************************/
|
||||
- (void) buildSimpleBar
|
||||
{
|
||||
int h, w, end, pixelsPerRow;
|
||||
uint32_t * p;
|
||||
uint32_t * colors;
|
||||
|
||||
pixelsPerRow = [fProgressBmp bytesPerRow] / 4;
|
||||
|
||||
/* The background image is 124*18 pixels, but the actual
|
||||
progress bar is 120*14 : the first two columns, the last
|
||||
two columns and the last four lines contain the shadow. */
|
||||
|
||||
p = (uint32_t *) [fProgressBmp bitmapData] + 2;
|
||||
end = lrintf( floor( fStat->progress * 120 ) );
|
||||
|
||||
if( fStat->status & TR_STATUS_SEED )
|
||||
colors = kGreen;
|
||||
else if( fStat->status & ( TR_STATUS_CHECK | TR_STATUS_DOWNLOAD ) )
|
||||
colors = kBlue2;
|
||||
else
|
||||
colors = kGray;
|
||||
|
||||
for( h = 0; h < 14; h++ )
|
||||
{
|
||||
for( w = 0; w < end; w++ )
|
||||
{
|
||||
p[w] = htonl( colors[h] );
|
||||
}
|
||||
p += pixelsPerRow;
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* buildAdvancedBar
|
||||
**********************************************************************/
|
||||
- (void) buildAdvancedBar
|
||||
{
|
||||
int h, w, end, pixelsPerRow;
|
||||
uint32_t * p;
|
||||
uint32_t * colors;
|
||||
|
||||
if( fStat->status & TR_STATUS_SEED )
|
||||
{
|
||||
/* All green, same as the simple bar */
|
||||
[self buildSimpleBar];
|
||||
return;
|
||||
}
|
||||
|
||||
pixelsPerRow = [fProgressBmp bytesPerRow] / 4;
|
||||
|
||||
/* First two lines: dark blue to show progression */
|
||||
p = (uint32_t *) [fProgressBmp bitmapData];
|
||||
p += 2;
|
||||
end = lrintf( floor( fStat->progress * 120 ) );
|
||||
for( h = 0; h < 2; h++ )
|
||||
{
|
||||
for( w = 0; w < end; w++ )
|
||||
{
|
||||
p[w] = htonl( kBlue4[h] );
|
||||
}
|
||||
p += pixelsPerRow;
|
||||
}
|
||||
|
||||
/* Lines 2 to 14: blue or grey depending on whether
|
||||
we have the piece or not */
|
||||
for( w = 0; w < 120; w++ )
|
||||
{
|
||||
/* Point to pixel ( 2 + w, 2 ). We will then draw
|
||||
"vertically" */
|
||||
p = (uint32_t *) ( [fProgressBmp bitmapData] +
|
||||
2 * [fProgressBmp bytesPerRow] );
|
||||
p += 2 + w;
|
||||
|
||||
if( fStat->pieces[w] < 0 )
|
||||
{
|
||||
colors = kGray;
|
||||
}
|
||||
else if( fStat->pieces[w] < 1 )
|
||||
{
|
||||
colors = kRed;
|
||||
}
|
||||
else if( fStat->pieces[w] < 2 )
|
||||
{
|
||||
colors = kBlue1;
|
||||
}
|
||||
else if( fStat->pieces[w] < 3 )
|
||||
{
|
||||
colors = kBlue2;
|
||||
}
|
||||
else
|
||||
{
|
||||
colors = kBlue3;
|
||||
}
|
||||
|
||||
for( h = 2; h < 14; h++ )
|
||||
{
|
||||
p[0] = htonl( colors[h] );
|
||||
p += pixelsPerRow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* drawWithFrame
|
||||
***********************************************************************
|
||||
* We have the strings, we have the bitmap. Let's just draw them where
|
||||
* they belong.
|
||||
**********************************************************************/
|
||||
- (void) drawWithFrame: (NSRect) cellFrame inView: (NSView *) view
|
||||
{
|
||||
NSImage * img;
|
||||
NSMutableDictionary * attributes;
|
||||
NSPoint pen;
|
||||
|
||||
if( ![view lockFocusIfCanDraw] )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pen = cellFrame.origin;
|
||||
|
||||
/* Init an NSImage with our bitmap in order to draw it. We need to
|
||||
do this every time, or for some reason it won't draw if the
|
||||
display is set to thousands of colors when Transmission was
|
||||
started */
|
||||
img = [[NSImage alloc] initWithSize: [fProgressBmp size]];
|
||||
[img addRepresentation: fProgressBmp];
|
||||
[img setFlipped: YES];
|
||||
|
||||
/* Actually draw the bar */
|
||||
pen.x += 5; pen.y += 5;
|
||||
[img drawAtPoint: pen fromRect: NSMakeRect( 0, 0,
|
||||
[fProgressBmp size].width, [fProgressBmp size].height )
|
||||
operation: NSCompositeSourceOver fraction: 1.0];
|
||||
|
||||
[img release];
|
||||
|
||||
/* Draw the strings with font 10 */
|
||||
attributes = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSFont messageFontOfSize: 10.0],
|
||||
NSFontAttributeName,
|
||||
fWhiteText ? [NSColor whiteColor] : [NSColor blackColor],
|
||||
NSForegroundColorAttributeName,
|
||||
NULL];
|
||||
pen.x += 5; pen.y += 20;
|
||||
[fDlString drawAtPoint: pen withAttributes: attributes];
|
||||
pen.x += 0; pen.y += 15;
|
||||
[fUlString drawAtPoint: pen withAttributes: attributes];
|
||||
|
||||
[view unlockFocus];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,15 +1,30 @@
|
||||
//
|
||||
// StringAdditions.h
|
||||
// Transmission
|
||||
//
|
||||
// Created by Mitchell Livingston on 1/16/06.
|
||||
// Copyright 2006 __MyCompanyName__. All rights reserved.
|
||||
//
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2005-2006 Transmission authors and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface NSString (StringAdditions)
|
||||
|
||||
+ (NSString *) stringWithInt: (int) value;
|
||||
+ (NSString *) stringForFileSize: (uint64_t) size;
|
||||
+ (NSString *) stringForSpeed: (float) speed;
|
||||
+ (NSString *) stringForSpeedAbbrev: (float) speed;
|
||||
|
||||
@@ -1,16 +1,35 @@
|
||||
//
|
||||
// StringAdditions.m
|
||||
// Transmission
|
||||
//
|
||||
// Created by Mitchell Livingston on 1/16/06.
|
||||
// Copyright 2006 __MyCompanyName__. All rights reserved.
|
||||
//
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2005-2006 Transmission authors and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#import "StringAdditions.h"
|
||||
#import "Utils.h"
|
||||
|
||||
@implementation NSString (StringAdditions)
|
||||
|
||||
+ (NSString *) stringWithInt: (int) value
|
||||
{
|
||||
return [NSString stringWithFormat: @"%d", value];
|
||||
}
|
||||
|
||||
+ (NSString *) stringForFileSize: (uint64_t) size
|
||||
{
|
||||
if (size < 1024)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2005 Eric Petit
|
||||
* Copyright (c) 2005-2006 Transmission authors and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
@@ -20,28 +20,51 @@
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef PROGRESSCELL_H
|
||||
#define PROGRESSCELL_H
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <transmission.h>
|
||||
|
||||
@interface ProgressCell : NSCell
|
||||
@interface Torrent : NSObject
|
||||
{
|
||||
tr_stat_t * fStat;
|
||||
BOOL fWhiteText;
|
||||
tr_handle_t * fLib;
|
||||
tr_torrent_t * fHandle;
|
||||
tr_info_t * fInfo;
|
||||
tr_stat_t * fStat;
|
||||
BOOL fResumeOnWake;
|
||||
|
||||
NSString * fDlString;
|
||||
NSString * fUlString;
|
||||
|
||||
NSBitmapImageRep * fBackgroundBmp;
|
||||
NSBitmapImageRep * fProgressBmp;
|
||||
NSImage * fIcon;
|
||||
NSMutableString * fStatusString;
|
||||
NSMutableString * fInfoString;
|
||||
NSMutableString * fDownloadString;
|
||||
NSMutableString * fUploadString;
|
||||
}
|
||||
- (id) init;
|
||||
- (void) setStat: (tr_stat_t *) stat whiteText: (BOOL) w;
|
||||
- (void) buildSimpleBar;
|
||||
- (void) buildAdvancedBar;
|
||||
- (void) drawWithFrame: (NSRect) cellFrame inView: (NSView *) view;
|
||||
@end
|
||||
|
||||
#endif
|
||||
- (id) initWithPath: (NSString *) path lib: (tr_handle_t *) lib;
|
||||
- (void) setFolder: (NSString *) path;
|
||||
- (NSString *) getFolder;
|
||||
- (void) getAvailability: (int8_t *) tab size: (int) size;
|
||||
|
||||
- (void) update;
|
||||
- (void) start;
|
||||
- (void) stop;
|
||||
- (void) sleep;
|
||||
- (void) wakeUp;
|
||||
- (void) reveal;
|
||||
- (void) trashTorrent;
|
||||
- (void) trashData;
|
||||
|
||||
- (NSImage *) icon;
|
||||
- (NSString *) path;
|
||||
- (NSString *) name;
|
||||
- (uint64_t) size;
|
||||
|
||||
- (float) progress;
|
||||
- (BOOL) isActive;
|
||||
- (BOOL) isSeeding;
|
||||
- (BOOL) isPaused;
|
||||
- (BOOL) justFinished;
|
||||
- (NSString *) statusString;
|
||||
- (NSString *) infoString;
|
||||
- (NSString *) downloadString;
|
||||
- (NSString *) uploadString;
|
||||
|
||||
@end
|
||||
319
macosx/Torrent.m
Normal file
319
macosx/Torrent.m
Normal file
@@ -0,0 +1,319 @@
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2005-2006 Transmission authors and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#import "Torrent.h"
|
||||
#import "StringAdditions.h"
|
||||
|
||||
@interface Torrent (Private)
|
||||
|
||||
- (void) trashPath: (NSString *) path;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation Torrent
|
||||
|
||||
- (id) initWithPath: (NSString *) path lib: (tr_handle_t *) lib
|
||||
{
|
||||
fLib = lib;
|
||||
|
||||
int error;
|
||||
fHandle = tr_torrentInit( fLib, [path UTF8String], &error );
|
||||
if( !fHandle )
|
||||
{
|
||||
[self release];
|
||||
return nil;
|
||||
}
|
||||
|
||||
fInfo = tr_torrentInfo( fHandle );
|
||||
|
||||
NSString * fileType = ( fInfo->fileCount > 1 ) ?
|
||||
NSFileTypeForHFSTypeCode('fldr') : [[self name] pathExtension];
|
||||
fIcon = [[NSWorkspace sharedWorkspace] iconForFileType: fileType];
|
||||
[fIcon setFlipped: YES];
|
||||
[fIcon retain];
|
||||
|
||||
fStatusString = [[NSMutableString alloc] initWithCapacity: 50];
|
||||
fInfoString = [[NSMutableString alloc] initWithCapacity: 50];
|
||||
fDownloadString = [[NSMutableString alloc] initWithCapacity: 10];
|
||||
fUploadString = [[NSMutableString alloc] initWithCapacity: 10];
|
||||
|
||||
[self update];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
if( fHandle )
|
||||
{
|
||||
tr_torrentClose( fLib, fHandle );
|
||||
[fIcon release];
|
||||
[fStatusString release];
|
||||
[fInfoString release];
|
||||
[fDownloadString release];
|
||||
[fUploadString release];
|
||||
}
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void) setFolder: (NSString *) path
|
||||
{
|
||||
tr_torrentSetFolder( fHandle, [path UTF8String] );
|
||||
}
|
||||
|
||||
- (NSString *) getFolder
|
||||
{
|
||||
return [NSString stringWithUTF8String: tr_torrentGetFolder( fHandle )];
|
||||
}
|
||||
|
||||
- (void) getAvailability: (int8_t *) tab size: (int) size
|
||||
{
|
||||
tr_torrentAvailability( fHandle, tab, size );
|
||||
}
|
||||
|
||||
- (void) update
|
||||
{
|
||||
fStat = tr_torrentStat( fHandle );
|
||||
|
||||
[fStatusString setString: @""];
|
||||
[fInfoString setString: @""];
|
||||
|
||||
switch( fStat->status )
|
||||
{
|
||||
case TR_STATUS_PAUSE:
|
||||
[fStatusString appendFormat: @"Paused (%.2f %%)",
|
||||
100 * fStat->progress];
|
||||
break;
|
||||
|
||||
case TR_STATUS_CHECK:
|
||||
[fStatusString appendFormat:
|
||||
@"Checking existing files (%.2f %%)",
|
||||
100 * fStat->progress];
|
||||
break;
|
||||
|
||||
case TR_STATUS_DOWNLOAD:
|
||||
if( fStat->eta < 0 )
|
||||
{
|
||||
[fStatusString appendFormat:
|
||||
@"Finishing in --:--:-- (%.2f %%)",
|
||||
100 * fStat->progress];
|
||||
}
|
||||
else
|
||||
{
|
||||
[fStatusString appendFormat:
|
||||
@"Finishing in %02d:%02d:%02d (%.2f %%)",
|
||||
fStat->eta / 3600, ( fStat->eta / 60 ) % 60,
|
||||
fStat->eta % 60, 100 * fStat->progress];
|
||||
}
|
||||
[fInfoString appendFormat:
|
||||
@"Downloading from %d of %d peer%s",
|
||||
fStat->peersUploading, fStat->peersTotal,
|
||||
( fStat->peersTotal == 1 ) ? "" : "s"];
|
||||
break;
|
||||
|
||||
case TR_STATUS_SEED:
|
||||
[fStatusString appendFormat:
|
||||
@"Seeding, uploading to %d of %d peer%s",
|
||||
fStat->peersDownloading, fStat->peersTotal,
|
||||
( fStat->peersTotal == 1 ) ? "" : "s"];
|
||||
break;
|
||||
|
||||
case TR_STATUS_STOPPING:
|
||||
[fStatusString setString: @"Stopping..."];
|
||||
break;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if( ( stat->status & ( TR_STATUS_DOWNLOAD | TR_STATUS_SEED ) ) &&
|
||||
( stat->status & TR_TRACKER_ERROR ) )
|
||||
{
|
||||
fPeersString = [NSString stringWithFormat: @"%@%@",
|
||||
@"Error: ", [NSString stringWithUTF8String: stat->error]];
|
||||
}
|
||||
#endif
|
||||
|
||||
[fUploadString setString: @""];
|
||||
if( fStat->progress == 1.0 )
|
||||
{
|
||||
[fDownloadString setString: @"Ratio: "];
|
||||
[fDownloadString appendString: [NSString stringForRatio:
|
||||
fStat->downloaded upload: fStat->uploaded]];
|
||||
}
|
||||
else
|
||||
{
|
||||
[fDownloadString setString: @"DL: "];
|
||||
[fDownloadString appendString: [NSString stringForSpeed:
|
||||
fStat->rateDownload]];
|
||||
}
|
||||
[fUploadString setString: @"UL: "];
|
||||
[fUploadString appendString: [NSString stringForSpeed:
|
||||
fStat->rateUpload]];
|
||||
}
|
||||
|
||||
- (void) start
|
||||
{
|
||||
if( fStat->status & TR_STATUS_INACTIVE )
|
||||
{
|
||||
tr_torrentStart( fHandle );
|
||||
}
|
||||
}
|
||||
|
||||
- (void) stop
|
||||
{
|
||||
if( fStat->status & TR_STATUS_ACTIVE )
|
||||
{
|
||||
tr_torrentStop( fHandle );
|
||||
}
|
||||
}
|
||||
|
||||
- (void) sleep
|
||||
{
|
||||
if( fStat->status & TR_STATUS_ACTIVE )
|
||||
{
|
||||
[self stop];
|
||||
fResumeOnWake = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
fResumeOnWake = NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void) wakeUp
|
||||
{
|
||||
if( fResumeOnWake )
|
||||
{
|
||||
[self start];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) reveal
|
||||
{
|
||||
NSString * path = [NSString stringWithFormat: @"%@/%@",
|
||||
[self getFolder], [self name]];
|
||||
NSURL * url = [NSURL fileURLWithPath: path];
|
||||
|
||||
[[NSWorkspace sharedWorkspace] selectFile: [url path]
|
||||
inFileViewerRootedAtPath: nil];
|
||||
}
|
||||
|
||||
- (void) trashTorrent
|
||||
{
|
||||
[self trashPath: [self path]];
|
||||
}
|
||||
|
||||
- (void) trashData
|
||||
{
|
||||
[self trashPath: [NSString stringWithFormat: @"%@/%@",
|
||||
[self getFolder], [self name]]];
|
||||
}
|
||||
|
||||
- (NSImage *) icon
|
||||
{
|
||||
return fIcon;
|
||||
}
|
||||
|
||||
- (NSString *) path
|
||||
{
|
||||
return [NSString stringWithUTF8String: fInfo->torrent];
|
||||
}
|
||||
|
||||
- (NSString *) name
|
||||
{
|
||||
return [NSString stringWithUTF8String: fInfo->name];
|
||||
}
|
||||
|
||||
- (uint64_t) size
|
||||
{
|
||||
return fInfo->totalSize;
|
||||
}
|
||||
|
||||
- (float) progress
|
||||
{
|
||||
return fStat->progress;
|
||||
}
|
||||
|
||||
- (BOOL) isActive
|
||||
{
|
||||
return ( fStat->status & TR_STATUS_ACTIVE );
|
||||
}
|
||||
|
||||
- (BOOL) isSeeding
|
||||
{
|
||||
return ( fStat->status == TR_STATUS_SEED );
|
||||
}
|
||||
|
||||
- (BOOL) isPaused
|
||||
{
|
||||
return ( fStat->status == TR_STATUS_PAUSE );
|
||||
}
|
||||
|
||||
- (BOOL) justFinished
|
||||
{
|
||||
return tr_getFinished( fHandle );
|
||||
}
|
||||
|
||||
- (NSString *) statusString
|
||||
{
|
||||
return fStatusString;
|
||||
}
|
||||
|
||||
- (NSString *) infoString
|
||||
{
|
||||
return fInfoString;
|
||||
}
|
||||
|
||||
- (NSString *) downloadString
|
||||
{
|
||||
return fDownloadString;
|
||||
}
|
||||
|
||||
- (NSString *) uploadString
|
||||
{
|
||||
return fUploadString;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation Torrent (Private)
|
||||
|
||||
- (void) trashPath: (NSString *) path
|
||||
{
|
||||
NSString * string;
|
||||
NSAppleScript * appleScript;
|
||||
NSDictionary * error;
|
||||
|
||||
string = [NSString stringWithFormat:
|
||||
@"tell application \"Finder\"\n"
|
||||
" move (POSIX file \"%@\") to trash\n"
|
||||
"end tell", path];
|
||||
|
||||
appleScript = [[NSAppleScript alloc] initWithSource: string];
|
||||
if( ![appleScript executeAndReturnError: &error] )
|
||||
{
|
||||
printf( "trashPath failed\n" );
|
||||
}
|
||||
[appleScript release];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,5 +1,5 @@
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2005 Eric Petit
|
||||
* Copyright (c) 2005-2006 Transmission authors and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
@@ -20,26 +20,23 @@
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef NAMECELL_H
|
||||
#define NAMECELL_H
|
||||
#ifndef TORRENTCELL_H
|
||||
#define TORRENTCELL_H
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <transmission.h>
|
||||
#import "Controller.h"
|
||||
#import "Torrent.h"
|
||||
|
||||
@interface NameCell : NSCell
|
||||
@interface TorrentCell : NSCell
|
||||
{
|
||||
BOOL fWhiteText;
|
||||
Torrent * fTorrent;
|
||||
NSColor * fTextColor;
|
||||
|
||||
NSString * fNameString;
|
||||
NSString * fSizeString;
|
||||
NSString * fTimeString;
|
||||
NSString * fPeersString;
|
||||
|
||||
NSMutableDictionary * fIcons;
|
||||
NSImage * fCurrentIcon;
|
||||
NSBitmapImageRep * fBitmap;
|
||||
int fWidth;
|
||||
int8_t * fPieces;
|
||||
}
|
||||
- (void) setStat: (tr_stat_t *) stat whiteText: (BOOL) w;
|
||||
- (void) setTorrent: (Torrent *) torrent;
|
||||
- (void) setTextColor: (NSColor *) color;
|
||||
@end
|
||||
|
||||
#endif
|
||||
329
macosx/TorrentCell.m
Normal file
329
macosx/TorrentCell.m
Normal file
@@ -0,0 +1,329 @@
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2005-2006 Transmission authors and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#import "TorrentCell.h"
|
||||
#import "StringAdditions.h"
|
||||
|
||||
#define BAR_HEIGHT 12
|
||||
|
||||
@implementation TorrentCell
|
||||
|
||||
/***********************************************************************
|
||||
* Static tables
|
||||
***********************************************************************
|
||||
* We use these tables to optimize the drawing. They contain packed
|
||||
* RGBA pixels for every color we might need.
|
||||
**********************************************************************/
|
||||
|
||||
static uint32_t kBorder[] =
|
||||
{ 0x00000005, 0x00000010, 0x00000015, 0x00000015,
|
||||
0x00000015, 0x00000015, 0x00000015, 0x00000015,
|
||||
0x00000015, 0x00000015, 0x00000010, 0x00000005 };
|
||||
|
||||
static uint32_t kBack[] =
|
||||
{ 0xB4B4B4FF, 0xE3E3E3FF, 0xE8E8E8FF, 0xDEDEDEFF,
|
||||
0xDBDBDBFF, 0xE5E5E5FF, 0xE7E7E7FF, 0xF5F5F5FF,
|
||||
0xFAFAFAFF, 0xDEDEDEFF, 0x0000003F, 0x00000015 };
|
||||
|
||||
/* Coefficients for the "3D effect":
|
||||
0.59, 0.91, 0.97, 0.92, 0.79, 0.76, 0.85, 0.93, 1.00, 0.99 */
|
||||
|
||||
/* 255, 100, 80 */
|
||||
static uint32_t kRed[] =
|
||||
{ 0x963A2FFF, 0xE85B48FF, 0xF7614DFF, 0xEA5C49FF,
|
||||
0xC94F3FFF, 0xC14C3CFF, 0xD85544FF, 0xED5D4AFF,
|
||||
0xFF6450FF, 0xFC634FFF, 0x0000003F, 0x00000015 };
|
||||
|
||||
/* 160, 220, 255 */
|
||||
static uint32_t kBlue1[] =
|
||||
{ 0x5E8196FF, 0x91C8E8FF, 0x9BD5F7FF, 0x93CAEAFF,
|
||||
0x7EADC9FF, 0x79A7C1FF, 0x88BBD8FF, 0x94CCEDFF,
|
||||
0xA0DCFFFF, 0x9ED9FCFF, 0x0000003F, 0x00000015 };
|
||||
|
||||
/* 120, 190, 255 */
|
||||
static uint32_t kBlue2[] =
|
||||
{ 0x467096FF, 0x6DACE8FF, 0x74B8F7FF, 0x6EAEEAFF,
|
||||
0x5E96C9FF, 0x5B90C1FF, 0x66A1D8FF, 0x6FB0EDFF,
|
||||
0x78BEFFFF, 0x76BCFCFF, 0x0000003F, 0x00000015 };
|
||||
|
||||
/* 80, 160, 255 */
|
||||
static uint32_t kBlue3[] =
|
||||
{ 0x2F5E96FF, 0x4891E8FF, 0x4D9BF7FF, 0x4993EAFF,
|
||||
0x3F7EC9FF, 0x3C79C1FF, 0x4488D8FF, 0x4A94EDFF,
|
||||
0x50A0FFFF, 0x4F9EFCFF, 0x0000003F, 0x00000015 };
|
||||
|
||||
/* 30, 70, 180 */
|
||||
static uint32_t kBlue4[] =
|
||||
{ 0x11296AFF, 0x1B3FA3FF, 0x1D43AEFF, 0x1B40A5FF,
|
||||
0x17378EFF, 0x163588FF, 0x193B99FF, 0x1B41A7FF,
|
||||
0x1E46B4FF, 0x1D45B2FF, 0x0000003F, 0x00000015 };
|
||||
|
||||
/* 130, 130, 130 */
|
||||
static uint32_t kGray[] =
|
||||
{ 0x4C4C4CFF, 0x767676FF, 0x7E7E7EFF, 0x777777FF,
|
||||
0x666666FF, 0x626262FF, 0x6E6E6EFF, 0x787878FF,
|
||||
0x828282FF, 0x808080FF, 0x0000003F, 0x00000015 };
|
||||
|
||||
/* 0, 255, 0 */
|
||||
static uint32_t kGreen[] =
|
||||
{ 0x009600FF, 0x00E800FF, 0x00F700FF, 0x00EA00FF,
|
||||
0x00C900FF, 0x00C100FF, 0x00D800FF, 0x00ED00FF,
|
||||
0x00FF00FF, 0x00FC00FF, 0x0000003F, 0x00000015 };
|
||||
|
||||
- (void) setTorrent: (Torrent *) torrent
|
||||
{
|
||||
fTorrent = torrent;
|
||||
}
|
||||
|
||||
- (void) setTextColor: (NSColor *) color
|
||||
{
|
||||
fTextColor = color;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* init
|
||||
***********************************************************************
|
||||
* Prepares the NSBitmapImageReps we are going to need in order to
|
||||
* draw.
|
||||
**********************************************************************/
|
||||
- (id) init
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* buildSimpleBar
|
||||
**********************************************************************/
|
||||
- (void) buildSimpleBar
|
||||
{
|
||||
int h, w, end, pixelsPerRow;
|
||||
uint32_t * p;
|
||||
uint32_t * colors;
|
||||
|
||||
pixelsPerRow = [fBitmap bytesPerRow] / 4;
|
||||
|
||||
p = (uint32_t *) [fBitmap bitmapData] + 1;
|
||||
end = lrintf( floor( [fTorrent progress] * ( fWidth - 2 ) ) );
|
||||
|
||||
if( [fTorrent isSeeding] )
|
||||
colors = kGreen;
|
||||
else if( [fTorrent isActive] )
|
||||
colors = kBlue2;
|
||||
else
|
||||
colors = kGray;
|
||||
|
||||
for( h = 0; h < BAR_HEIGHT; h++ )
|
||||
{
|
||||
for( w = 0; w < end; w++ )
|
||||
{
|
||||
p[w] = htonl( colors[h] );
|
||||
}
|
||||
for( w = end; w < fWidth - 2; w++ )
|
||||
{
|
||||
p[w] = htonl( kBack[h] );
|
||||
}
|
||||
p += pixelsPerRow;
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* buildAdvancedBar
|
||||
**********************************************************************/
|
||||
- (void) buildAdvancedBar
|
||||
{
|
||||
int h, w, end, pixelsPerRow;
|
||||
uint32_t * p, * colors;
|
||||
uint8_t * bitmapData = [fBitmap bitmapData];
|
||||
int bytesPerRow = [fBitmap bytesPerRow];
|
||||
|
||||
fPieces = malloc( fWidth );
|
||||
[fTorrent getAvailability: fPieces size: fWidth];
|
||||
|
||||
if( [fTorrent isSeeding] )
|
||||
{
|
||||
/* All green, same as the simple bar */
|
||||
[self buildSimpleBar];
|
||||
return;
|
||||
}
|
||||
|
||||
pixelsPerRow = [fBitmap bytesPerRow] / 4;
|
||||
|
||||
/* First two lines: dark blue to show progression */
|
||||
end = lrintf( floor( [fTorrent progress] * ( fWidth - 2 ) ) );
|
||||
for( h = 0; h < 2; h++ )
|
||||
{
|
||||
p = (uint32_t *) ( bitmapData + h * bytesPerRow ) + 1;
|
||||
for( w = 0; w < end; w++ )
|
||||
{
|
||||
p[w] = htonl( kBlue4[h] );
|
||||
}
|
||||
for( w = end; w < fWidth - 2; w++ )
|
||||
{
|
||||
p[w] = htonl( kBack[h] );
|
||||
}
|
||||
}
|
||||
|
||||
/* Lines 2 to 14: blue or grey depending on whether
|
||||
we have the piece or not */
|
||||
for( w = 0; w < fWidth - 2; w++ )
|
||||
{
|
||||
/* Point to pixel ( 2 + w, 2 ). We will then draw
|
||||
"vertically" */
|
||||
p = (uint32_t *) ( bitmapData + 2 * bytesPerRow ) + 1 + w;
|
||||
|
||||
if( fPieces[w] < 0 )
|
||||
{
|
||||
colors = kGray;
|
||||
}
|
||||
else if( fPieces[w] < 1 )
|
||||
{
|
||||
colors = kRed;
|
||||
}
|
||||
else if( fPieces[w] < 2 )
|
||||
{
|
||||
colors = kBlue1;
|
||||
}
|
||||
else if( fPieces[w] < 3 )
|
||||
{
|
||||
colors = kBlue2;
|
||||
}
|
||||
else
|
||||
{
|
||||
colors = kBlue3;
|
||||
}
|
||||
|
||||
for( h = 2; h < BAR_HEIGHT; h++ )
|
||||
{
|
||||
p[0] = htonl( colors[h] );
|
||||
p = (uint32_t *) ( (uint8_t *) p + bytesPerRow );
|
||||
}
|
||||
}
|
||||
|
||||
free( fPieces );
|
||||
}
|
||||
|
||||
- (void) buildBar
|
||||
{
|
||||
int h;
|
||||
uint32_t * p;
|
||||
|
||||
/* Left and right borders */
|
||||
p = (uint32_t *) [fBitmap bitmapData];
|
||||
for( h = 0; h < BAR_HEIGHT; h++ )
|
||||
{
|
||||
p[0] = htonl( kBorder[h] );
|
||||
p[fWidth - 1] = htonl( kBorder[h] );
|
||||
p += [fBitmap bytesPerRow] / 4;
|
||||
}
|
||||
|
||||
/* ...and redraw the progress bar on the top of it */
|
||||
if( [[NSUserDefaults standardUserDefaults]
|
||||
boolForKey:@"UseAdvancedBar"])
|
||||
{
|
||||
[self buildAdvancedBar];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self buildSimpleBar];
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* drawWithFrame
|
||||
***********************************************************************
|
||||
* We have the strings, we have the bitmap. Let's just draw them where
|
||||
* they belong.
|
||||
**********************************************************************/
|
||||
- (void) drawWithFrame: (NSRect) cellFrame inView: (NSView *) view
|
||||
{
|
||||
if( ![view lockFocusIfCanDraw] )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NSMutableDictionary * attributes;
|
||||
attributes = [NSMutableDictionary dictionaryWithCapacity: 2];
|
||||
[attributes setObject: fTextColor
|
||||
forKey: NSForegroundColorAttributeName];
|
||||
|
||||
NSPoint pen = cellFrame.origin;
|
||||
|
||||
/* Draw the icon */
|
||||
pen.x += 5; pen.y += 10;
|
||||
NSImage * icon = [fTorrent icon];
|
||||
[icon drawAtPoint: pen fromRect:
|
||||
NSMakeRect( 0, 0, [icon size].width, [icon size].height )
|
||||
operation: NSCompositeSourceOver fraction: 1.0];
|
||||
|
||||
NSString * string;
|
||||
fWidth = NSWidth( cellFrame ) - [icon size].width - 15;
|
||||
|
||||
/* Draw file or folder name */
|
||||
pen.x += [icon size].width + 5; pen.y -= 7;
|
||||
[attributes setObject: [NSFont messageFontOfSize: 11]
|
||||
forKey: NSFontAttributeName];
|
||||
NSString * sizeString = [NSString stringWithFormat: @" (%@)",
|
||||
[NSString stringForFileSize: [fTorrent size]]];
|
||||
string = [[[fTorrent name] stringFittingInWidth: fWidth - 50 -
|
||||
[sizeString sizeWithAttributes: attributes].width
|
||||
withAttributes: attributes] stringByAppendingString: sizeString];
|
||||
[string drawAtPoint: pen withAttributes: attributes];
|
||||
|
||||
/* Draw the progress bar */
|
||||
pen.y += 17;
|
||||
fBitmap = [[NSBitmapImageRep alloc]
|
||||
initWithBitmapDataPlanes: nil pixelsWide: fWidth
|
||||
pixelsHigh: BAR_HEIGHT bitsPerSample: 8 samplesPerPixel: 4
|
||||
hasAlpha: YES isPlanar: NO colorSpaceName:
|
||||
NSCalibratedRGBColorSpace bytesPerRow: 0 bitsPerPixel: 0];
|
||||
NSImage * img = [[NSImage alloc] initWithSize: [fBitmap size]];
|
||||
[img addRepresentation: fBitmap];
|
||||
[img setFlipped: YES];
|
||||
[self buildBar];
|
||||
[img drawAtPoint: pen fromRect: NSMakeRect( 0, 0,
|
||||
[img size].width, [img size].height )
|
||||
operation: NSCompositeSourceOver fraction: 1.0];
|
||||
[img release];
|
||||
[fBitmap release];
|
||||
|
||||
/* Status strings */
|
||||
[attributes setObject: [NSFont messageFontOfSize: 9]
|
||||
forKey: NSFontAttributeName];
|
||||
pen.y += BAR_HEIGHT + 2;
|
||||
[[fTorrent statusString] drawAtPoint: pen withAttributes: attributes];
|
||||
pen.y += 13;
|
||||
string = [[fTorrent infoString] stringFittingInWidth:
|
||||
( cellFrame.size.width - 77 ) withAttributes: attributes];
|
||||
[string drawAtPoint: pen withAttributes: attributes];
|
||||
|
||||
/* Rates */
|
||||
pen.x += fWidth - 70; pen.y -= 13;
|
||||
[[fTorrent downloadString] drawAtPoint: pen withAttributes: attributes];
|
||||
pen.y += 13;
|
||||
[[fTorrent uploadString] drawAtPoint: pen withAttributes: attributes];
|
||||
|
||||
[view unlockFocus];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -6,15 +6,14 @@
|
||||
@interface TorrentTableView : NSTableView
|
||||
|
||||
{
|
||||
IBOutlet Controller * fController;
|
||||
IBOutlet Controller * fController;
|
||||
NSArray * fTorrents;
|
||||
|
||||
tr_stat_t * fStat;
|
||||
NSPoint fClickPoint;
|
||||
NSPoint fClickPoint;
|
||||
|
||||
IBOutlet NSMenu * fContextRow;
|
||||
IBOutlet NSMenu * fContextNoRow;
|
||||
}
|
||||
|
||||
- (void) updateUI: (tr_stat_t *) stat;
|
||||
- (void) setTorrents: (NSArray *) torrents;
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,31 +1,49 @@
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2005-2006 Transmission authors and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#import "TorrentTableView.h"
|
||||
#import "Controller.h"
|
||||
#import "Torrent.h"
|
||||
|
||||
@implementation TorrentTableView
|
||||
|
||||
- (void) updateUI: (tr_stat_t *) stat
|
||||
- (void) setTorrents: (NSArray *) torrents
|
||||
{
|
||||
fStat = stat;
|
||||
[self reloadData];
|
||||
fTorrents = torrents;
|
||||
}
|
||||
|
||||
- (void) pauseOrResume: (int) row
|
||||
{
|
||||
if( fStat[row].status & TR_STATUS_PAUSE )
|
||||
Torrent * torrent = [fTorrents objectAtIndex: row];
|
||||
|
||||
if( [torrent isPaused] )
|
||||
{
|
||||
[fController resumeTorrentWithIndex: row];
|
||||
}
|
||||
else if( fStat[row].status & ( TR_STATUS_CHECK |
|
||||
TR_STATUS_DOWNLOAD | TR_STATUS_SEED ) )
|
||||
else if( [torrent isActive] )
|
||||
{
|
||||
[fController stopTorrentWithIndex: row];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) mouseDown: (NSEvent *) e
|
||||
{
|
||||
fClickPoint = [self convertPoint: [e locationInWindow] fromView: NULL];
|
||||
[self display];
|
||||
}
|
||||
else;
|
||||
}
|
||||
|
||||
- (NSRect) pauseRectForRow: (int) row
|
||||
@@ -33,11 +51,10 @@
|
||||
int col;
|
||||
NSRect cellRect, rect;
|
||||
|
||||
col = [self columnWithIdentifier: @"Name"];
|
||||
col = [self columnWithIdentifier: @"Torrent"];
|
||||
cellRect = [self frameOfCellAtColumn: col row: row];
|
||||
rect = NSMakeRect( cellRect.origin.x + cellRect.size.width - 19,
|
||||
cellRect.origin.y + cellRect.size.height - 38,
|
||||
14, 14 );
|
||||
rect = NSMakeRect( cellRect.origin.x + cellRect.size.width - 39,
|
||||
cellRect.origin.y + 3, 14, 14 );
|
||||
|
||||
return rect;
|
||||
}
|
||||
@@ -47,11 +64,10 @@
|
||||
int col;
|
||||
NSRect cellRect, rect;
|
||||
|
||||
col = [self columnWithIdentifier: @"Name"];
|
||||
col = [self columnWithIdentifier: @"Torrent"];
|
||||
cellRect = [self frameOfCellAtColumn: col row: row];
|
||||
rect = NSMakeRect( cellRect.origin.x + cellRect.size.width - 19,
|
||||
cellRect.origin.y + cellRect.size.height - 19,
|
||||
14, 14 );
|
||||
rect = NSMakeRect( cellRect.origin.x + cellRect.size.width - 20,
|
||||
cellRect.origin.y + 3, 14, 14 );
|
||||
|
||||
return rect;
|
||||
}
|
||||
@@ -68,41 +84,60 @@
|
||||
[self rowAtPoint: point]] );
|
||||
}
|
||||
|
||||
|
||||
- (void) mouseDown: (NSEvent *) e
|
||||
{
|
||||
fClickPoint = [self convertPoint: [e locationInWindow] fromView: nil];
|
||||
int row = [self rowAtPoint: fClickPoint];
|
||||
|
||||
if( [e modifierFlags] & NSAlternateKeyMask )
|
||||
{
|
||||
[fController advancedChanged: self];
|
||||
fClickPoint = NSMakePoint( 0, 0 );
|
||||
}
|
||||
else if( ![self pointInPauseRect: fClickPoint] &&
|
||||
![self pointInRevealRect: fClickPoint] )
|
||||
{
|
||||
if( row >= 0 )
|
||||
{
|
||||
[self selectRowIndexes: [NSIndexSet indexSetWithIndex: row]
|
||||
byExtendingSelection: NO];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self deselectAll: self];
|
||||
}
|
||||
}
|
||||
else;
|
||||
|
||||
[self display];
|
||||
}
|
||||
|
||||
- (void) mouseUp: (NSEvent *) e
|
||||
{
|
||||
NSPoint point;
|
||||
int row, col;
|
||||
int row;
|
||||
bool sameRow;
|
||||
Torrent * torrent;
|
||||
|
||||
point = [self convertPoint: [e locationInWindow] fromView: NULL];
|
||||
point = [self convertPoint: [e locationInWindow] fromView: nil];
|
||||
row = [self rowAtPoint: point];
|
||||
col = [self columnAtPoint: point];
|
||||
|
||||
if( row < 0 )
|
||||
{
|
||||
[self deselectAll: NULL];
|
||||
}
|
||||
else if( [self pointInPauseRect: point] )
|
||||
sameRow = row == [self rowAtPoint: fClickPoint];
|
||||
|
||||
if( sameRow && [self pointInPauseRect: point]
|
||||
&& [self pointInPauseRect: fClickPoint] )
|
||||
{
|
||||
[self pauseOrResume: row];
|
||||
}
|
||||
else if( [self pointInRevealRect: point] )
|
||||
else if( sameRow && [self pointInRevealRect: point]
|
||||
&& [self pointInRevealRect: fClickPoint] )
|
||||
{
|
||||
[fController finderReveal: [NSString stringWithFormat:
|
||||
@"%@/%@", [NSString stringWithUTF8String: fStat[row].folder],
|
||||
[NSString stringWithUTF8String: fStat[row].info.name]]];
|
||||
[self display];
|
||||
}
|
||||
else if( row >= 0 && col == [self columnWithIdentifier: @"Progress"]
|
||||
&& ( [e modifierFlags] & NSAlternateKeyMask ) )
|
||||
{
|
||||
[fController advancedChanged: NULL];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self selectRowIndexes: [NSIndexSet indexSetWithIndex: row]
|
||||
byExtendingSelection: NO];
|
||||
torrent = [fTorrents objectAtIndex: row];
|
||||
[torrent reveal];
|
||||
}
|
||||
else;
|
||||
|
||||
[self display];
|
||||
fClickPoint = NSMakePoint( 0, 0 );
|
||||
}
|
||||
|
||||
@@ -111,52 +146,67 @@
|
||||
NSPoint point;
|
||||
int row;
|
||||
|
||||
point = [self convertPoint: [e locationInWindow] fromView: NULL];
|
||||
point = [self convertPoint: [e locationInWindow] fromView: nil];
|
||||
row = [self rowAtPoint: point];
|
||||
|
||||
[self selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
|
||||
|
||||
return row >= 0 ? fContextRow : fContextNoRow;
|
||||
if( row >= 0 )
|
||||
{
|
||||
[self selectRowIndexes: [NSIndexSet indexSetWithIndex: row]
|
||||
byExtendingSelection: NO];
|
||||
return fContextRow;
|
||||
}
|
||||
else
|
||||
{
|
||||
[self deselectAll: self];
|
||||
return fContextNoRow;
|
||||
}
|
||||
}
|
||||
|
||||
- (void) drawRect: (NSRect) r
|
||||
{
|
||||
int i;
|
||||
unsigned i;
|
||||
NSRect rect;
|
||||
NSPoint point;
|
||||
NSImage * image;
|
||||
Torrent * torrent;
|
||||
|
||||
[super drawRect: r];
|
||||
|
||||
for( i = 0; i < [self numberOfRows]; i++ )
|
||||
for( i = 0; i < [fTorrents count]; i++ )
|
||||
{
|
||||
torrent = [fTorrents objectAtIndex: i];
|
||||
rect = [self pauseRectForRow: i];
|
||||
image = NULL;
|
||||
if( fStat[i].status & TR_STATUS_PAUSE )
|
||||
image = nil;
|
||||
|
||||
if( [torrent isPaused] )
|
||||
{
|
||||
image = NSPointInRect( fClickPoint, rect ) ?
|
||||
[NSImage imageNamed: @"ResumeOn.png"] :
|
||||
[NSImage imageNamed: @"ResumeOff.png"];
|
||||
}
|
||||
else if( fStat[i].status &
|
||||
( TR_STATUS_CHECK | TR_STATUS_DOWNLOAD | TR_STATUS_SEED ) )
|
||||
else if( [torrent isActive] )
|
||||
{
|
||||
image = NSPointInRect( fClickPoint, rect ) ?
|
||||
[NSImage imageNamed: @"PauseOn.png"] :
|
||||
[NSImage imageNamed: @"PauseOff.png"];
|
||||
}
|
||||
else;
|
||||
|
||||
if( image )
|
||||
{
|
||||
point = NSMakePoint( rect.origin.x, rect.origin.y + 14 );
|
||||
[image compositeToPoint: point operation: NSCompositeSourceOver];
|
||||
[image setFlipped: YES];
|
||||
[image drawAtPoint: rect.origin fromRect:
|
||||
NSMakeRect( 0, 0, 14, 14 ) operation:
|
||||
NSCompositeSourceOver fraction: 1.0];
|
||||
}
|
||||
|
||||
rect = [self revealRectForRow: i];
|
||||
image = NSPointInRect( fClickPoint, rect ) ?
|
||||
[NSImage imageNamed: @"RevealOn.png"] :
|
||||
[NSImage imageNamed: @"RevealOff.png"];
|
||||
point = NSMakePoint( rect.origin.x, rect.origin.y + 14 );
|
||||
[image compositeToPoint: point operation: NSCompositeSourceOver];
|
||||
[image setFlipped: YES];
|
||||
[image drawAtPoint: rect.origin fromRect:
|
||||
NSMakeRect( 0, 0, 14, 14 ) operation:
|
||||
NSCompositeSourceOver fraction: 1.0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
4D043A7F090AE979009FEDA8 /* TransmissionDocument.icns in Resources */ = {isa = PBXBuildFile; fileRef = 4D043A7E090AE979009FEDA8 /* TransmissionDocument.icns */; };
|
||||
4D096C12089FB4E20091B166 /* NameCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D096C0F089FB4E20091B166 /* NameCell.m */; };
|
||||
4D096C13089FB4E20091B166 /* ProgressCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D096C11089FB4E20091B166 /* ProgressCell.m */; };
|
||||
4D118E1A08CB46B20033958F /* PrefsController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D118E1908CB46B20033958F /* PrefsController.m */; };
|
||||
4D2784370905709500687951 /* Transmission.icns in Resources */ = {isa = PBXBuildFile; fileRef = 4D2784360905709500687951 /* Transmission.icns */; };
|
||||
4D364DA0091FBB2C00377D12 /* TorrentTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D364D9F091FBB2C00377D12 /* TorrentTableView.m */; };
|
||||
@@ -17,12 +15,12 @@
|
||||
4D6DAAC6090CE00500F43C22 /* RevealOff.png in Resources */ = {isa = PBXBuildFile; fileRef = 4D6DAAC4090CE00500F43C22 /* RevealOff.png */; };
|
||||
4D6DAAC7090CE00500F43C22 /* RevealOn.png in Resources */ = {isa = PBXBuildFile; fileRef = 4D6DAAC5090CE00500F43C22 /* RevealOn.png */; };
|
||||
4D752E930913C949008EAAD4 /* Preferences.png in Resources */ = {isa = PBXBuildFile; fileRef = 4D752E920913C949008EAAD4 /* Preferences.png */; };
|
||||
4D813EB508AA43AC00191DB4 /* Progress.png in Resources */ = {isa = PBXBuildFile; fileRef = 4D813EB408AA43AC00191DB4 /* Progress.png */; };
|
||||
4D8CEF91095870E00063BAEA /* Network.png in Resources */ = {isa = PBXBuildFile; fileRef = 4D8CEF90095870E00063BAEA /* Network.png */; };
|
||||
4DA6FDBA0911233800450CB1 /* PauseOn.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DA6FDB80911233800450CB1 /* PauseOn.png */; };
|
||||
4DA6FDBB0911233800450CB1 /* PauseOff.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DA6FDB90911233800450CB1 /* PauseOff.png */; };
|
||||
4DA6FDC5091141AD00450CB1 /* ResumeOff.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DA6FDC3091141AD00450CB1 /* ResumeOff.png */; };
|
||||
4DA6FDC6091141AD00450CB1 /* ResumeOn.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DA6FDC4091141AD00450CB1 /* ResumeOn.png */; };
|
||||
4DCCBB3E09C3D71100D3CABF /* TorrentCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DCCBB3C09C3D71100D3CABF /* TorrentCell.m */; };
|
||||
4DDFDD22099A5D8E00189D81 /* DownloadBadge.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DDFDD20099A5D8E00189D81 /* DownloadBadge.png */; };
|
||||
4DDFDD23099A5D8E00189D81 /* UploadBadge.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DDFDD21099A5D8E00189D81 /* UploadBadge.png */; };
|
||||
4DE5CC9D0980656F00BE280E /* StringAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DE5CC9C0980656F00BE280E /* StringAdditions.m */; };
|
||||
@@ -36,6 +34,7 @@
|
||||
4DF7500C08A103AD007B0D70 /* Open.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500708A103AD007B0D70 /* Open.png */; };
|
||||
4DF7500D08A103AD007B0D70 /* Info.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500808A103AD007B0D70 /* Info.png */; };
|
||||
4DF7500E08A103AD007B0D70 /* Remove.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500908A103AD007B0D70 /* Remove.png */; };
|
||||
4DFBC2DF09C0970D00D5C571 /* Torrent.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DFBC2DE09C0970D00D5C571 /* Torrent.m */; };
|
||||
8D11072A0486CEB800E47090 /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = 29B97318FDCFA39411CA2CEA /* MainMenu.nib */; };
|
||||
8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
|
||||
8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; };
|
||||
@@ -79,10 +78,6 @@
|
||||
29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
|
||||
32CA4F630368D1EE00C91783 /* Transmission_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Transmission_Prefix.pch; sourceTree = "<group>"; };
|
||||
4D043A7E090AE979009FEDA8 /* TransmissionDocument.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = TransmissionDocument.icns; path = Images/TransmissionDocument.icns; sourceTree = "<group>"; };
|
||||
4D096C0E089FB4E20091B166 /* NameCell.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = NameCell.h; sourceTree = "<group>"; };
|
||||
4D096C0F089FB4E20091B166 /* NameCell.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = NameCell.m; sourceTree = "<group>"; };
|
||||
4D096C10089FB4E20091B166 /* ProgressCell.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = ProgressCell.h; sourceTree = "<group>"; };
|
||||
4D096C11089FB4E20091B166 /* ProgressCell.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = ProgressCell.m; sourceTree = "<group>"; };
|
||||
4D118E1808CB46B20033958F /* PrefsController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = PrefsController.h; sourceTree = "<group>"; };
|
||||
4D118E1908CB46B20033958F /* PrefsController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = PrefsController.m; sourceTree = "<group>"; };
|
||||
4D2784360905709500687951 /* Transmission.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = Transmission.icns; path = Images/Transmission.icns; sourceTree = "<group>"; };
|
||||
@@ -92,12 +87,13 @@
|
||||
4D6DAAC4090CE00500F43C22 /* RevealOff.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = RevealOff.png; path = Images/RevealOff.png; sourceTree = "<group>"; };
|
||||
4D6DAAC5090CE00500F43C22 /* RevealOn.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = RevealOn.png; path = Images/RevealOn.png; sourceTree = "<group>"; };
|
||||
4D752E920913C949008EAAD4 /* Preferences.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Preferences.png; path = Images/Preferences.png; sourceTree = "<group>"; };
|
||||
4D813EB408AA43AC00191DB4 /* Progress.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Progress.png; path = Images/Progress.png; sourceTree = "<group>"; };
|
||||
4D8CEF90095870E00063BAEA /* Network.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Network.png; path = Images/Network.png; sourceTree = "<group>"; };
|
||||
4DA6FDB80911233800450CB1 /* PauseOn.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = PauseOn.png; path = Images/PauseOn.png; sourceTree = "<group>"; };
|
||||
4DA6FDB90911233800450CB1 /* PauseOff.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = PauseOff.png; path = Images/PauseOff.png; sourceTree = "<group>"; };
|
||||
4DA6FDC3091141AD00450CB1 /* ResumeOff.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = ResumeOff.png; path = Images/ResumeOff.png; sourceTree = "<group>"; };
|
||||
4DA6FDC4091141AD00450CB1 /* ResumeOn.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = ResumeOn.png; path = Images/ResumeOn.png; sourceTree = "<group>"; };
|
||||
4DCCBB3C09C3D71100D3CABF /* TorrentCell.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = TorrentCell.m; sourceTree = "<group>"; };
|
||||
4DCCBB3D09C3D71100D3CABF /* TorrentCell.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = TorrentCell.h; sourceTree = "<group>"; };
|
||||
4DDFDD20099A5D8E00189D81 /* DownloadBadge.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = DownloadBadge.png; path = Images/DownloadBadge.png; sourceTree = "<group>"; };
|
||||
4DDFDD21099A5D8E00189D81 /* UploadBadge.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = UploadBadge.png; path = Images/UploadBadge.png; sourceTree = "<group>"; };
|
||||
4DE5CC9B0980656F00BE280E /* StringAdditions.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = StringAdditions.h; sourceTree = "<group>"; };
|
||||
@@ -114,6 +110,8 @@
|
||||
4DF7500708A103AD007B0D70 /* Open.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Open.png; path = Images/Open.png; sourceTree = "<group>"; };
|
||||
4DF7500808A103AD007B0D70 /* Info.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Info.png; path = Images/Info.png; sourceTree = "<group>"; };
|
||||
4DF7500908A103AD007B0D70 /* Remove.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Remove.png; path = Images/Remove.png; sourceTree = "<group>"; };
|
||||
4DFBC2DD09C0970D00D5C571 /* Torrent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Torrent.h; sourceTree = "<group>"; };
|
||||
4DFBC2DE09C0970D00D5C571 /* Torrent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Torrent.m; sourceTree = "<group>"; };
|
||||
8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||
8D1107320486CEB800E47090 /* Transmission.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Transmission.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
@@ -135,10 +133,6 @@
|
||||
080E96DDFE201D6D7F000001 /* Classes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4D096C0E089FB4E20091B166 /* NameCell.h */,
|
||||
4D096C0F089FB4E20091B166 /* NameCell.m */,
|
||||
4D096C10089FB4E20091B166 /* ProgressCell.h */,
|
||||
4D096C11089FB4E20091B166 /* ProgressCell.m */,
|
||||
4DF0C5A90899190500DD8943 /* Controller.m */,
|
||||
4DF0C5AA0899190500DD8943 /* Controller.h */,
|
||||
4D118E1808CB46B20033958F /* PrefsController.h */,
|
||||
@@ -149,6 +143,10 @@
|
||||
4DE5CC9C0980656F00BE280E /* StringAdditions.m */,
|
||||
4DE5CCA50980735700BE280E /* Badger.h */,
|
||||
4DE5CCA60980735700BE280E /* Badger.m */,
|
||||
4DFBC2DD09C0970D00D5C571 /* Torrent.h */,
|
||||
4DFBC2DE09C0970D00D5C571 /* Torrent.m */,
|
||||
4DCCBB3D09C3D71100D3CABF /* TorrentCell.h */,
|
||||
4DCCBB3C09C3D71100D3CABF /* TorrentCell.m */,
|
||||
);
|
||||
name = Classes;
|
||||
sourceTree = "<group>";
|
||||
@@ -208,7 +206,6 @@
|
||||
4D2784360905709500687951 /* Transmission.icns */,
|
||||
4D043A7E090AE979009FEDA8 /* TransmissionDocument.icns */,
|
||||
4DF7500808A103AD007B0D70 /* Info.png */,
|
||||
4D813EB408AA43AC00191DB4 /* Progress.png */,
|
||||
4DF7500708A103AD007B0D70 /* Open.png */,
|
||||
4DF7500908A103AD007B0D70 /* Remove.png */,
|
||||
4D6DAAC4090CE00500F43C22 /* RevealOff.png */,
|
||||
@@ -304,7 +301,6 @@
|
||||
4DF7500C08A103AD007B0D70 /* Open.png in Resources */,
|
||||
4DF7500D08A103AD007B0D70 /* Info.png in Resources */,
|
||||
4DF7500E08A103AD007B0D70 /* Remove.png in Resources */,
|
||||
4D813EB508AA43AC00191DB4 /* Progress.png in Resources */,
|
||||
4D2784370905709500687951 /* Transmission.icns in Resources */,
|
||||
4D043A7F090AE979009FEDA8 /* TransmissionDocument.icns in Resources */,
|
||||
4D6DAAC6090CE00500F43C22 /* RevealOff.png in Resources */,
|
||||
@@ -333,12 +329,12 @@
|
||||
files = (
|
||||
8D11072D0486CEB800E47090 /* main.m in Sources */,
|
||||
4DF0C5AB0899190500DD8943 /* Controller.m in Sources */,
|
||||
4D096C12089FB4E20091B166 /* NameCell.m in Sources */,
|
||||
4D096C13089FB4E20091B166 /* ProgressCell.m in Sources */,
|
||||
4D118E1A08CB46B20033958F /* PrefsController.m in Sources */,
|
||||
4D364DA0091FBB2C00377D12 /* TorrentTableView.m in Sources */,
|
||||
4DE5CC9D0980656F00BE280E /* StringAdditions.m in Sources */,
|
||||
4DE5CCA70980735700BE280E /* Badger.m in Sources */,
|
||||
4DFBC2DF09C0970D00D5C571 /* Torrent.m in Sources */,
|
||||
4DCCBB3E09C3D71100D3CABF /* TorrentCell.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
@@ -59,9 +59,10 @@ static void sigHandler ( int signal );
|
||||
|
||||
int main( int argc, char ** argv )
|
||||
{
|
||||
int i, count;
|
||||
tr_handle_t * h;
|
||||
tr_stat_t * s;
|
||||
int i, error;
|
||||
tr_handle_t * h;
|
||||
tr_torrent_t * tor;
|
||||
tr_stat_t * s;
|
||||
|
||||
printf( "Transmission %s - http://transmission.m0k.org/\n\n",
|
||||
VERSION_STRING );
|
||||
@@ -104,7 +105,7 @@ int main( int argc, char ** argv )
|
||||
h = tr_init();
|
||||
|
||||
/* Open and parse torrent file */
|
||||
if( tr_torrentInit( h, torrentPath ) )
|
||||
if( !( tor = tr_torrentInit( h, torrentPath, &error ) ) )
|
||||
{
|
||||
printf( "Failed opening torrent file `%s'\n", torrentPath );
|
||||
goto failed;
|
||||
@@ -112,10 +113,7 @@ int main( int argc, char ** argv )
|
||||
|
||||
if( showInfo )
|
||||
{
|
||||
tr_info_t * info;
|
||||
|
||||
count = tr_torrentStat( h, &s );
|
||||
info = &s[0].info;
|
||||
tr_info_t * info = tr_torrentInfo( tor );
|
||||
|
||||
/* Print torrent info (quite <20> la btshowmetainfo) */
|
||||
printf( "hash: " );
|
||||
@@ -137,7 +135,6 @@ int main( int argc, char ** argv )
|
||||
info->files[i].length );
|
||||
}
|
||||
|
||||
free( s );
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
@@ -145,7 +142,7 @@ int main( int argc, char ** argv )
|
||||
{
|
||||
int seeders, leechers;
|
||||
|
||||
if( tr_torrentScrape( h, 0, &seeders, &leechers ) )
|
||||
if( tr_torrentScrape( tor, &seeders, &leechers ) )
|
||||
{
|
||||
printf( "Scrape failed.\n" );
|
||||
}
|
||||
@@ -162,8 +159,8 @@ int main( int argc, char ** argv )
|
||||
tr_setBindPort( h, bindPort );
|
||||
tr_setUploadLimit( h, uploadLimit );
|
||||
|
||||
tr_torrentSetFolder( h, 0, "." );
|
||||
tr_torrentStart( h, 0 );
|
||||
tr_torrentSetFolder( tor, "." );
|
||||
tr_torrentStart( tor );
|
||||
|
||||
while( !mustDie )
|
||||
{
|
||||
@@ -173,69 +170,64 @@ int main( int argc, char ** argv )
|
||||
|
||||
sleep( 1 );
|
||||
|
||||
count = tr_torrentStat( h, &s );
|
||||
s = tr_torrentStat( tor );
|
||||
|
||||
if( s[0].status & TR_STATUS_CHECK )
|
||||
if( s->status & TR_STATUS_CHECK )
|
||||
{
|
||||
chars = snprintf( string, 80,
|
||||
"Checking files... %.2f %%", 100.0 * s[0].progress );
|
||||
"Checking files... %.2f %%", 100.0 * s->progress );
|
||||
}
|
||||
else if( s[0].status & TR_STATUS_DOWNLOAD )
|
||||
else if( s->status & TR_STATUS_DOWNLOAD )
|
||||
{
|
||||
chars = snprintf( string, 80,
|
||||
"Progress: %.2f %%, %d peer%s, dl from %d (%.2f KB/s), "
|
||||
"ul to %d (%.2f KB/s)", 100.0 * s[0].progress,
|
||||
s[0].peersTotal, ( s[0].peersTotal == 1 ) ? "" : "s",
|
||||
s[0].peersUploading, s[0].rateDownload,
|
||||
s[0].peersDownloading, s[0].rateUpload );
|
||||
"ul to %d (%.2f KB/s)", 100.0 * s->progress,
|
||||
s->peersTotal, ( s->peersTotal == 1 ) ? "" : "s",
|
||||
s->peersUploading, s->rateDownload,
|
||||
s->peersDownloading, s->rateUpload );
|
||||
}
|
||||
else if( s[0].status & TR_STATUS_SEED )
|
||||
else if( s->status & TR_STATUS_SEED )
|
||||
{
|
||||
chars = snprintf( string, 80,
|
||||
"Seeding, uploading to %d of %d peer(s), %.2f KB/s",
|
||||
s[0].peersDownloading, s[0].peersTotal,
|
||||
s[0].rateUpload );
|
||||
s->peersDownloading, s->peersTotal,
|
||||
s->rateUpload );
|
||||
}
|
||||
memset( &string[chars], ' ', 79 - chars );
|
||||
string[79] = '\0';
|
||||
fprintf( stderr, "\r%s", string );
|
||||
|
||||
if( s[0].status & TR_TRACKER_ERROR )
|
||||
if( s->error & TR_ETRACKER )
|
||||
{
|
||||
fprintf( stderr, "\n%s\n", s[0].error );
|
||||
fprintf( stderr, "\n%s\n", s->trackerError );
|
||||
}
|
||||
else if( verboseLevel > 0 )
|
||||
{
|
||||
fprintf( stderr, "\n" );
|
||||
}
|
||||
|
||||
if( tr_getFinished( h, 0 ) )
|
||||
if( tr_getFinished( tor ) )
|
||||
{
|
||||
tr_setFinished( h, 0, 0 );
|
||||
result = system(finishCall);
|
||||
}
|
||||
|
||||
free( s );
|
||||
}
|
||||
fprintf( stderr, "\n" );
|
||||
|
||||
/* Try for 5 seconds to notice the tracker that we are leaving */
|
||||
tr_torrentStop( h, 0 );
|
||||
tr_torrentStop( tor );
|
||||
for( i = 0; i < 10; i++ )
|
||||
{
|
||||
count = tr_torrentStat( h, &s );
|
||||
if( s[0].status & TR_STATUS_PAUSE )
|
||||
s = tr_torrentStat( tor );
|
||||
if( s->status & TR_STATUS_PAUSE )
|
||||
{
|
||||
/* The 'stopped' message was sent */
|
||||
free( s );
|
||||
break;
|
||||
}
|
||||
free( s );
|
||||
usleep( 500000 );
|
||||
}
|
||||
|
||||
cleanup:
|
||||
tr_torrentClose( h, 0 );
|
||||
tr_torrentClose( h, tor );
|
||||
|
||||
failed:
|
||||
tr_close( h );
|
||||
|
||||
Reference in New Issue
Block a user