Update 2005-11-24

This commit is contained in:
Eric Petit
2006-01-12 18:40:47 +00:00
parent 23ef3653e3
commit 835c3284dd
25 changed files with 643 additions and 438 deletions

View File

@@ -8,7 +8,7 @@ Eric Petit <titer@m0k.org>
+ OS X interface
Josh Elsasser <josh@elsasser.org>
+ GTK interface
+ GTK+ interface
Michael Demars <keul@m0k.org>
+ OS X interface

View File

@@ -6,7 +6,6 @@ ObjectHdrs transmissioncli.c : $(TOP)/libtransmission ;
if $(OS) = MACOSX
{
OSXLinks macosx/Frameworks/Growl.framework/Growl ;
OSXInfoPlist macosx/Info.plist : macosx/Info.plist.in ;
OSXBundle Transmission.app : libtransmission.a
macosx/Controller.h
@@ -16,7 +15,6 @@ if $(OS) = MACOSX
macosx/English.lproj/MainMenu.nib/classes.nib
macosx/English.lproj/MainMenu.nib/info.nib
macosx/English.lproj/MainMenu.nib/keyedobjects.nib
macosx/Frameworks/Growl.framework/Growl
macosx/Images/Info.png
macosx/Images/Open.png
macosx/Images/PauseOff.png
@@ -37,6 +35,7 @@ if $(OS) = MACOSX
macosx/main.m
macosx/TorrentTableView.h
macosx/TorrentTableView.m
macosx/Transmission.sdef
macosx/Transmission.xcodeproj/project.pbxproj
macosx/Transmission_Prefix.pch
macosx/Utils.h ;

View File

@@ -8,7 +8,7 @@ if ! $(DEFINES)
VERSION_MAJOR = 0 ;
VERSION_MINOR = 4 ;
# VERSION_STRING = $(VERSION_MAJOR).$(VERSION_MINOR) ;
VERSION_STRING = 0.5-cvs ;
VERSION_STRING = CVS-20051124 ;
DEFINES += VERSION_MAJOR=$(VERSION_MAJOR)
VERSION_MINOR=$(VERSION_MINOR)
@@ -40,18 +40,6 @@ if $(OS) = MACOSX
libtool -static $(>) -o $(<) ;
}
rule OSXLinks
{
}
actions OSXLinks
{
( cd macosx/Frameworks/Growl.framework/ && \
ln -s Versions/Current/Growl && \
ln -s Versions/Current/Headers && \
ln -s Versions/Current/Resources && \
ln -s A Versions/Current )
}
rule OSXInfoPlist
{
Depends $(1) : $(2) ;

View File

@@ -1,6 +1,6 @@
SubDir TOP gtk ;
GTK_SRC = conf.c main.c util.c ;
GTK_SRC = conf.c main.c prefs.c util.c ;
Main transmission-gtk : $(GTK_SRC) ;
LinkLibraries transmission-gtk : libtransmission.a ;

View File

@@ -39,6 +39,7 @@
#include "conf.h"
#include "transmission.h"
#include "util.h"
#define FILE_LOCK "gtk_lock"
#define FILE_PREFS "gtk_prefs"
@@ -159,7 +160,6 @@ cf_loadprefs(char **errstr) {
path, err->message);
goto done;
}
/*g_io_channel_set_encoding(io, NULL, NULL);*/
g_io_channel_set_line_term(io, &term, 1);
err = NULL;
@@ -209,7 +209,10 @@ cf_setpref(const char *name, const char *value, char **errstr) {
g_tree_insert(prefs, g_strdup(name), g_strdup(value));
return writeprefs(errstr);
if(NULL != errstr)
return writeprefs(errstr);
else
return TRUE;
}
struct writeinfo {
@@ -236,9 +239,14 @@ writeprefs(char **errstr) {
goto done;
}
#ifdef NDEBUG
ftruncate(fd, 0);
#else
assert(0 == ftruncate(fd, 0));
#endif
info.err = NULL;
io = g_io_channel_unix_new(fd);
/*g_io_channel_set_encoding(io, NULL, NULL);*/
g_io_channel_set_close_on_unref(io, TRUE);
info.io = io;
@@ -318,7 +326,6 @@ cf_loadstate(char **errstr) {
path, err->message);
goto done;
}
/*g_io_channel_set_encoding(io, NULL, NULL);*/
g_io_channel_set_line_term(io, &term, 1);
err = NULL;
@@ -367,43 +374,46 @@ static char *
getstateval(struct cf_torrentstate *state, char *line) {
char *start, *end;
/* skip any leading whitespace */
while(isspace(*line))
line++;
if(NULL == (start = strchr(line, '=')))
return NULL;
while(isspace(*(++start)))
/* walk over the key, which may be alphanumerics as well as - or _ */
for(start = line; isalnum(*start) || '_' == *start || '-' == *start; start++)
;
if('"' != *start)
/* they key must be immediately followed by an = */
if('=' != *start)
return NULL;
*(start++) = '\0';
/* then the opening quote for the value */
if('"' != *(start++))
return NULL;
for(end = ++start; '\0' != *end && '"' != *end; end++)
/* walk over the value */
for(end = start; '\0' != *end && '"' != *end; end++)
/* skip over escaped quotes */
if('\\' == *end && '\0' != *(end + 1))
end++;
/* make sure we didn't hit the end of the string */
if('"' != *end)
return NULL;
*end = '\0';
if(0 == memcmp(line, "torrent", sizeof("torrent") - 1)) {
state->ts_torrent = g_new(char, end - start + 1);
memcpy(state->ts_torrent, start, end - start);
state->ts_torrent[end - start] = '\0';
}
else if(0 == memcmp(line, "dir", sizeof("dir") - 1)) {
state->ts_directory = g_new(char, end - start + 1);
memcpy(state->ts_directory, start, end - start);
state->ts_directory[end - start] = '\0';
}
else if(0 == memcmp(line, "paused", sizeof("paused") - 1)) {
state->ts_paused = (0 == memcmp("yes", start, end - start));
}
/* if it's a key we recognize then save the data */
if(0 == strcmp(line, "torrent"))
state->ts_torrent = g_strcompress(start);
else if(0 == strcmp(line, "dir"))
state->ts_directory = g_strcompress(start);
else if(0 == strcmp(line, "paused"))
state->ts_paused = strbool(start);
/* return a pointer to just past the end of the value */
return end + 1;
}
/* XXX need to save download directory, also maybe running/stopped state */
gboolean
cf_savestate(int count, tr_stat_t *torrents, char **errstr) {
char *file = g_build_filename(confdir, FILE_STATE, NULL);
@@ -425,11 +435,17 @@ cf_savestate(int count, tr_stat_t *torrents, char **errstr) {
goto done;
}
#ifdef NDEBUG
ftruncate(fd, 0);
#else
assert(0 == ftruncate(fd, 0));
#endif
io = g_io_channel_unix_new(fd);
/* XXX what the hell should I be doing about unicode? */
/*g_io_channel_set_encoding(io, NULL, NULL);*/
g_io_channel_set_close_on_unref(io, TRUE);
/* XXX what the hell should I be doing about unicode? */
err = NULL;
for(ii = 0; ii < count; ii++) {
/* XXX need a better way to query running/stopped state */

View File

@@ -43,6 +43,7 @@ gboolean
cf_loadprefs(char **errstr);
const char *
cf_getpref(const char *name);
/* if errstr is NULL then prefs will not be saved */
gboolean
cf_setpref(const char *name, const char *value, char **errstr);
GList *

View File

@@ -24,17 +24,19 @@
POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/param.h>
#include <time.h>
#include <unistd.h>
#include <gtk/gtk.h>
#include "conf.h"
#include "prefs.h"
#include "transmission.h"
#include "util.h"
@@ -42,7 +44,7 @@
struct cbdata {
tr_handle_t *tr;
GtkWidget *wind;
GtkWindow *wind;
GtkListStore *model;
GtkTreeView *view;
guint timer;
@@ -58,8 +60,6 @@ struct pieces {
char p[120];
};
void
readargs(int argc, char **argv, int *port, int *limit);
void
maketypes(void);
gpointer
@@ -92,7 +92,7 @@ actionclick(GtkWidget *widget, gpointer gdata);
void
makeaddwind(struct cbdata *data);
gboolean
addtorrent(tr_handle_t *tr, GtkWidget *parentwind, const char *torrent,
addtorrent(tr_handle_t *tr, GtkWindow *parentwind, const char *torrent,
const char *dir, gboolean paused);
void
fileclick(GtkWidget *widget, gpointer gdata);
@@ -101,7 +101,7 @@ statusstr(int status);
void
makeinfowind(struct cbdata *data, int index);
gboolean
savetorrents(tr_handle_t *tr, GtkWidget *wind, int count, tr_stat_t *stat);
savetorrents(tr_handle_t *tr, GtkWindow *wind, int count, tr_stat_t *stat);
#define TR_TYPE_PIECES_NAME "tr-type-pieces"
#define TR_TYPE_PIECES ((const GType)tr_type_pieces)
@@ -109,7 +109,7 @@ savetorrents(tr_handle_t *tr, GtkWidget *wind, int count, tr_stat_t *stat);
GType tr_type_pieces;
#define LIST_ACTION "torrent-list-action"
enum listact { ACT_OPEN, ACT_START, ACT_STOP, ACT_DELETE, ACT_INFO };
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 };
@@ -128,6 +128,8 @@ actionitems[] = {
"Remove a torrent from the list", "XXX"},
{4, "Properties", GTK_STOCK_PROPERTIES, ACT_INFO,
"Get additional information for a torrent", "XXX"},
{5, "Preferences", GTK_STOCK_PREFERENCES, ACT_PREF,
"Open preferences dialog", "XXX"},
};
#define CBDATA_PTR "callback-data-pointer"
@@ -135,12 +137,12 @@ int
main(int argc, char **argv) {
GtkWidget *mainwind, *preferr, *stateerr;
char *err;
int port, limit;
tr_handle_t *tr;
GList *saved;
const char *pref;
long intval;
gtk_init(&argc, &argv);
readargs(argc, argv, &port, &limit);
tr = tr_init();
@@ -152,23 +154,22 @@ main(int argc, char **argv) {
stateerr = NULL;
if(!cf_loadprefs(&err)) {
preferr = errmsg(mainwind, "%s", err);
preferr = errmsg(GTK_WINDOW(mainwind), "%s", err);
g_free(err);
}
saved = cf_loadstate(&err);
if(NULL != err) {
stateerr = errmsg(mainwind, "%s", err);
stateerr = errmsg(GTK_WINDOW(mainwind), "%s", err);
g_free(err);
}
/* XXX need to remove port and limit options and make them prefs */
/* XXX need prefs gui */
/* XXX need default save dir pref */
/* set the upload limit */
setlimit(tr);
if(0 != port)
tr_setBindPort(tr, port);
if(0 != limit)
tr_setUploadLimit(tr, limit);
/* set the listening port */
if(NULL != (pref = cf_getpref(PREF_PORT)) &&
0 < (intval = strtol(pref, NULL, 10)) && 0xffff >= intval)
tr_setBindPort(tr, intval);
maketypes();
makewind(mainwind, tr, saved);
@@ -178,11 +179,13 @@ main(int argc, char **argv) {
if(NULL != stateerr)
gtk_widget_show_all(stateerr);
} else {
errmsg_full(NULL, (errfunc_t)gtk_main_quit, NULL, "%s", err);
gtk_widget_show(errmsg_full(NULL, (errfunc_t)gtk_main_quit,
NULL, "%s", err));
g_free(err);
}
} else {
errmsg_full(NULL, (errfunc_t)gtk_main_quit, NULL, "%s", err);
gtk_widget_show(errmsg_full(NULL, (errfunc_t)gtk_main_quit,
NULL, "%s", err));
g_free(err);
}
@@ -191,45 +194,6 @@ main(int argc, char **argv) {
return 0;
}
void
readargs(int argc, char **argv, int *port, int *limit) {
char *name;
int opt, num;
*port = 0;
*limit = 0;
if(NULL == (name = strrchr(argv[0], '/')) || '\0' == *(++name))
name = argv[0];
for(num = 1; num < argc; num++)
if(0 == strcmp(argv[num], "-help") || 0 == strcmp(argv[num], "--help"))
goto usage;
while(0 <= (opt = getopt(argc, argv, "hp:u:"))) {
switch(opt) {
case 'p':
num = atoi(optarg);
if(0 < num && 0xffff > num)
*port = num;
break;
case 'u':
num = atoi(optarg);
if(0 != num)
*limit = num;
break;
default:
goto usage;
}
}
return;
usage:
printf("usage: %s [-h] [-p port] [-u limit]\n", name);
exit(1);
}
void
maketypes(void) {
tr_type_pieces = g_boxed_type_register_static(
@@ -257,7 +221,7 @@ makewind(GtkWidget *wind, tr_handle_t *tr, GList *saved) {
struct cf_torrentstate *ts;
data->tr = tr;
data->wind = wind;
data->wind = GTK_WINDOW(wind);
data->timer = -1;
/* filled in by makewind_list */
data->model = NULL;
@@ -277,7 +241,8 @@ makewind(GtkWidget *wind, tr_handle_t *tr, GList *saved) {
for(ii = g_list_first(saved); NULL != ii; ii = ii->next) {
ts = ii->data;
addtorrent(tr, wind, ts->ts_torrent, ts->ts_directory, ts->ts_paused);
addtorrent(tr, GTK_WINDOW(wind),
ts->ts_torrent, ts->ts_directory, ts->ts_paused);
cf_freestate(ts);
}
g_list_free(saved);
@@ -322,8 +287,6 @@ winclose(GtkWidget *widget SHUTUP, GdkEvent *event SHUTUP, gpointer gdata) {
fprintf(stderr, "quit: starting timeout at %i\n", edata->started);
//exitcheck(edata);
/* returning FALSE means to destroy the window */
return TRUE;
}
@@ -363,7 +326,7 @@ exitcheck(gpointer gdata) {
g_source_remove(data->timer);
data->timer = -1;
gtk_widget_destroy(data->cbdata->wind);
gtk_widget_destroy(GTK_WIDGET(data->cbdata->wind));
tr_close(data->cbdata->tr);
g_free(data->cbdata);
g_free(data);
@@ -381,7 +344,7 @@ makewind_toolbar(struct cbdata *data) {
gtk_toolbar_set_tooltips(GTK_TOOLBAR(bar), TRUE);
gtk_toolbar_set_style(GTK_TOOLBAR(bar), GTK_TOOLBAR_BOTH);
for(ii = 0; ii < sizeof(actionitems) / sizeof(actionitems[0]); ii++) {
for(ii = 0; ii < ALEN(actionitems); ii++) {
item = gtk_tool_button_new_from_stock(actionitems[ii].id);
gtk_tool_button_set_label(GTK_TOOL_BUTTON(item), actionitems[ii].name);
gtk_tool_item_set_tooltip(GTK_TOOL_ITEM(item), GTK_TOOLBAR(bar)->tooltips,
@@ -419,7 +382,7 @@ makewind_list(struct cbdata *data) {
GtkCellRenderer *rend;
GtkCellRenderer *rendprog;
assert(MC_ROW_COUNT == sizeof(types) / sizeof(types[0]));
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));
@@ -432,30 +395,6 @@ makewind_list(struct cbdata *data) {
rendprog = gtk_cell_renderer_progress_new();
g_object_set(rendprog, "text", "", NULL);
/*
col = gtk_tree_view_column_new_with_attributes(
"Name", rend, "text", MC_NAME, NULL);
gtk_tree_view_column_add_attribute(col, rend, "text", MC_SIZE);
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
col = gtk_tree_view_column_new_with_attributes(
"Status", rend, "text", MC_STAT, NULL);
gtk_tree_view_column_add_attribute(col, rend, "text", MC_ERR);
gtk_tree_view_column_add_attribute(col, rend, "text", MC_DPEERS);
gtk_tree_view_column_add_attribute(col, rend, "text", MC_UPEERS);
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
col = gtk_tree_view_column_new_with_attributes(
"Progress", rendprog, "value", MC_PROG, NULL);
gtk_tree_view_column_pack_start(col, rend, TRUE);
gtk_tree_view_column_add_attribute(col, rend, "text", MC_ETA);
gtk_tree_view_column_add_attribute(col, rend, "text", MC_DRATE);
gtk_tree_view_column_add_attribute(col, rend, "text", MC_URATE);
gtk_tree_view_column_add_attribute(col, rend, "text", MC_DOWN);
gtk_tree_view_column_add_attribute(col, rend, "text", MC_UP);
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
*/
gtk_tree_view_append_column(GTK_TREE_VIEW(view),
gtk_tree_view_column_new_with_attributes("Name", rend,
"text", MC_NAME, NULL));
@@ -489,9 +428,6 @@ makewind_list(struct cbdata *data) {
gtk_tree_view_append_column(GTK_TREE_VIEW(view),
gtk_tree_view_column_new_with_attributes("Leechers", rend,
"text", MC_DPEERS, NULL));
/*gtk_tree_view_append_column(GTK_TREE_VIEW(view),
gtk_tree_view_column_new_with_attributes("", rend,
"text", MC_PIECES, NULL));*/
gtk_tree_view_append_column(GTK_TREE_VIEW(view),
gtk_tree_view_column_new_with_attributes("Downloaded", rend,
"text", MC_DOWN, NULL));
@@ -532,7 +468,7 @@ updatemodel(gpointer gdata) {
MC_ERR, st[ii].error, MC_PROG, prog, 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_PIECES, st[ii].pieces,*/ MC_DOWN, st[ii].downloaded, MC_UP, st[ii].uploaded, -1);
MC_DOWN, st[ii].downloaded, MC_UP, st[ii].uploaded, -1);
}
free(st);
@@ -578,7 +514,7 @@ dopopupmenu(GtkWidget *widget SHUTUP, GdkEventButton *event,
/* XXX am I leaking references here? */
/* XXX can I cache this in cbdata? */
for(ii = 0; ii < sizeof(actionitems) / sizeof(actionitems[0]); ii++) {
for(ii = 0; ii < ALEN(actionitems); ii++) {
item = gtk_menu_item_new_with_label(actionitems[ii].name);
g_object_set_data(G_OBJECT(item), LIST_ACTION,
GINT_TO_POINTER(actionitems[ii].act));
@@ -610,9 +546,15 @@ actionclick(GtkWidget *widget, gpointer gdata) {
int index;
tr_stat_t *sb;
if(ACT_OPEN == act) {
makeaddwind(data);
return;
switch(act) {
case ACT_OPEN:
makeaddwind(data);
return;
case ACT_PREF:
makeprefwindow(data->wind, data->tr);
return;
default:
break;
}
index = -1;
@@ -673,17 +615,26 @@ makeaddwind(struct cbdata *data) {
G_CALLBACK(fileclick), wind);
g_signal_connect_swapped(GTK_FILE_SELECTION(wind)->cancel_button, "clicked",
G_CALLBACK(gtk_widget_destroy), wind);
gtk_window_set_transient_for(GTK_WINDOW(wind), GTK_WINDOW(data->wind));
gtk_window_set_transient_for(GTK_WINDOW(wind), data->wind);
gtk_window_set_destroy_with_parent(GTK_WINDOW(wind), TRUE);
gtk_window_set_modal(GTK_WINDOW(wind), TRUE);
gtk_widget_show_all(wind);
}
gboolean
addtorrent(tr_handle_t *tr, GtkWidget *parentwind, const char *torrent,
addtorrent(tr_handle_t *tr, GtkWindow *parentwind, const char *torrent,
const char *dir, gboolean paused) {
char *wd;
if(NULL == dir) {
dir = cf_getpref(PREF_DIR);
if(!mkdir_p(dir, 0777)) {
errmsg(parentwind, "Failed to create download directory %s:\n%s",
dir, strerror(errno));
return FALSE;
}
}
if(0 != tr_torrentInit(tr, torrent)) {
/* XXX would be nice to have errno strings, are they printed to stdout? */
errmsg(parentwind, "Failed to open torrent file %s", torrent);
@@ -693,7 +644,6 @@ addtorrent(tr_handle_t *tr, GtkWidget *parentwind, const char *torrent,
if(NULL != dir)
tr_torrentSetFolder(tr, tr_torrentCount(tr) - 1, dir);
else {
/* XXX need pref for download directory */
wd = g_new(char, MAXPATHLEN + 1);
if(NULL == getcwd(wd, MAXPATHLEN + 1))
tr_torrentSetFolder(tr, tr_torrentCount(tr) - 1, ".");
@@ -747,7 +697,7 @@ makeinfowind(struct cbdata *data, int index) {
if(index >= tr_torrentStat(data->tr, &sb)) {
assert(!"XXX i'm tired");
}
wind = gtk_dialog_new_with_buttons(sb[index].info.name, GTK_WINDOW(data->wind),
wind = gtk_dialog_new_with_buttons(sb[index].info.name, data->wind,
GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
table = gtk_table_new(21, 2, FALSE);
@@ -855,7 +805,7 @@ makeinfowind(struct cbdata *data, int index) {
}
gboolean
savetorrents(tr_handle_t *tr, GtkWidget *wind, int count, tr_stat_t *stat) {
savetorrents(tr_handle_t *tr, GtkWindow *wind, int count, tr_stat_t *stat) {
char *errstr;
tr_stat_t *st;
gboolean ret;

181
gtk/prefs.c Normal file
View File

@@ -0,0 +1,181 @@
/*
Copyright (c) 2005 Joshua Elsasser. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <gtk/gtk.h>
#include "conf.h"
#include "prefs.h"
#include "transmission.h"
#include "util.h"
struct prefdata {
GtkSpinButton *port;
GtkCheckButton *uselimit;
GtkSpinButton *limit;
GtkEntry *dir;
GtkWindow *parent;
tr_handle_t *tr;
};
static void
clicklimitbox(GtkWidget *widget, gpointer gdata);
static void
clickdialog(GtkWidget *widget, int resp, gpointer gdata);
void
makeprefwindow(GtkWindow *parent, tr_handle_t *tr) {
GtkWidget *wind = gtk_dialog_new_with_buttons("Preferences", parent,
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK,
GTK_RESPONSE_OK, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
GtkWidget *table = gtk_table_new(4, 2, FALSE);
GtkWidget *portnum = gtk_spin_button_new_with_range(1, 0xffff, 1);
GtkWidget *limitbox = gtk_check_button_new_with_label("Limit upload speed");
GtkWidget *limitnum = gtk_spin_button_new_with_range(0, G_MAXLONG, 1);
GtkWidget *dirstr = gtk_entry_new();
GtkWidget *label;
const char *pref;
struct prefdata *data = g_new0(struct prefdata, 1);
data->port = GTK_SPIN_BUTTON(portnum);
data->uselimit = GTK_CHECK_BUTTON(limitbox);
data->limit = GTK_SPIN_BUTTON(limitnum);
data->dir = GTK_ENTRY(dirstr);
data->parent = parent;
data->tr = tr;
/* port label and entry */
label = gtk_label_new("Listening port");
if(NULL != (pref = cf_getpref(PREF_PORT)))
gtk_spin_button_set_value(GTK_SPIN_BUTTON(portnum), strtol(pref,NULL,10));
else
gtk_spin_button_set_value(GTK_SPIN_BUTTON(portnum), TR_DEFAULT_PORT);
gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
gtk_table_attach_defaults(GTK_TABLE(table), portnum, 1, 2, 0, 1);
/* limit checkbox */
pref = cf_getpref(PREF_USELIMIT);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(limitbox),
(NULL == pref ? FALSE : strbool(pref)));
gtk_widget_set_sensitive(limitnum,
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(limitbox)));
g_signal_connect(G_OBJECT(limitbox), "clicked",
G_CALLBACK(clicklimitbox), limitnum);
gtk_table_attach_defaults(GTK_TABLE(table), limitbox, 0, 2, 1, 2);
/* limit label and entry */
label = gtk_label_new("Maximum upload speed");
if(NULL != (pref = cf_getpref(PREF_LIMIT)))
gtk_spin_button_set_value(GTK_SPIN_BUTTON(limitnum), strtol(pref,NULL,10));
gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3);
gtk_table_attach_defaults(GTK_TABLE(table), limitnum, 1, 2, 2, 3);
/* directory label and entry */
label = gtk_label_new("Download directory");
if(NULL != (pref = cf_getpref(PREF_DIR)))
gtk_entry_set_text(GTK_ENTRY(dirstr), pref);
gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4);
gtk_table_attach_defaults(GTK_TABLE(table), dirstr, 1, 2, 3, 4);
gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(wind)->vbox), table);
g_signal_connect(G_OBJECT(wind), "response", G_CALLBACK(clickdialog), data);
gtk_widget_show_all(wind);
}
static void
clicklimitbox(GtkWidget *widget, gpointer gdata) {
GtkWidget *entry = gdata;
gtk_widget_set_sensitive(entry,
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
}
static void
clickdialog(GtkWidget *widget, int resp, gpointer gdata) {
struct prefdata *data = gdata;
int intval;
const char *strval;
char *strnum;
gboolean boolval;
if(GTK_RESPONSE_OK == resp) {
/* check directory */
strval = gtk_entry_get_text(data->dir);
if(!mkdir_p(strval, 0777)) {
errmsg(data->parent, "Failed to create directory %s:\n%s",
strval, strerror(errno));
return;
}
/* save port pref */
strnum = g_strdup_printf("%i",
gtk_spin_button_get_value_as_int(data->port));
cf_setpref(PREF_PORT, strnum, NULL);
g_free(strnum);
/* XXX should I change the port here? is it even possible? */
/* save uselimit pref */
boolval = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->uselimit));
cf_setpref(PREF_USELIMIT, (boolval ? "yes" : "no"), NULL);
/* save limit pref */
intval = gtk_spin_button_get_value_as_int(data->limit);
strnum = g_strdup_printf("%i", intval);
cf_setpref(PREF_LIMIT, strnum, NULL);
g_free(strnum);
setlimit(data->tr);
/*
note that prefs aren't written to disk unless we pass a pointer
to an error string, so do this for the last call to cf_setpref()
*/
/* save dir pref */
if(!cf_setpref(PREF_DIR, gtk_entry_get_text(data->dir), &strnum)) {
errmsg(data->parent, "%s", strnum);
g_free(strnum);
return;
}
}
gtk_widget_destroy(widget);
g_free(data);
}
void
setlimit(tr_handle_t *tr) {
const char *pref;
if(NULL == (pref = cf_getpref(PREF_USELIMIT)) || !strbool(pref))
tr_setUploadLimit(tr, -1);
else if(NULL != (pref = cf_getpref(PREF_LIMIT)))
tr_setUploadLimit(tr, strtol(pref, NULL, 10));
else
tr_setUploadLimit(tr, -1);
}

45
gtk/prefs.h Normal file
View File

@@ -0,0 +1,45 @@
/*
Copyright (c) 2005 Joshua Elsasser. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TG_PREFS_H
#define TG_PREFS_H
#include "transmission.h"
/* macros for names of prefs we use */
#define PREF_PORT "listening-port"
#define PREF_USELIMIT "use-upload-limit"
#define PREF_LIMIT "upload-limit"
#define PREF_DIR "download-directory"
void
makeprefwindow(GtkWindow *parent, tr_handle_t *tr);
/* set the upload limit based on saved prefs */
void
setlimit(tr_handle_t *tr);
#endif /* TG_PREFS_H */

View File

@@ -24,13 +24,18 @@
POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdarg.h>
#include <string.h>
#include <gtk/gtk.h>
#include "util.h"
static void
errcb(GtkWidget *widget, int resp, gpointer data);
errcb(GtkWidget *wind, int resp, gpointer data);
gboolean
strbool(const char *str) {
@@ -50,8 +55,34 @@ strbool(const char *str) {
return FALSE;
}
gboolean
mkdir_p(const char *name, mode_t mode) {
struct stat sb;
char *parent;
gboolean ret;
int oerrno;
if(0 != stat(name, &sb)) {
if(ENOENT != errno)
return FALSE;
parent = g_path_get_dirname(name);
ret = mkdir_p(parent, mode);
oerrno = errno;
g_free(parent);
errno = oerrno;
return (ret ? (0 == mkdir(name, mode)) : FALSE);
}
if(!S_ISDIR(sb.st_mode)) {
errno = ENOTDIR;
return FALSE;
}
return TRUE;
}
GtkWidget *
errmsg(GtkWidget *wind, const char *format, ...) {
errmsg(GtkWindow *wind, const char *format, ...) {
GtkWidget *dialog;
va_list ap;
@@ -63,7 +94,7 @@ errmsg(GtkWidget *wind, const char *format, ...) {
}
GtkWidget *
errmsg_full(GtkWidget *wind, errfunc_t func, void *data,
errmsg_full(GtkWindow *wind, errfunc_t func, void *data,
const char *format, ...) {
GtkWidget *dialog;
va_list ap;
@@ -76,7 +107,7 @@ errmsg_full(GtkWidget *wind, errfunc_t func, void *data,
}
GtkWidget *
verrmsg(GtkWidget *wind, errfunc_t func, void *data,
verrmsg(GtkWindow *wind, errfunc_t func, void *data,
const char *format, va_list ap) {
GtkWidget *dialog;
char *msg;
@@ -88,8 +119,8 @@ verrmsg(GtkWidget *wind, errfunc_t func, void *data,
dialog = gtk_message_dialog_new(
NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", msg);
else
dialog = gtk_message_dialog_new(
GTK_WINDOW(wind), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
dialog = gtk_message_dialog_new(wind,
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", msg);
if(NULL == func)
@@ -98,7 +129,7 @@ verrmsg(GtkWidget *wind, errfunc_t func, void *data,
funcdata = g_list_append(g_list_append(NULL, func), data);
g_signal_connect(dialog, "response", G_CALLBACK(errcb), funcdata);
if(NULL != wind)
gtk_widget_show_all(dialog);
gtk_widget_show(dialog);
g_free(msg);
return dialog;

View File

@@ -27,6 +27,7 @@
#ifndef TG_UTIL_H
#define TG_UTIL_H
#include <sys/types.h>
#include <stdarg.h>
/* macro to shut up "unused parameter" warnings */
@@ -36,22 +37,28 @@
#define SHUTUP
#endif
/* return number of items in array */
#define ALEN(a) (sizeof(a) / sizeof((a)[0]))
gboolean
strbool(const char *str);
gboolean
mkdir_p(const char *name, mode_t mode);
typedef void (*errfunc_t)(void*);
/* if wind is NULL then you must call gtk_widget_show on the returned widget */
GtkWidget *
errmsg(GtkWidget *wind, const char *format, ...)
errmsg(GtkWindow *wind, const char *format, ...)
#ifdef __GNUC__
__attribute__ ((format (printf, 2, 3)))
#endif
;
GtkWidget *
errmsg_full(GtkWidget *wind, errfunc_t func, void *data,
errmsg_full(GtkWindow *wind, errfunc_t func, void *data,
const char *format, ...)
#ifdef __GNUC__
__attribute__ ((format (printf, 4, 5)))
@@ -59,7 +66,7 @@ errmsg_full(GtkWidget *wind, errfunc_t func, void *data,
;
GtkWidget *
verrmsg(GtkWidget *wind, errfunc_t func, void *data,
verrmsg(GtkWindow *wind, errfunc_t func, void *data,
const char *format, va_list ap);
#endif /* TG_UTIL_H */

View File

@@ -65,7 +65,7 @@ tr_handle_t * tr_init()
h->upload = tr_uploadInit();
h->fdlimit = tr_fdInit();
h->bindPort = 9090;
h->bindPort = TR_DEFAULT_PORT;
snprintf( h->prefsDirectory, sizeof( h->prefsDirectory ),
"%s/.transmission", getenv( "HOME" ) );

View File

@@ -29,6 +29,8 @@
#define MAX_PATH_LENGTH 1024
#define TR_MAX_TORRENT_COUNT 50
#define TR_DEFAULT_PORT 9090
/***********************************************************************
* tr_init
***********************************************************************

View File

@@ -101,13 +101,13 @@ int tr_uploadCanUpload( tr_upload_t * u )
size = 0;
now = tr_date();
/* Check the last four times we sent something and decide if
/* Check the last FOO times we sent something and decide if
we must wait */
for( i = 0; i < FOO; i++ )
{
size += u->sizes[i];
if( (uint64_t) size < 1024ULL *
u->limit * ( now - u->dates[i] ) / 1000 )
if( (uint64_t) size * 1000 <
( now - u->dates[i] ) * u->limit * 1024 )
{
ret = 1;
break;

View File

@@ -42,7 +42,7 @@ static inline uint64_t tr_date()
{
struct timeval tv;
gettimeofday( &tv, NULL );
return( (uint64_t) tv.tv_sec * 1000 + (uint64_t) tv.tv_usec / 1000 );
return (uint64_t) tv.tv_sec * 1000 + ( tv.tv_usec / 1000 );
}
/***********************************************************************

Binary file not shown.

View File

@@ -26,11 +26,10 @@
#include <Cocoa/Cocoa.h>
#include <transmission.h>
#include "PrefsController.h"
#include <Growl/Growl.h>
@class TorrentTableView;
@interface Controller : NSObject <GrowlApplicationBridgeDelegate>
@interface Controller : NSObject
{
tr_handle_t * fHandle;
int fCount;
@@ -98,8 +97,10 @@
- (void) showMainWindow: (id) sender;
- (void) linkHomepage: (id) sender;
- (void) linkForums: (id) sender;
- (void) notifyGrowl: (NSString *) file folder: (NSString *) folder;
- (void) revealInFinder: (NSString *) path;
- (void) notifyGrowl: (NSString *) file;
- (void) finderReveal: (NSString *) path;
- (void) finderTrash: (NSString *) path;
- (void) growlRegister: (id) sender;
@end

View File

@@ -191,7 +191,7 @@ static void sleepCallBack( void * controller, io_service_t y,
}
/* Register with the growl system */
[GrowlApplicationBridge setGrowlDelegate:self];
[self growlRegister: self];
/* Update the interface every 500 ms */
fCount = 0;
@@ -467,74 +467,16 @@ static void sleepCallBack( void * controller, io_service_t y,
deleteTorrent: (BOOL) deleteTorrent
deleteData: (BOOL) deleteData
{
BOOL torrentWarning = ![[NSUserDefaults standardUserDefaults]
boolForKey:@"SkipTorrentDeletionWarning"];
BOOL dataWarning = ![[NSUserDefaults standardUserDefaults]
boolForKey:@"SkipDataDeletionWarning"];
if ( ( torrentWarning && deleteTorrent ) || (dataWarning && deleteData ) )
if( deleteData )
{
NSAlert *alert = [[[NSAlert alloc] init] autorelease];
[alert addButtonWithTitle:@"Delete"];
[alert addButtonWithTitle:@"Cancel"];
[alert setAlertStyle:NSWarningAlertStyle];
[alert setMessageText:@"Do you want to remove this torrent from Transmission?"];
if ( (deleteTorrent && torrentWarning) &&
!( dataWarning && deleteData ) )
{
/* delete torrent warning YES, delete data warning NO */
[alert setInformativeText:@"This will delete the .torrent file only. "
"This can not be undone!"];
}
else if( (deleteData && dataWarning) &&
!( torrentWarning && deleteTorrent ) )
{
/* delete torrent warning NO, delete data warning YES */
[alert setInformativeText:@"This will delete the downloaded data. "
"This can not be undone!"];
}
else
{
/* delete torrent warning YES, delete data warning YES */
[alert setInformativeText:@"This will delete the downloaded data and "
".torrent file. This can not be undone!"];
}
if ( [alert runModal] == NSAlertSecondButtonReturn )
return;
[self finderTrash: [NSString stringWithFormat: @"%@/%@",
[NSString stringWithUTF8String: fStat[idx].folder],
[NSString stringWithUTF8String: fStat[idx].info.name]]];
}
if ( deleteData )
if( deleteTorrent )
{
tr_file_t * files = fStat[idx].info.files;
int i;
for ( i = 0; i < fStat[idx].info.fileCount; i++ )
{
if ( -1 == remove([[NSString stringWithFormat:@"%s/%s",
fStat[idx].folder,
files[i].name]
cString]) )
{
NSLog(@"remove(%s) failed, errno = %i",
files[i].name,
errno);
}
}
/* in some cases, we should remove fStat[idx].folder also. When? */
}
if ( deleteTorrent )
{
if ( -1 == remove( fStat[idx].info.torrent ) )
{
NSLog(@"remove(%s) failed, errno = %i",
fStat[idx].info.torrent,
errno);
}
[self finderTrash: [NSString stringWithUTF8String:
fStat[idx].info.torrent]];
}
tr_torrentClose( fHandle, idx );
@@ -611,8 +553,7 @@ static void sleepCallBack( void * controller, io_service_t y,
continue;
}
[self notifyGrowl: [NSString stringWithUTF8String:
fStat[i].info.name] folder: [NSString stringWithUTF8String:
fStat[i].folder]];
fStat[i].info.name]];
tr_setFinished( fHandle, i, 0 );
}
@@ -909,67 +850,108 @@ static void sleepCallBack( void * controller, io_service_t y,
URLWithString:@"http://transmission.m0k.org/forum/"]];
}
- (void) notifyGrowl: (NSString * ) file folder: (NSString *) folder
- (void) notifyGrowl: (NSString * ) file
{
[GrowlApplicationBridge
notifyWithTitle: @"Download complete."
description: [NSString stringWithFormat: @"Seeding: %@", file]
notificationName: @"Download complete."
iconData: nil
priority: 0
isSticky: FALSE
clickContext: [NSString stringWithFormat: @"%@/%@", folder, file]];
}
- (NSDictionary *)registrationDictionaryForGrowl
{
NSString *title = [NSString stringWithUTF8String: "Download complete."];
NSMutableArray *defNotesArray = [NSMutableArray array];
NSMutableArray *allNotesArray = [NSMutableArray array];
[allNotesArray addObject:title];
[defNotesArray addObject:[NSNumber numberWithUnsignedInt:0]];
NSDictionary *regDict = [NSDictionary dictionaryWithObjectsAndKeys:
@"Transmission", GROWL_APP_NAME,
allNotesArray, GROWL_NOTIFICATIONS_ALL,
defNotesArray, GROWL_NOTIFICATIONS_DEFAULT,
nil];
return regDict;
}
- (NSString *) applicationNameForGrowl
{
return [NSString stringWithUTF8String: "Transmission"];
}
- (void) growlNotificationWasClicked: (id) clickContext
{
[self revealInFinder: (NSString *) clickContext];
}
- (void) revealFromMenu: (id) sender
{
[fTableView revealInFinder: [fTableView selectedRow]];
}
- (void) revealInFinder: (NSString *) path
{
NSString * string;
NSString * growlScript;
NSAppleScript * appleScript;
NSDictionary * error;
string = [NSString stringWithFormat: @"tell application "
"\"Finder\"\nactivate\nreveal (POSIX file \"%@\")\nend tell",
path];
appleScript = [[NSAppleScript alloc] initWithSource: string];
growlScript = [NSString stringWithFormat:
@"tell application \"System Events\"\n"
" if exists application process \"GrowlHelperApp\" then\n"
" tell application \"GrowlHelperApp\"\n "
" notify with name \"Download Complete\""
" title \"Download Complete\""
" description \"%@\""
" application name \"Transmission\"\n"
" end tell\n"
" end if\n"
"end tell", file];
appleScript = [[NSAppleScript alloc] initWithSource: growlScript];
if( ![appleScript executeAndReturnError: &error] )
{
printf( "Reveal in Finder: AppleScript failed\n" );
printf( "Growl notify failed\n" );
}
[appleScript release];
}
- (void) growlRegister: (id) sender
{
NSString * growlScript;
NSAppleScript * appleScript;
NSDictionary * error;
growlScript = [NSString stringWithFormat:
@"tell application \"System Events\"\n"
" if exists application process \"GrowlHelperApp\" then\n"
" tell application \"GrowlHelperApp\"\n"
" register as application \"Transmission\" "
" all notifications {\"Download Complete\"}"
" default notifications {\"Download Complete\"}"
" icon of application \"Transmission\"\n"
" end tell\n"
" end if\n"
"end tell"];
appleScript = [[NSAppleScript alloc] initWithSource: growlScript];
if( ![appleScript executeAndReturnError: &error] )
{
printf( "Growl registration failed\n" );
}
[appleScript release];
}
- (void) revealFromMenu: (id) sender
{
int row;
row = [fTableView selectedRow];
if( row < 0 )
{
return;
}
[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];
}
@end

View File

@@ -44,24 +44,24 @@
{
NSUserDefaults * defaults;
NSDictionary * appDefaults;
NSString * desktop, * port;
fHandle = handle;
/* Register defaults settings:
- Simple bar
- Always download to Desktop
- Port 9090
- Port TR_DEFAULT_PORT
- 20 KB/s upload limit */
NSString * desktopPath
= [NSString stringWithFormat: @"%@/Desktop",
NSHomeDirectory()];
desktop = [NSHomeDirectory() stringByAppendingString: @"/Desktop"];
port = [NSString stringWithFormat: @"%d", TR_DEFAULT_PORT];
defaults = [NSUserDefaults standardUserDefaults];
appDefaults = [NSDictionary dictionaryWithObjectsAndKeys:
@"NO", @"UseAdvancedBar",
@"Constant", @"DownloadChoice",
desktopPath, @"DownloadFolder",
@"9090", @"BindPort",
desktop, @"DownloadFolder",
port, @"BindPort",
@"20", @"UploadLimit",
NULL];
[defaults registerDefaults: appDefaults];

View File

@@ -30,12 +30,11 @@
{
tr_stat_t * fStat;
NSString * fProgressString;
NSString * fDlString;
NSString * fUlString;
NSBitmapImageRep * fBgBmp;
NSBitmapImageRep * fBmp;
NSBitmapImageRep * fBackgroundBmp;
NSBitmapImageRep * fProgressBmp;
}
- (id) init;
- (void) setStat: (tr_stat_t *) stat;

View File

@@ -22,6 +22,14 @@
#include "ProgressCell.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[] =
@@ -71,49 +79,63 @@ static uint32_t kGreen[] =
0x00C900FF, 0x00C600FF, 0x00D100FF, 0x00DB00FF, 0x00E800FF,
0x00ED00FF, 0x00F200FF, 0x00F400FF, 0x00B500FF };
@implementation ProgressCell
/***********************************************************************
* init
***********************************************************************
* Prepares the NSBitmapImageReps we are going to need in order to
* draw.
**********************************************************************/
- (id) init
{
NSImage * bgImg;
NSSize size;
self = [super init];
/* Have a NSBitmapImageRep ready to draw the progression bar */
bgImg = [NSImage imageNamed: @"Progress.png"];
fBgBmp = [[bgImg representations] objectAtIndex: 0];
size = [bgImg size];
fBmp = [[NSBitmapImageRep alloc]
initWithBitmapDataPlanes: NULL pixelsWide: size.width
pixelsHigh: size.height bitsPerSample: 8 samplesPerPixel: 4
hasAlpha: YES isPlanar: NO
/* 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
{
int i;
uint8_t * in, * out;
fStat = stat;
fProgressString = [NSString stringWithFormat:
@"%.2f %%", 100.0 * fStat->progress];
/* Update the strings to be displayed */
fDlString = [NSString stringWithFormat:
@"DL: %.2f KB/s", fStat->rateDownload];
fUlString = [NSString stringWithFormat:
@"UL: %.2f KB/s", fStat->rateUpload];
for( i = 0; i < [fBmp size].height; i++ )
/* Reset our bitmap to the background image... */
in = [fBackgroundBmp bitmapData];
out = [fProgressBmp bitmapData];
for( i = 0; i < [fProgressBmp size].height; i++ )
{
memcpy( [fBmp bitmapData] + i * [fBmp bytesPerRow],
[fBgBmp bitmapData] + i * [fBgBmp bytesPerRow],
[fBmp size].width * 4 );
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"])
{
@@ -125,128 +147,146 @@ static uint32_t kGreen[] =
}
}
/***********************************************************************
* buildSimpleBar
**********************************************************************/
- (void) buildSimpleBar
{
int h, w;
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];
p += 2;
end = lrintf( floor( fStat->progress * 120 ) );
colors = ( fStat->status & TR_STATUS_SEED ) ? kGreen : kBlue2;
for( h = 0; h < 14; h++ )
{
p = (uint32_t *) ( [fBmp bitmapData] +
h * [fBmp bytesPerRow] ) + 2;
for( w = 0; w < 120; w++ )
for( w = 0; w < end; w++ )
{
if( w >= (int) ( fStat->progress * 120 ) )
{
break;
}
if( fStat->status & TR_STATUS_SEED )
{
*p = kGreen[h];
}
else
{
*p = kBlue2[h];
}
p++;
p[w] = colors[h];
}
p += pixelsPerRow;
}
}
/***********************************************************************
* buildAdvancedBar
**********************************************************************/
- (void) buildAdvancedBar
{
int h, w;
int h, w, end, pixelsPerRow;
uint32_t * p;
uint32_t * colors;
for( h = 0; h < 14; h++ )
if( fStat->status & TR_STATUS_SEED )
{
p = (uint32_t *) ( [fBmp bitmapData] +
h * [fBmp bytesPerRow] ) + 2;
/* All green, same as the simple bar */
[self buildSimpleBar];
return;
}
for( w = 0; w < 120; w++ )
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++ )
{
if( fStat->status & TR_STATUS_SEED )
{
*p = kGreen[h];
}
else
{
/* Download is not finished yet */
if( h < 2 )
{
/* First two lines: dark blue to show progression */
if( w >= (int) ( fStat->progress * 120 ) )
{
break;
}
*p = kBlue4[h];
}
else
{
/* Lines 2 to X: blue or grey depending on whether
we have the piece or not */
if( fStat->pieces[w] < 0 )
{
*p = kGray[h];
}
else if( fStat->pieces[w] < 1 )
{
*p = kRed[h];
}
else if( fStat->pieces[w] < 2 )
{
*p = kBlue1[h];
}
else if( fStat->pieces[w] < 3 )
{
*p = kBlue2[h];
}
else
{
*p = kBlue3[h];
}
}
}
p[w] = kBlue4[h];
}
p += pixelsPerRow;
}
p++;
/* 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] = 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;
}
NSMutableDictionary * attributes;
NSPoint pen = cellFrame.origin;
pen = cellFrame.origin;
attributes = [NSMutableDictionary dictionaryWithCapacity: 1];
[attributes setObject: [NSFont messageFontOfSize:12.0]
forKey: NSFontAttributeName];
pen.x += 5; pen.y += 5;
img = [[NSImage alloc] initWithSize: [fBmp size]];
[img addRepresentation: fBmp];
/* 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];
[img drawAtPoint: pen fromRect:
NSMakeRect( 0, 0, [fBmp size].width, [fBmp size].height )
/* 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];
[attributes setObject: [NSFont messageFontOfSize:10.0]
forKey: NSFontAttributeName];
/* Draw the strings with font 10 */
attributes = [NSDictionary dictionaryWithObject:
[NSFont messageFontOfSize: 10.0] forKey: NSFontAttributeName];
pen.x += 5; pen.y += 20;
[fDlString drawAtPoint: pen withAttributes: attributes];
pen.x += 0; pen.y += 15;
[fUlString drawAtPoint: pen withAttributes: attributes];

View File

@@ -9,24 +9,6 @@
[self reloadData];
}
- (void) revealInFinder: (int) row
{
NSString * string;
NSAppleScript * appleScript;
NSDictionary * error;
string = [NSString stringWithFormat: @"tell application "
"\"Finder\"\nactivate\nreveal (POSIX file \"%@/%@\")\nend tell",
[NSString stringWithUTF8String: fStat[row].folder],
[NSString stringWithUTF8String: fStat[row].info.name]];
appleScript = [[NSAppleScript alloc] initWithSource: string];
if( ![appleScript executeAndReturnError: &error] )
{
printf( "Reveal in Finder: AppleScript failed\n" );
}
[appleScript release];
}
- (void) pauseOrResume: (int) row
{
if( fStat[row].status & TR_STATUS_PAUSE )
@@ -106,7 +88,7 @@
}
else if( [self pointInRevealRect: point] )
{
[fController revealInFinder: [NSString stringWithFormat:
[fController finderReveal: [NSString stringWithFormat:
@"%@/%@", [NSString stringWithUTF8String: fStat[row].folder],
[NSString stringWithUTF8String: fStat[row].info.name]]];
[self display];

Binary file not shown.

View File

@@ -18,8 +18,6 @@
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 */; };
4D81E2E4092EF0CB00F24127 /* Growl.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D81E2E3092EF0CB00F24127 /* Growl.framework */; };
4D81E309092EF26F00F24127 /* Growl.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4D81E2E3092EF0CB00F24127 /* Growl.framework */; };
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 */; };
@@ -62,19 +60,6 @@
};
/* End PBXBuildStyle section */
/* Begin PBXCopyFilesBuildPhase section */
4D81E301092EF24500F24127 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
4D81E309092EF26F00F24127 /* Growl.framework in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
@@ -99,7 +84,6 @@
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>"; };
4D81E2E3092EF0CB00F24127 /* Growl.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Growl.framework; path = Frameworks/Growl.framework; 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>"; };
@@ -122,7 +106,6 @@
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */,
4DF0C5AE08991C1600DD8943 /* libtransmission.a in Frameworks */,
4D3EA0AA08AE13C600EA10C2 /* IOKit.framework in Frameworks */,
4D81E2E4092EF0CB00F24127 /* Growl.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -150,7 +133,6 @@
isa = PBXGroup;
children = (
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */,
4D81E2E3092EF0CB00F24127 /* Growl.framework */,
);
name = "Linked Frameworks";
sourceTree = "<group>";
@@ -238,7 +220,6 @@
8D1107290486CEB800E47090 /* Resources */,
8D11072C0486CEB800E47090 /* Sources */,
8D11072E0486CEB800E47090 /* Frameworks */,
4D81E301092EF24500F24127 /* CopyFiles */,
);
buildRules = (
);

View File

@@ -39,14 +39,14 @@
" -i, --info Print metainfo and exit\n" \
" -s, --scrape Print counts of seeders/leechers and exit\n" \
" -v, --verbose <int> Verbose level (0 to 2, default = 0)\n" \
" -p, --port <int> Port we should listen on (default = 9090)\n" \
" -p, --port <int> Port we should listen on (default = %d)\n" \
" -u, --upload <int> Maximum upload rate (-1 = no limit, default = 20)\n"
static int showHelp = 0;
static int showInfo = 0;
static int showScrape = 0;
static int verboseLevel = 0;
static int bindPort = 9090;
static int bindPort = TR_DEFAULT_PORT;
static int uploadLimit = 20;
static char * torrentPath = NULL;
static volatile char mustDie = 0;
@@ -66,13 +66,13 @@ int main( int argc, char ** argv )
/* Get options */
if( parseCommandLine( argc, argv ) )
{
printf( USAGE, argv[0] );
printf( USAGE, argv[0], TR_DEFAULT_PORT );
return 1;
}
if( showHelp )
{
printf( USAGE, argv[0] );
printf( USAGE, argv[0], TR_DEFAULT_PORT );
return 0;
}