summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Alan Fitton <ajf@eth0.org.uk>2011-03-10 14:57:38 +0000
committerGravatar Alan Fitton <ajf@eth0.org.uk>2011-03-10 14:57:38 +0000
commita1433a45c470950b3f03541257ef54c4bb48d449 (patch)
tree9e285c6bf2d0508f10b0ada60761f1bee2eb98f8 /src
parentce795861eeb1893e40e4476fea71986450658948 (diff)
Korean support, thanks Youn! Also filter by tracker. Adding/removing trackers from the state selector every update seems like a waste of cycles, so only refresh when a torrent is added.
Diffstat (limited to 'src')
-rw-r--r--src/torrent.c19
-rw-r--r--src/torrent.h1
-rw-r--r--src/trg-main-window.c46
-rw-r--r--src/trg-main-window.h3
-rw-r--r--src/trg-model.c6
-rw-r--r--src/trg-model.h2
-rw-r--r--src/trg-state-selector.c127
-rw-r--r--src/trg-state-selector.h10
-rw-r--r--src/trg-torrent-model.c19
-rw-r--r--src/trg-torrent-model.h6
-rw-r--r--src/util.c17
-rw-r--r--src/util.h3
12 files changed, 236 insertions, 23 deletions
diff --git a/src/torrent.c b/src/torrent.c
index def9cbc..f45cc31 100644
--- a/src/torrent.c
+++ b/src/torrent.c
@@ -23,6 +23,7 @@
#include "torrent.h"
#include "protocol-constants.h"
+#include "util.h"
JsonArray *torrent_get_peers(JsonObject * t)
{
@@ -208,6 +209,24 @@ gchar *torrent_get_status_string(gint64 value)
}
}
+gboolean torrent_has_tracker(JsonObject *t, GRegex *rx, gchar *search)
+{
+ JsonArray *trackers = torrent_get_trackers(t);
+ int i;
+
+ for (i = 0; i < json_array_get_length(trackers); i++) {
+ JsonObject *tracker = json_array_get_object_element(trackers, i);
+ const gchar *trackerAnnounce = tracker_get_announce(tracker);
+ gchar *trackerAnnounceHost = trg_uri_host_extract(rx, trackerAnnounce);
+ int cmpResult = g_strcmp0(trackerAnnounceHost, search);
+ g_free(trackerAnnounceHost);
+ if (cmpResult == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
gint64 tracker_get_id(JsonObject * t)
{
return json_object_get_int_member(t, FIELD_ID);
diff --git a/src/torrent.h b/src/torrent.h
index 16102e4..226cd4e 100644
--- a/src/torrent.h
+++ b/src/torrent.h
@@ -68,6 +68,7 @@ gboolean torrent_get_download_limited(JsonObject * t);
gdouble torrent_get_seed_ratio_limit(JsonObject * t);
gint64 torrent_get_seed_ratio_mode(JsonObject * t);
gint64 torrent_get_peer_limit(JsonObject * t);
+gboolean torrent_has_tracker(JsonObject *t, GRegex *rx, gchar *search);
JsonArray *get_torrents(JsonObject * response);
diff --git a/src/trg-main-window.c b/src/trg-main-window.c
index ed25791..1b67687 100644
--- a/src/trg-main-window.c
+++ b/src/trg-main-window.c
@@ -40,6 +40,7 @@
#include "util.h"
#include "requests.h"
#include "session-get.h"
+#include "torrent.h"
#include "protocol-constants.h"
#include "trg-main-window.h"
@@ -72,13 +73,13 @@ static void torrent_event_notification(TrgTorrentModel * model,
gchar * icon, gchar * desc,
gint tmout, gchar * prefKey,
GtkTreeIter * iter,
- gpointer * data);
+ gpointer data);
static void on_torrent_completed(TrgTorrentModel * model,
- GtkTreeIter * iter, gpointer * data);
+ GtkTreeIter * iter, gpointer data);
static void on_torrent_added(TrgTorrentModel * model, GtkTreeIter * iter,
- gpointer * data);
+ gpointer data);
static gboolean delete_event(GtkWidget * w, GdkEvent * event,
- gpointer * data);
+ gpointer data);
static void destroy_window(GtkWidget * w, gpointer data);
static void torrent_tv_onRowActivated(GtkTreeView * treeview,
GtkTreePath * path,
@@ -268,7 +269,7 @@ static gboolean update_selected_torrent_notebook(TrgMainWindow * win,
static void torrent_event_notification(TrgTorrentModel * model,
gchar * icon, gchar * desc,
gint tmout, gchar * prefKey,
- GtkTreeIter * iter, gpointer * data)
+ GtkTreeIter * iter, gpointer data)
{
TrgMainWindowPrivate *priv;
gchar *name;
@@ -305,24 +306,30 @@ static void torrent_event_notification(TrgTorrentModel * model,
}
static void on_torrent_completed(TrgTorrentModel * model,
- GtkTreeIter * iter, gpointer * data)
+ GtkTreeIter * iter, gpointer data)
{
torrent_event_notification(model, GTK_STOCK_APPLY,
- "This torrent has completed.", 8000,
+ "This torrent has completed.", TORRENT_COMPLETE_NOTIFY_TMOUT,
TRG_GCONF_KEY_COMPLETE_NOTIFY, iter, data);
}
+static void on_torrent_addremove(TrgTorrentModel *model, gpointer data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+ trg_state_selector_update_trackers(priv->stateSelector, priv->client->torrents, priv->client->updateSerial);
+}
+
static void on_torrent_added(TrgTorrentModel * model,
- GtkTreeIter * iter, gpointer * data)
+ GtkTreeIter * iter, gpointer data)
{
torrent_event_notification(model, GTK_STOCK_ADD,
- "This torrent has been added.", 3000,
+ "This torrent has been added.", TORRENT_ADD_NOTIFY_TMOUT,
TRG_GCONF_KEY_ADD_NOTIFY, iter, data);
}
static gboolean delete_event(GtkWidget * w,
GdkEvent * event G_GNUC_UNUSED,
- gpointer * data G_GNUC_UNUSED)
+ gpointer data G_GNUC_UNUSED)
{
TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(w);
int width, height;
@@ -1043,9 +1050,20 @@ trg_torrent_tree_view_visible_func(GtkTreeModel * model,
TORRENT_COLUMN_FLAGS, &flags,
TORRENT_COLUMN_NAME, &name, -1);
- if (criteria != 0 && !(flags & criteria)) {
- visible = FALSE;
- } else if (name != NULL) {
+ if (criteria != 0) {
+ if (criteria & FILTER_FLAG_TRACKER) {
+ gchar *text = trg_state_selector_get_selected_text(priv->stateSelector);
+ JsonObject *json = NULL;
+ gtk_tree_model_get(model, iter, TORRENT_COLUMN_JSON, &json, -1);
+
+ if (!torrent_has_tracker(json, trg_state_selector_get_url_host_regex(priv->stateSelector), text))
+ visible = FALSE;
+ } else if (!(flags & criteria)) {
+ visible = FALSE;
+ }
+ }
+
+ if (visible && name != NULL) {
const gchar *filterText =
gtk_entry_get_text(GTK_ENTRY(priv->filterEntry));
if (strlen(filterText) > 0) {
@@ -1762,6 +1780,8 @@ static GObject *trg_main_window_constructor(GType type,
G_CALLBACK(on_torrent_completed), self);
g_signal_connect(priv->torrentModel, "torrent-added",
G_CALLBACK(on_torrent_added), self);
+ g_signal_connect(priv->torrentModel, "torrent-addremove",
+ G_CALLBACK(on_torrent_addremove), self);
priv->filteredTorrentModel =
gtk_tree_model_filter_new(GTK_TREE_MODEL(priv->torrentModel),
diff --git a/src/trg-main-window.h b/src/trg-main-window.h
index fd413c6..cdba421 100644
--- a/src/trg-main-window.h
+++ b/src/trg-main-window.h
@@ -57,6 +57,9 @@ typedef struct {
#define TORRENT_GET_MODE_INTERACTION 1
#define TORRENT_GET_MODE_UPDATE 2
+#define TORRENT_COMPLETE_NOTIFY_TMOUT 8000
+#define TORRENT_ADD_NOTIFY_TMOUT 3000
+
GType trg_main_window_get_type(void);
gboolean trg_add_from_filename(TrgMainWindow * win, gchar * fileName);
void on_session_set(JsonObject * response, int status, gpointer data);
diff --git a/src/trg-model.c b/src/trg-model.c
index e41c4b3..5796507 100644
--- a/src/trg-model.c
+++ b/src/trg-model.c
@@ -43,12 +43,13 @@ trg_model_remove_removed_foreachfunc(GtkTreeModel * model,
return FALSE;
}
-void
+guint
trg_model_remove_removed(GtkListStore * model, gint serial_column,
gint64 currentSerial)
{
struct trg_model_remove_removed_foreachfunc_args args;
GList *li;
+ guint removed = 0;
args.toRemove = NULL;
args.currentSerial = currentSerial;
@@ -60,9 +61,12 @@ trg_model_remove_removed(GtkListStore * model, gint serial_column,
li = g_list_previous(li)) {
gtk_list_store_remove(model, (GtkTreeIter *) li->data);
gtk_tree_iter_free((GtkTreeIter *) li->data);
+ removed++;
}
g_list_free(args.toRemove);
}
+
+ return removed;
}
struct find_existing_item_foreach_args {
diff --git a/src/trg-model.h b/src/trg-model.h
index b20cbe4..987cfca 100644
--- a/src/trg-model.h
+++ b/src/trg-model.h
@@ -22,7 +22,7 @@
#include <gtk/gtk.h>
-void trg_model_remove_removed(GtkListStore * model, gint serial_column,
+guint trg_model_remove_removed(GtkListStore * model, gint serial_column,
gint64 currentSerial);
gboolean
diff --git a/src/trg-state-selector.c b/src/trg-state-selector.c
index 1ae4247..f20a407 100644
--- a/src/trg-state-selector.c
+++ b/src/trg-state-selector.c
@@ -18,11 +18,13 @@
*/
#include <glib-object.h>
+#include <json-glib/json-glib.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include "torrent.h"
#include "trg-state-selector.h"
+#include "util.h"
enum {
SELECTOR_STATE_CHANGED,
@@ -38,8 +40,16 @@ typedef struct _TrgStateSelectorPrivate TrgStateSelectorPrivate;
struct _TrgStateSelectorPrivate {
guint flag;
+ GHashTable *trackers;
+ GRegex *urlHostRegex;
};
+GRegex *trg_state_selector_get_url_host_regex(TrgStateSelector *s)
+{
+ TrgStateSelectorPrivate *priv = TRG_STATE_SELECTOR_GET_PRIVATE(s);
+ return priv->urlHostRegex;
+}
+
guint32 trg_state_selector_get_flag(TrgStateSelector * s)
{
TrgStateSelectorPrivate *priv = TRG_STATE_SELECTOR_GET_PRIVATE(s);
@@ -85,6 +95,104 @@ static void state_selection_changed(GtkTreeSelection * selection,
signals[SELECTOR_STATE_CHANGED], 0, priv->flag);
}
+static GtkTreeRowReference *quick_tree_ref_new(GtkTreeModel *model, GtkTreeIter *iter)
+{
+ GtkTreePath *path = gtk_tree_model_get_path(model, iter);
+ GtkTreeRowReference *rr = gtk_tree_row_reference_new(model, path);
+ gtk_tree_path_free(path);
+ return rr;
+}
+
+struct cruft_remove_args {
+ GHashTable *table;
+ gint64 serial;
+};
+
+static gboolean trg_state_selector_remove_cruft(gpointer key, gpointer value, gpointer data)
+{
+ struct cruft_remove_args *args = (struct cruft_remove_args*)data;
+ GtkTreeRowReference *rr = (GtkTreeRowReference*)value;
+ GtkTreeModel *model = gtk_tree_row_reference_get_model(rr);
+ GtkTreePath *path = gtk_tree_row_reference_get_path(rr);
+ gboolean remove;
+
+ GtkTreeIter iter;
+ gint64 currentSerial;
+
+ gtk_tree_model_get_iter(model, &iter, path);
+ gtk_tree_model_get(model, &iter, STATE_SELECTOR_SERIAL, &currentSerial, -1);
+
+ remove = (args->serial != currentSerial);
+
+ gtk_tree_path_free(path);
+
+ return remove;
+}
+
+gchar *trg_state_selector_get_selected_text(TrgStateSelector *s)
+{
+ GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(s));
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gchar *name = NULL;
+
+ if (gtk_tree_selection_get_selected(sel, &model, &iter))
+ gtk_tree_model_get(model, &iter, STATE_SELECTOR_NAME, &name, -1);
+
+ return name;
+}
+
+void trg_state_selector_update_trackers(TrgStateSelector *s, JsonArray *torrents, gint64 serial)
+{
+ TrgStateSelectorPrivate *priv = TRG_STATE_SELECTOR_GET_PRIVATE(s);
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ int i, j;
+ struct cruft_remove_args cruft;
+
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(s));
+
+ for (i = 0; i < json_array_get_length(torrents); i++) {
+ JsonObject *t = json_array_get_object_element(torrents, i);
+ JsonArray *trackers = torrent_get_trackers(t);
+
+ for (j = 0; j < json_array_get_length(trackers); j++) {
+ JsonObject *tracker = json_array_get_object_element(trackers, j);
+ const gchar *announceUrl = tracker_get_announce(tracker);
+ gchar *announceHost = trg_uri_host_extract(priv->urlHostRegex, announceUrl);
+ gpointer result;
+
+ if (!announceHost)
+ continue;
+
+ result = g_hash_table_lookup(priv->trackers, announceHost);
+
+ if (result)
+ {
+ GtkTreeRowReference *rr = (GtkTreeRowReference*)result;
+ GtkTreePath *path = gtk_tree_row_reference_get_path(rr);
+ gtk_tree_model_get_iter(model, &iter, path);
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter,
+ STATE_SELECTOR_SERIAL, serial, -1);
+ gtk_tree_path_free(path);
+ } else {
+ gtk_list_store_append(GTK_LIST_STORE(model), &iter);
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter,
+ STATE_SELECTOR_ICON, GTK_STOCK_NETWORK,
+ STATE_SELECTOR_NAME, announceHost,
+ STATE_SELECTOR_SERIAL, serial,
+ STATE_SELECTOR_BIT, FILTER_FLAG_TRACKER, -1);
+ g_hash_table_insert(priv->trackers, announceHost, quick_tree_ref_new(model, &iter));
+ }
+ }
+ }
+
+ cruft.serial = serial;
+ cruft.table = priv->trackers;
+
+ g_hash_table_foreach_remove(priv->trackers, trg_state_selector_remove_cruft, &cruft);
+}
+
static void trg_state_selector_add_state(GtkListStore * model,
GtkTreeIter * iter, gchar * icon,
gchar * name, guint32 flag)
@@ -96,6 +204,18 @@ static void trg_state_selector_add_state(GtkListStore * model,
STATE_SELECTOR_BIT, flag, -1);
}
+static void remove_row_ref_and_free(GtkTreeRowReference *rr)
+{
+ GtkTreeModel *model = gtk_tree_row_reference_get_model(rr);
+ GtkTreePath *path = gtk_tree_row_reference_get_path(rr);
+ GtkTreeIter iter;
+
+ gtk_tree_model_get_iter(model, &iter, path);
+ gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
+ gtk_tree_path_free(path);
+ gtk_tree_row_reference_free(rr);
+}
+
static void trg_state_selector_init(TrgStateSelector * self)
{
TrgStateSelectorPrivate *priv;
@@ -108,6 +228,9 @@ static void trg_state_selector_init(TrgStateSelector * self)
priv = TRG_STATE_SELECTOR_GET_PRIVATE(self);
priv->flag = 0;
+ priv->urlHostRegex = trg_uri_host_regex_new();
+ priv->trackers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)remove_row_ref_and_free);
+
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(self), FALSE);
column = gtk_tree_view_column_new();
@@ -126,7 +249,7 @@ static void trg_state_selector_init(TrgStateSelector * self)
store =
gtk_list_store_new(STATE_SELECTOR_COLUMNS, G_TYPE_STRING,
- G_TYPE_STRING, G_TYPE_UINT);
+ G_TYPE_STRING, G_TYPE_UINT, G_TYPE_INT64);
trg_state_selector_add_state(store, &iter, GTK_STOCK_ABOUT, _("All"),
0);
@@ -145,6 +268,8 @@ static void trg_state_selector_init(TrgStateSelector * self)
_("Seeding"), TORRENT_FLAG_SEEDING);
trg_state_selector_add_state(store, &iter, GTK_STOCK_DIALOG_WARNING,
_("Error"), TORRENT_FLAG_ERROR);
+ trg_state_selector_add_state(store, &iter, NULL,
+ NULL, 0);
gtk_tree_view_set_model(GTK_TREE_VIEW(self), GTK_TREE_MODEL(store));
gtk_tree_view_set_rubber_banding(GTK_TREE_VIEW(self), TRUE);
diff --git a/src/trg-state-selector.h b/src/trg-state-selector.h
index 944647a..d09d76a 100644
--- a/src/trg-state-selector.h
+++ b/src/trg-state-selector.h
@@ -22,11 +22,13 @@
#define TRG_STATE_LIST_H_
#include <glib-object.h>
+#include <json-glib/json-glib.h>
enum {
STATE_SELECTOR_ICON,
STATE_SELECTOR_NAME,
STATE_SELECTOR_BIT,
+ STATE_SELECTOR_SERIAL,
STATE_SELECTOR_COLUMNS
};
@@ -49,15 +51,17 @@ G_BEGIN_DECLS
typedef struct {
GtkTreeViewClass parent_class;
- /* SIGNALS */
-
void (*torrent_state_changed) (TrgStateSelector * selector,
- guint flag, gpointer data);
+ guint flag, gpointer data);
+
} TrgStateSelectorClass;
GType trg_state_selector_get_type(void);
TrgStateSelector *trg_state_selector_new(void);
G_END_DECLS guint32 trg_state_selector_get_flag(TrgStateSelector * s);
+void trg_state_selector_update_trackers(TrgStateSelector *s, JsonArray *torrents, gint64 serial);
+gchar *trg_state_selector_get_selected_text(TrgStateSelector *s);
+GRegex *trg_state_selector_get_url_host_regex(TrgStateSelector *s);
#endif /* TRG_STATE_LIST_H_ */
diff --git a/src/trg-torrent-model.c b/src/trg-torrent-model.c
index c201fa2..7fa82ae 100644
--- a/src/trg-torrent-model.c
+++ b/src/trg-torrent-model.c
@@ -33,6 +33,7 @@
enum {
TMODEL_TORRENT_COMPLETED,
TMODEL_TORRENT_ADDED,
+ TMODEL_TORRENT_ADDREMOVE,
TMODEL_SIGNAL_COUNT
};
@@ -69,6 +70,16 @@ static void trg_torrent_model_class_init(TrgTorrentModelClass * klass)
torrent_added), NULL,
NULL, g_cclosure_marshal_VOID__POINTER,
G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+ signals[TMODEL_TORRENT_ADDREMOVE] =
+ g_signal_new("torrent-addremove",
+ G_TYPE_FROM_CLASS(object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET(TrgTorrentModelClass,
+ torrent_removed), NULL,
+ NULL, g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
}
static void trg_torrent_model_count_peers(TrgTorrentModel * model,
@@ -272,6 +283,7 @@ void trg_torrent_model_update(TrgTorrentModel * model, trg_client * tc,
{
int i;
JsonArray *newTorrents;
+ gboolean added = FALSE;
newTorrents = get_torrents(get_arguments(response));
stats->count = json_array_get_length(newTorrents);
@@ -287,8 +299,10 @@ void trg_torrent_model_update(TrgTorrentModel * model, trg_client * tc,
TORRENT_COLUMN_ID,
torrent_get_id(t),
&iter) == FALSE) {
+ added = TRUE;
gtk_list_store_append(GTK_LIST_STORE(model), &iter);
update_torrent_iter(tc->updateSerial, model, &iter, t, stats);
+
if (!first)
g_signal_emit(model, signals[TMODEL_TORRENT_ADDED], 0,
&iter);
@@ -304,7 +318,8 @@ void trg_torrent_model_update(TrgTorrentModel * model, trg_client * tc,
tc->torrents = newTorrents;
- trg_model_remove_removed(GTK_LIST_STORE(model),
+ if (trg_model_remove_removed(GTK_LIST_STORE(model),
TORRENT_COLUMN_UPDATESERIAL,
- tc->updateSerial);
+ tc->updateSerial) > 0 || added)
+ g_signal_emit(model, signals[TMODEL_TORRENT_ADDREMOVE], 0);
}
diff --git a/src/trg-torrent-model.h b/src/trg-torrent-model.h
index cc8c695..ee064d2 100644
--- a/src/trg-torrent-model.h
+++ b/src/trg-torrent-model.h
@@ -45,11 +45,13 @@ G_BEGIN_DECLS
typedef struct {
GtkListStoreClass parent_class;
void (*torrent_completed) (TrgTorrentModel * model,
- GtkTreeIter * iter, JsonObject * t,
+ GtkTreeIter * iter,
gpointer data);
void (*torrent_added) (TrgTorrentModel * model,
- GtkTreeIter * iter, JsonObject * t,
+ GtkTreeIter * iter,
gpointer data);
+
+ void (*torrent_removed) (TrgTorrentModel *model, gpointer data);
} TrgTorrentModelClass;
typedef struct {
diff --git a/src/util.c b/src/util.c
index 906cf90..bd20d14 100644
--- a/src/util.c
+++ b/src/util.c
@@ -34,6 +34,23 @@
#include "util.h"
#include "dispatch.h"
+GRegex *trg_uri_host_regex_new(void)
+{
+ return g_regex_new("^[^:/?#]+:?//([^/?#]*)", 0, 0, NULL);
+}
+
+gchar *trg_uri_host_extract(GRegex *rx, const gchar *uri)
+{
+ GMatchInfo *mi = NULL;
+ gchar *host = NULL;
+ g_regex_match (rx, uri, 0, &mi);
+ if (mi) {
+ host = g_match_info_fetch(mi, 1);
+ g_match_info_free (mi);
+ }
+ return host;
+}
+
void trg_error_dialog(GtkWindow * parent, int status,
JsonObject * response)
{
diff --git a/src/util.h b/src/util.h
index 3b63163..6d9e62c 100644
--- a/src/util.h
+++ b/src/util.h
@@ -36,6 +36,9 @@
#define MEGABYTE_FACTOR ( 1024.0 * 1024.0 )
#define GIGABYTE_FACTOR ( 1024.0 * 1024.0 * 1024.0 )
+GRegex *trg_uri_host_regex_new(void);
+gchar *trg_uri_host_extract(GRegex *rx, const gchar *uri);
+
char *tr_strltime_long(char *buf, gint64 seconds, size_t buflen);
char *tr_strltime_short(char *buf, gint64 seconds, size_t buflen);
char *tr_strpercent(char *buf, double x, size_t buflen);