From 42eddac0e9f0216175fef223fd9b1023ed502a6e Mon Sep 17 00:00:00 2001 From: Alan Fitton Date: Tue, 12 Apr 2011 10:54:29 +0000 Subject: some quite significant changes to only receive/update recently-active torrents, if enabled. also use a hash table and tree row references for lookup. hopefully performance will be much better for people with large number of torrents. --- src/protocol-constants.h | 2 + src/requests.c | 12 +- src/requests.h | 2 +- src/session-get.c | 2 +- src/torrent.c | 8 ++ src/torrent.h | 1 + src/transmission-remote-gtk.schemas | 13 +++ src/trg-client.c | 3 +- src/trg-client.h | 10 +- src/trg-files-model.c | 18 +-- src/trg-files-model.h | 2 +- src/trg-main-window.c | 101 ++++++++++------ src/trg-main-window.h | 4 - src/trg-menu-bar.c | 25 ++++ src/trg-peers-model.c | 16 +-- src/trg-preferences-dialog.c | 76 +++++-------- src/trg-preferences-dialog.h | 2 +- src/trg-preferences.h | 1 + src/trg-state-selector.c | 29 ++++- src/trg-torrent-add-dialog.c | 34 ++++-- src/trg-torrent-model.c | 221 +++++++++++++++++++++++++++++------- src/trg-torrent-model.h | 5 +- src/trg-torrent-props-dialog.h | 1 - src/trg-torrent-tree-view.c | 12 +- src/trg-trackers-model.c | 13 ++- src/trg-trackers-model.h | 4 +- src/trg-trackers-tree-view.c | 2 +- 27 files changed, 433 insertions(+), 186 deletions(-) (limited to 'src') diff --git a/src/protocol-constants.h b/src/protocol-constants.h index 5299f3b..e61ca50 100644 --- a/src/protocol-constants.h +++ b/src/protocol-constants.h @@ -27,7 +27,9 @@ /* torrents */ +#define FIELD_RECENTLY_ACTIVE "recently-active" #define FIELD_TORRENTS "torrents" /* parent node */ +#define FIELD_REMOVED "removed" #define FIELD_ANNOUNCE_URL "announceUrl" #define FIELD_LEFT_UNTIL_DONE "leftUntilDone" #define FIELD_TOTAL_SIZE "totalSize" diff --git a/src/requests.c b/src/requests.c index d3edc3a..4c841e4 100644 --- a/src/requests.c +++ b/src/requests.c @@ -110,10 +110,18 @@ JsonNode *torrent_remove(JsonArray * array, gboolean removeData) return root; } -JsonNode *torrent_get(void) +JsonNode *torrent_get(gboolean recent) { JsonNode *root = base_request(METHOD_TORRENT_GET); + JsonObject *args = node_get_arguments(root); JsonArray *fields = json_array_new(); + + if (recent) { + JsonArray *ids = json_array_new(); + json_array_add_string_element(ids, FIELD_RECENTLY_ACTIVE); + json_object_set_array_member(args, PARAM_IDS, ids); + } + json_array_add_string_element(fields, FIELD_ETA); json_array_add_string_element(fields, FIELD_PEERS); json_array_add_string_element(fields, FIELD_FILES); @@ -151,7 +159,7 @@ JsonNode *torrent_get(void) json_array_add_string_element(fields, FIELD_ERRORSTR); json_array_add_string_element(fields, FIELD_WANTED); json_array_add_string_element(fields, FIELD_PRIORITIES); - json_object_set_array_member(node_get_arguments(root), + json_object_set_array_member(args, PARAM_FIELDS, fields); return root; } diff --git a/src/requests.h b/src/requests.h index 1b53704..b87cf3f 100644 --- a/src/requests.h +++ b/src/requests.h @@ -27,7 +27,7 @@ JsonNode *generic_request(gchar * method, JsonArray * array); JsonNode *session_set(void); JsonNode *session_get(void); -JsonNode *torrent_get(void); +JsonNode *torrent_get(gboolean recent); JsonNode *torrent_set(JsonArray * array); JsonNode *torrent_pause(JsonArray * array); JsonNode *torrent_start(JsonArray * array); diff --git a/src/session-get.c b/src/session-get.c index dd4bfa1..848e72a 100644 --- a/src/session-get.c +++ b/src/session-get.c @@ -28,7 +28,7 @@ int session_get_version(JsonObject * s, float *version) { const gchar *versionStr = json_object_get_string_member(s, SGET_VERSION); - return sscanf(versionStr, "%f", version); + return sscanf(versionStr, "%g", version); } gboolean session_get_pex_enabled(JsonObject * s) diff --git a/src/torrent.c b/src/torrent.c index 3c6f0d4..4c35e06 100644 --- a/src/torrent.c +++ b/src/torrent.c @@ -265,6 +265,14 @@ const gchar *tracker_get_scrape(JsonObject * t) return json_object_get_string_member(t, FIELD_SCRAPE); } +JsonArray *get_torrents_removed(JsonObject *response) +{ + if (G_UNLIKELY(json_object_has_member(response, FIELD_REMOVED))) + return json_object_get_array_member(response, FIELD_REMOVED); + else + return NULL; +} + JsonArray *get_torrents(JsonObject * response) { return json_object_get_array_member(response, FIELD_TORRENTS); diff --git a/src/torrent.h b/src/torrent.h index 248e0d4..3376dd2 100644 --- a/src/torrent.h +++ b/src/torrent.h @@ -74,5 +74,6 @@ gint64 torrent_get_peer_limit(JsonObject * t); gboolean torrent_has_tracker(JsonObject * t, GRegex * rx, gchar * search); JsonArray *get_torrents(JsonObject * response); +JsonArray *get_torrents_removed(JsonObject *response); #endif /* TORRENT_H_ */ diff --git a/src/transmission-remote-gtk.schemas b/src/transmission-remote-gtk.schemas index 11415a8..7b56d1a 100644 --- a/src/transmission-remote-gtk.schemas +++ b/src/transmission-remote-gtk.schemas @@ -14,6 +14,19 @@ + + /schemas/apps/transmission-remote-gtk/update-active-only + /apps/transmission-remote-gtk/update-active-only + transmission-remote-gtk + bool + 0 + + + Update Active Only + Update Active Only + + + /schemas/apps/transmission-remote-gtk/auto-connect /apps/transmission-remote-gtk/auto-connect diff --git a/src/trg-client.c b/src/trg-client.c index 2ed8ffa..d2e0cf3 100644 --- a/src/trg-client.c +++ b/src/trg-client.c @@ -45,6 +45,7 @@ trg_client *trg_init_client() client = g_new0(trg_client, 1); client->gconf = gconf_client_get_default(); client->updateMutex = g_mutex_new(); + client->activeOnlyUpdate = gconf_client_get_bool(client->gconf, TRG_GCONF_KEY_UPDATE_ACTIVE_ONLY, NULL); return client; } @@ -56,7 +57,7 @@ void trg_client_set_session(trg_client * tc, JsonObject * session) if (tc->session != NULL) json_object_unref(tc->session); - session_get_version(session, &(tc->version)); + session_get_version(session, &tc->version); tc->session = session; } diff --git a/src/trg-client.h b/src/trg-client.h index 51d851d..480d828 100644 --- a/src/trg-client.h +++ b/src/trg-client.h @@ -20,6 +20,13 @@ #ifndef TRG_CLIENT_H_ #define TRG_CLIENT_H_ +#define TRANSMISSION_MIN_SUPPORTED 2.0 + +#define TORRENT_GET_MODE_FIRST 0 +#define TORRENT_GET_MODE_ACTIVE 1 +#define TORRENT_GET_MODE_INTERACTION 2 +#define TORRENT_GET_MODE_UPDATE 3 + #define TRG_GCONF_SCHEMA_ERROR -1 #define TRG_NO_HOSTNAME_SET -2 @@ -30,6 +37,7 @@ typedef struct { char *session_id; + gboolean activeOnlyUpdate; gint failCount; gint interval; gint64 updateSerial; @@ -40,7 +48,7 @@ typedef struct { char *username; char *password; char *proxy; - JsonArray *torrents; + GHashTable *torrentTable; GConfClient *gconf; GMutex *updateMutex; } trg_client; diff --git a/src/trg-files-model.c b/src/trg-files-model.c index 084997d..ccc4757 100644 --- a/src/trg-files-model.c +++ b/src/trg-files-model.c @@ -21,6 +21,7 @@ #include #include "trg-files-model.h" +#include "trg-client.h" #include "torrent.h" #include "tfile.h" #include "util.h" @@ -129,30 +130,29 @@ trg_files_model_update_foreach(GtkListStore * model, void trg_files_model_update(TrgFilesModel * model, gint64 updateSerial, - JsonObject * t, gboolean first) + JsonObject * t, gint mode) { TrgFilesModelPrivate *priv = TRG_FILES_MODEL_GET_PRIVATE(model); - guint j; - - if (first) - gtk_list_store_clear(GTK_LIST_STORE(model)); + GList *li; + gint j = 0; priv->torrentId = torrent_get_id(t); priv->priorities = torrent_get_priorities(t); priv->wanted = torrent_get_wanted(t); priv->files = torrent_get_files(t); - if (first == TRUE) { + if (mode == TORRENT_GET_MODE_FIRST) { + gtk_list_store_clear(GTK_LIST_STORE(model)); priv->accept = TRUE; - for (j = 0; j < json_array_get_length(priv->files); j++) { + for (li = json_array_get_elements(priv->files); li; li = g_list_next(li)) { JsonObject *file = - json_node_get_object(json_array_get_element - (priv->files, j)); + json_node_get_object((JsonNode*)li->data); GtkTreeIter filesIter; trg_files_model_iter_new(model, &filesIter, file, j); trg_files_model_iter_update(model, &filesIter, file, priv->wanted, priv->priorities, j); + j++; } } else { gtk_tree_model_foreach(GTK_TREE_MODEL(model), diff --git a/src/trg-files-model.h b/src/trg-files-model.h index 76dccf7..c47c361 100644 --- a/src/trg-files-model.h +++ b/src/trg-files-model.h @@ -62,7 +62,7 @@ G_END_DECLS enum { void trg_files_model_update(TrgFilesModel * model, gint64 updateSerial, - JsonObject * t, gboolean first); + JsonObject * t, gint mode); gint64 trg_files_model_get_torrent_id(TrgFilesModel * model); void trg_files_model_set_accept(TrgFilesModel * model, gboolean accept); diff --git a/src/trg-main-window.c b/src/trg-main-window.c index f681de5..b1c78d8 100644 --- a/src/trg-main-window.c +++ b/src/trg-main-window.c @@ -68,8 +68,7 @@ #include "trg-remote-prefs-dialog.h" #include "trg-preferences-dialog.h" -static gboolean update_selected_torrent_notebook(TrgMainWindow * win, - gboolean first); +static gboolean update_selected_torrent_notebook(TrgMainWindow * win, gint mode); static void torrent_event_notification(TrgTorrentModel * model, gchar * icon, gchar * desc, gint tmout, gchar * prefKey, @@ -119,6 +118,7 @@ static void on_torrent_get(JsonObject * response, int mode, int status, gpointer data); static void on_torrent_get_first(JsonObject * response, int status, gpointer data); +static void on_torrent_get_active(JsonObject *response, int status, gpointer data); static void on_torrent_get_update(JsonObject * response, int status, gpointer data); static void on_torrent_get_interactive(JsonObject * response, int status, @@ -126,8 +126,7 @@ static void on_torrent_get_interactive(JsonObject * response, int status, static gboolean trg_update_torrents_timerfunc(gpointer data); static void trg_main_window_update_notebook_displays(TrgMainWindow * win, JsonObject * t, - GtkTreeIter * iter, - gboolean first); + GtkTreeIter * iter, gint mode); static void open_about_cb(GtkWidget * w, GtkWindow * parent); static gboolean trg_torrent_tree_view_visible_func(GtkTreeModel * model, GtkTreeIter * iter, @@ -165,7 +164,7 @@ static GtkWidget *trg_imagemenuitem_new(GtkMenuShell * shell, char *text, GCallback cb, gpointer cbdata); static void set_limit_cb(GtkWidget * w, gpointer data); static GtkWidget *limit_item_new(TrgMainWindow * win, GtkWidget * menu, - gint64 currentLimit, gint limit); + gint64 currentLimit, gfloat limit); static GtkWidget *limit_menu_new(TrgMainWindow * win, gchar * title, gchar * enabledKey, gchar * speedKey, JsonArray * ids); @@ -241,8 +240,7 @@ static void trg_main_window_init(TrgMainWindow * self G_GNUC_UNUSED) { } -static gboolean update_selected_torrent_notebook(TrgMainWindow * win, - gboolean first) +static gboolean update_selected_torrent_notebook(TrgMainWindow * win, gint mode) { TrgMainWindowPrivate *priv; GtkTreeIter iter; @@ -261,7 +259,7 @@ static gboolean update_selected_torrent_notebook(TrgMainWindow * win, } if ((priv->selectedTorrentId = newFirstSelected) >= 0) { - trg_main_window_update_notebook_displays(win, json, &iter, first); + trg_main_window_update_notebook_displays(win, json, &iter, mode); return TRUE; } @@ -440,7 +438,6 @@ static void resume_all_cb(GtkWidget *w G_GNUC_UNUSED, gpointer data) { TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data); - g_printf("%s\n", __func__); dispatch_async(priv->client, torrent_start(NULL), on_generic_interactive_action, data); @@ -516,7 +513,7 @@ static void open_local_prefs_cb(GtkWidget * w G_GNUC_UNUSED, gpointer data) GtkWidget *dlg = trg_preferences_dialog_get_instance(TRG_MAIN_WINDOW(data), - priv->client->gconf); + priv->client); gtk_widget_show_all(dlg); } @@ -850,15 +847,32 @@ static void on_session_get(JsonObject * response, int status, newSession = get_arguments(response); if (client->session == NULL) { + float version; + if (session_get_version(newSession, &version)) + { + if (version < TRANSMISSION_MIN_SUPPORTED) { + gchar *msg = g_strdup_printf(_("This application supports Transmission %.2f and later, you have %.2f."), TRANSMISSION_MIN_SUPPORTED, version); + GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(win), + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, "%s", msg); + gtk_window_set_title(GTK_WINDOW(dialog), _("Error")); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + g_free(msg); + goto out; + } + } + trg_status_bar_connect(priv->statusBar, newSession); trg_main_window_conn_changed(win, TRUE); - - dispatch_async(client, torrent_get(), on_torrent_get_first, data); + dispatch_async(client, torrent_get(FALSE), on_torrent_get_first, data); } trg_client_set_session(client, newSession); trg_trackers_tree_view_new_connection(priv->trackersTreeView, client); +out: gdk_threads_leave(); json_object_ref(newSession); response_unref(response); @@ -871,16 +885,13 @@ on_torrent_get(JsonObject * response, int mode, int status, gpointer data) trg_client *client = priv->client; trg_torrent_model_update_stats stats; - gboolean first; /* Disconnected between request and response callback */ - if (client->session == NULL) { + if (!client->session) { response_unref(response); return; } - first = (mode == TORRENT_GET_MODE_FIRST); - g_mutex_lock(client->updateMutex); gdk_threads_enter(); @@ -921,9 +932,10 @@ on_torrent_get(JsonObject * response, int mode, int status, gpointer data) client->updateSerial++; trg_torrent_model_update(priv->torrentModel, priv->client, - response, &stats, first); + response, &stats, mode); + trg_torrent_model_stats_scan(priv->torrentModel, &stats); - update_selected_torrent_notebook(TRG_MAIN_WINDOW(data), first); + update_selected_torrent_notebook(TRG_MAIN_WINDOW(data), mode); trg_status_bar_update(priv->statusBar, &stats); @@ -939,6 +951,12 @@ on_torrent_get(JsonObject * response, int mode, int status, gpointer data) response_unref(response); } +static void +on_torrent_get_active(JsonObject *response, int status, gpointer data) +{ + on_torrent_get(response, TORRENT_GET_MODE_ACTIVE, status, data); +} + static void on_torrent_get_first(JsonObject * response, int status, gpointer data) { @@ -961,8 +979,9 @@ static gboolean trg_update_torrents_timerfunc(gpointer data) { TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data); - if (priv->client->session != NULL) - dispatch_async(priv->client, torrent_get(), on_torrent_get_update, + if (priv->client->session) + dispatch_async(priv->client, torrent_get(priv->client->activeOnlyUpdate), + priv->client->activeOnlyUpdate ? on_torrent_get_active : on_torrent_get_update, data); return FALSE; @@ -971,19 +990,18 @@ static gboolean trg_update_torrents_timerfunc(gpointer data) static void trg_main_window_update_notebook_displays(TrgMainWindow * win, JsonObject * t, - GtkTreeIter * iter, - gboolean first) + GtkTreeIter * iter, gint mode) { TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(win); trg_client *client = priv->client; trg_general_panel_update(priv->genDetails, t, iter); trg_trackers_model_update(priv->trackersModel, client->updateSerial, t, - first); + mode); trg_files_model_update(priv->filesModel, client->updateSerial, t, - first); + mode); trg_peers_model_update(priv->peersModel, client->updateSerial, t, - first); + mode); } static void open_about_cb(GtkWidget * w G_GNUC_UNUSED, GtkWindow * parent) @@ -1094,7 +1112,7 @@ trg_dialog_error_handler(TrgMainWindow * win, JsonObject * response, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", msg); - gtk_window_set_title(GTK_WINDOW(dialog), "Error"); + gtk_window_set_title(GTK_WINDOW(dialog), _("Error")); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); g_free((gpointer) msg); @@ -1116,7 +1134,7 @@ torrent_selection_changed(GtkWidget * w G_GNUC_UNUSED, gpointer data) win = TRG_MAIN_WINDOW(data); client = priv->client; - isSelected = update_selected_torrent_notebook(win, TRUE); + isSelected = update_selected_torrent_notebook(win, TORRENT_GET_MODE_FIRST); trg_toolbar_torrent_actions_sensitive(priv->toolBar, isSelected); trg_menu_bar_torrent_actions_sensitive(priv->menuBar, isSelected); @@ -1140,7 +1158,7 @@ on_generic_interactive_action(JsonObject * response, int status, gdk_threads_leave(); if (status == CURLE_OK || status == FAIL_RESPONSE_UNSUCCESSFUL) - dispatch_async(priv->client, torrent_get(), + dispatch_async(priv->client, torrent_get(FALSE), on_torrent_get_interactive, data); } @@ -1248,7 +1266,8 @@ static TrgMenuBar *trg_main_window_menu_bar_new(TrgMainWindow * win) GObject *b_connect, *b_disconnect, *b_add, *b_resume, *b_pause, *b_verify, *b_remove, *b_delete, *b_props, *b_local_prefs, *b_remote_prefs, *b_about, *b_view_states, *b_view_notebook, - *b_view_stats, *b_add_url, *b_quit, *b_move, *b_reannounce; + *b_view_stats, *b_add_url, *b_quit, *b_move, *b_reannounce, + *b_pause_all, *b_resume_all; TrgMenuBar *menuBar; menuBar = trg_menu_bar_new(win); @@ -1258,7 +1277,9 @@ static TrgMenuBar *trg_main_window_menu_bar_new(TrgMainWindow * win) "add-button", &b_add, "add-url-button", &b_add_url, "resume-button", &b_resume, + "resume-all-button", &b_resume_all, "pause-button", &b_pause, + "pause-all-button", &b_pause_all, "delete-button", &b_delete, "remove-button", &b_remove, "move-button", &b_move, @@ -1278,7 +1299,9 @@ static TrgMenuBar *trg_main_window_menu_bar_new(TrgMainWindow * win) g_signal_connect(b_add, "activate", G_CALLBACK(add_cb), win); g_signal_connect(b_add_url, "activate", G_CALLBACK(add_url_cb), win); g_signal_connect(b_resume, "activate", G_CALLBACK(resume_cb), win); + g_signal_connect(b_resume_all, "activate", G_CALLBACK(resume_all_cb), win); g_signal_connect(b_pause, "activate", G_CALLBACK(pause_cb), win); + g_signal_connect(b_pause_all, "activate", G_CALLBACK(pause_all_cb), win); g_signal_connect(b_verify, "activate", G_CALLBACK(verify_cb), win); g_signal_connect(b_reannounce, "activate", G_CALLBACK(reannounce_cb), win); @@ -1380,16 +1403,21 @@ static void set_limit_cb(GtkWidget * w, gpointer data) } static GtkWidget *limit_item_new(TrgMainWindow * win, GtkWidget * menu, - gint64 currentLimit, gint limit) + gint64 currentLimit, gfloat limit) { char speed[32]; GtkWidget *item; gboolean active = limit < 0 ? FALSE : (currentLimit == (gint64) limit); - g_snprintf(speed, sizeof(speed), "%d KB/s", limit); + if (limit >= 1000) + g_snprintf(speed, sizeof(speed), "%.2f MB/s", limit / 1000); + else + g_snprintf(speed, sizeof(speed), "%.0f KB/s", limit); + item = gtk_check_menu_item_new_with_label(speed); - g_object_set_data(G_OBJECT(item), "limit", GINT_TO_POINTER(limit)); + /* Yeah, I know it's unsafe to cast from a float to an int, but its safe here */ + g_object_set_data(G_OBJECT(item), "limit", GINT_TO_POINTER((gint)limit)); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), active); g_signal_connect(item, "activate", G_CALLBACK(set_limit_cb), win); @@ -1452,6 +1480,13 @@ static GtkWidget *limit_menu_new(TrgMainWindow * win, gchar * title, limit_item_new(win, menu, limit, 300); limit_item_new(win, menu, limit, 400); limit_item_new(win, menu, limit, 500); + limit_item_new(win, menu, limit, 750); + limit_item_new(win, menu, limit, 1000); + limit_item_new(win, menu, limit, 1250); + limit_item_new(win, menu, limit, 1500); + limit_item_new(win, menu, limit, 2000); + limit_item_new(win, menu, limit, 2500); + limit_item_new(win, menu, limit, 3000); gtk_menu_item_set_submenu(GTK_MENU_ITEM(toplevel), menu); @@ -1766,6 +1801,8 @@ static GObject *trg_main_window_constructor(GType type, G_CALLBACK(window_state_event), NULL); priv->torrentModel = trg_torrent_model_new(); + priv->client->torrentTable = get_torrent_table(priv->torrentModel); + g_signal_connect(priv->torrentModel, "torrent-completed", G_CALLBACK(on_torrent_completed), self); g_signal_connect(priv->torrentModel, "torrent-added", diff --git a/src/trg-main-window.h b/src/trg-main-window.h index 09a2439..564dc24 100644 --- a/src/trg-main-window.h +++ b/src/trg-main-window.h @@ -53,10 +53,6 @@ typedef struct { GtkWindowClass parent_class; } TrgMainWindowClass; -#define TORRENT_GET_MODE_FIRST 0 -#define TORRENT_GET_MODE_INTERACTION 1 -#define TORRENT_GET_MODE_UPDATE 2 - #define TORRENT_COMPLETE_NOTIFY_TMOUT 8000 #define TORRENT_ADD_NOTIFY_TMOUT 3000 diff --git a/src/trg-menu-bar.c b/src/trg-menu-bar.c index 9b81154..b86b0c7 100644 --- a/src/trg-menu-bar.c +++ b/src/trg-menu-bar.c @@ -31,7 +31,9 @@ enum { PROP_REMOVE_BUTTON, PROP_DELETE_BUTTON, PROP_RESUME_BUTTON, + PROP_RESUME_ALL_BUTTON, PROP_PAUSE_BUTTON, + PROP_PAUSE_ALL_BUTTON, PROP_VERIFY_BUTTON, PROP_REANNOUNCE_BUTTON, PROP_PROPS_BUTTON, @@ -60,6 +62,8 @@ struct _TrgMenuBarPrivate { GtkWidget *mb_delete; GtkWidget *mb_resume; GtkWidget *mb_pause; + GtkWidget *mb_resume_all; + GtkWidget *mb_pause_all; GtkWidget *mb_verify; GtkWidget *mb_reannounce; GtkWidget *mb_props; @@ -82,6 +86,8 @@ void trg_menu_bar_connected_change(TrgMenuBar * mb, gboolean connected) gtk_widget_set_sensitive(priv->mb_disconnect, connected); gtk_widget_set_sensitive(priv->mb_remote_prefs, connected); gtk_widget_set_sensitive(priv->mb_view_stats, connected); + gtk_widget_set_sensitive(priv->mb_resume_all, connected); + gtk_widget_set_sensitive(priv->mb_pause_all, connected); } void trg_menu_bar_torrent_actions_sensitive(TrgMenuBar * mb, @@ -129,9 +135,15 @@ trg_menu_bar_get_property(GObject * object, guint property_id, case PROP_RESUME_BUTTON: g_value_set_object(value, priv->mb_resume); break; + case PROP_RESUME_ALL_BUTTON: + g_value_set_object(value, priv->mb_resume_all); + break; case PROP_PAUSE_BUTTON: g_value_set_object(value, priv->mb_pause); break; + case PROP_PAUSE_ALL_BUTTON: + g_value_set_object(value, priv->mb_pause_all); + break; case PROP_VERIFY_BUTTON: g_value_set_object(value, priv->mb_verify); break; @@ -312,6 +324,15 @@ GtkWidget *trg_menu_bar_torrent_menu_new(TrgMenuBarPrivate * priv) _("Remove and Delete"), GTK_STOCK_DELETE, FALSE); + gtk_menu_shell_append(GTK_MENU_SHELL(torrentMenu), gtk_separator_menu_item_new()); + + priv->mb_resume_all = + trg_menu_bar_item_new(GTK_MENU_SHELL(torrentMenu), _("_Resume All"), + GTK_STOCK_MEDIA_PLAY, FALSE); + priv->mb_pause_all = + trg_menu_bar_item_new(GTK_MENU_SHELL(torrentMenu), _("_Pause All"), + GTK_STOCK_MEDIA_PAUSE, FALSE); + return torrent; } @@ -357,11 +378,15 @@ static void trg_menu_bar_class_init(TrgMenuBarClass * klass) "delete-button", "Delete Button"); trg_menu_bar_install_widget_prop(object_class, PROP_RESUME_BUTTON, "resume-button", "Resume Button"); + trg_menu_bar_install_widget_prop(object_class, PROP_RESUME_ALL_BUTTON, + "resume-all-button", "Resume All Button"); trg_menu_bar_install_widget_prop(object_class, PROP_VERIFY_BUTTON, "verify-button", "Verify Button"); trg_menu_bar_install_widget_prop(object_class, PROP_REANNOUNCE_BUTTON, "reannounce-button", "Re-announce Button"); + trg_menu_bar_install_widget_prop(object_class, PROP_PAUSE_ALL_BUTTON, + "pause-all-button", "Pause All Button"); trg_menu_bar_install_widget_prop(object_class, PROP_PAUSE_BUTTON, "pause-button", "Pause Button"); trg_menu_bar_install_widget_prop(object_class, PROP_PROPS_BUTTON, diff --git a/src/trg-peers-model.c b/src/trg-peers-model.c index ee48d4d..a96b44f 100644 --- a/src/trg-peers-model.c +++ b/src/trg-peers-model.c @@ -31,6 +31,7 @@ #endif #include "torrent.h" +#include "trg-client.h" #include "tpeer.h" #include "trg-peers-model.h" #include "trg-model.h" @@ -123,7 +124,7 @@ static void resolved_dns_cb(GObject * source_object, } void trg_peers_model_update(TrgPeersModel * model, gint64 updateSerial, - JsonObject * t, gboolean first) + JsonObject * t, gint mode) { #ifdef HAVE_GEOIP TrgPeersModelPrivate *priv = TRG_PEERS_MODEL_GET_PRIVATE(model); @@ -131,23 +132,22 @@ void trg_peers_model_update(TrgPeersModel * model, gint64 updateSerial, JsonArray *peers; GtkTreeIter peerIter; - guint j; + GList *li; gboolean isNew; peers = torrent_get_peers(t); - if (first == TRUE) + if (mode == TORRENT_GET_MODE_FIRST) gtk_list_store_clear(GTK_LIST_STORE(model)); - for (j = 0; j < json_array_get_length(peers); j++) { - JsonObject *peer; + for (li = json_array_get_elements(peers); li; li = g_list_next(li)) { + JsonObject *peer = json_node_get_object((JsonNode*)li->data); const gchar *address = NULL, *flagStr; #ifdef HAVE_GEOIP const gchar *country = NULL; #endif - peer = json_node_get_object(json_array_get_element(peers, j)); - if (first == TRUE + if (mode == TORRENT_GET_MODE_FIRST || find_existing_peer_item(model, peer, &peerIter) == FALSE) { gtk_list_store_append(GTK_LIST_STORE(model), &peerIter); @@ -204,7 +204,7 @@ void trg_peers_model_update(TrgPeersModel * model, gint64 updateSerial, } } - if (first == FALSE) + if (mode != TORRENT_GET_MODE_FIRST) trg_model_remove_removed(GTK_LIST_STORE(model), PEERSCOL_UPDATESERIAL, updateSerial); } diff --git a/src/trg-preferences-dialog.c b/src/trg-preferences-dialog.c index d3c7979..0563949 100644 --- a/src/trg-preferences-dialog.c +++ b/src/trg-preferences-dialog.c @@ -42,7 +42,6 @@ G_DEFINE_TYPE(TrgPreferencesDialog, trg_preferences_dialog, enum { PROP_0, - PROP_GCONF_CLIENT, PROP_TRG_CLIENT, PROP_MAIN_WINDOW }; @@ -50,7 +49,6 @@ enum { #define GCONF_OBJECT_KEY "gconf-key" struct _TrgPreferencesDialogPrivate { - GConfClient *gconf; TrgMainWindow *win; trg_client *client; }; @@ -67,9 +65,6 @@ trg_preferences_dialog_set_property(GObject * object, TRG_PREFERENCES_DIALOG_GET_PRIVATE(object); switch (prop_id) { - case PROP_GCONF_CLIENT: - priv->gconf = g_value_get_object(value); - break; case PROP_MAIN_WINDOW: priv->win = g_value_get_object(value); break; @@ -97,9 +92,6 @@ trg_preferences_dialog_get_property(GObject * object, TRG_PREFERENCES_DIALOG_GET_PRIVATE(object); switch (prop_id) { - case PROP_GCONF_CLIENT: - g_value_set_object(value, priv->gconf); - break; case PROP_MAIN_WINDOW: g_value_set_object(value, priv->win); break; @@ -109,13 +101,16 @@ trg_preferences_dialog_get_property(GObject * object, } } -static void toggled_cb(GtkToggleButton * w, gpointer gconf) +static void update_activeonly_cb(GtkToggleButton *w, gpointer data) { - const char *key; - gboolean flag; + trg_client *client = (trg_client*)data; + client->activeOnlyUpdate = gtk_toggle_button_get_active(w); +} - key = g_object_get_data(G_OBJECT(w), GCONF_OBJECT_KEY); - flag = gtk_toggle_button_get_active(w); +static void toggled_cb(GtkToggleButton * w, gpointer gconf) +{ + const char *key = g_object_get_data(G_OBJECT(w), GCONF_OBJECT_KEY); + gboolean flag = gtk_toggle_button_get_active(w); gconf_client_set_bool(GCONF_CLIENT(gconf), key, flag, NULL); } @@ -235,12 +230,14 @@ static void toggle_tray_icon(GtkToggleButton * w, gpointer win) trg_main_window_remove_status_icon(TRG_MAIN_WINDOW(win)); } -static GtkWidget *trg_prefs_desktopPage(GConfClient * gconf, +static GtkWidget *trg_prefs_desktopPage(trg_client *client, TrgMainWindow * win) { GtkWidget *tray, *w, *t; gint row = 0; + GConfClient *gconf = client->gconf; + t = hig_workarea_create(); hig_workarea_add_section_title(t, &row, _("Features")); @@ -300,7 +297,7 @@ static GtkWidget *trg_prefs_desktopPage(GConfClient * gconf, return t; } -static GtkWidget *trg_prefs_behaviorPage(GConfClient * gconf) +static GtkWidget *trg_prefs_behaviorPage(trg_client *client) { GtkWidget *w, *t; gint row = 0; @@ -309,23 +306,24 @@ static GtkWidget *trg_prefs_behaviorPage(GConfClient * gconf) hig_workarea_add_section_title(t, &row, _("Torrents")); - w = new_check_button(gconf, _("Start paused"), + w = new_check_button(client->gconf, _("Start paused"), TRG_GCONF_KEY_START_PAUSED); hig_workarea_add_wide_control(t, &row, w); - w = new_check_button(gconf, _("Options dialog on add"), + w = new_check_button(client->gconf, _("Options dialog on add"), TRG_GCONF_KEY_ADD_OPTIONS_DIALOG); hig_workarea_add_wide_control(t, &row, w); return t; } -static GtkWidget *trg_prefs_serverPage(GConfClient * gconf, - trg_client * client) +static GtkWidget *trg_prefs_serverPage(trg_client * client) { GtkWidget *w, *t; gint row = 0; + GConfClient *gconf = client->gconf; + t = hig_workarea_create(); hig_workarea_add_section_title(t, &row, _("Server")); @@ -348,6 +346,10 @@ static GtkWidget *trg_prefs_serverPage(GConfClient * gconf, client); hig_workarea_add_row(t, &row, _("Update interval:"), w, NULL); + w = new_check_button(gconf, _("Update active torrents only"), TRG_GCONF_KEY_UPDATE_ACTIVE_ONLY); + g_signal_connect(w, "toggled", G_CALLBACK(update_activeonly_cb), client); + hig_workarea_add_wide_control(t, &row, w); + hig_workarea_add_section_divider(t, &row); hig_workarea_add_section_title(t, &row, _("Authentication")); @@ -393,16 +395,15 @@ static GObject *trg_preferences_dialog_constructor(GType type, notebook = gtk_notebook_new(); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), - trg_prefs_serverPage(priv->gconf, - priv->client), + trg_prefs_serverPage(priv->client), gtk_label_new(_("Connection"))); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), - trg_prefs_desktopPage(priv->gconf, priv->win), + trg_prefs_desktopPage(priv->client, priv->win), gtk_label_new(_("Desktop"))); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), - trg_prefs_behaviorPage(priv->gconf), + trg_prefs_behaviorPage(priv->client), gtk_label_new(_("Behavior"))); gtk_container_set_border_width(GTK_CONTAINER(notebook), GUI_PAD); @@ -415,6 +416,10 @@ static GObject *trg_preferences_dialog_constructor(GType type, return object; } +static void trg_preferences_dialog_init(TrgPreferencesDialog * pref_dlg) +{ +} + static void trg_preferences_dialog_class_init(TrgPreferencesDialogClass * class) { @@ -435,22 +440,6 @@ trg_preferences_dialog_class_init(TrgPreferencesDialogClass * class) G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); - g_object_class_install_property(g_object_class, - PROP_GCONF_CLIENT, - g_param_spec_object("gconf-client", - "GConf Client", - "GConf Client", - GCONF_TYPE_CLIENT, - G_PARAM_READWRITE - | - G_PARAM_CONSTRUCT_ONLY - | - G_PARAM_STATIC_NAME - | - G_PARAM_STATIC_NICK - | - G_PARAM_STATIC_BLURB)); - g_object_class_install_property(g_object_class, PROP_MAIN_WINDOW, g_param_spec_object @@ -466,18 +455,13 @@ trg_preferences_dialog_class_init(TrgPreferencesDialogClass * class) sizeof(TrgPreferencesDialogPrivate)); } -static void trg_preferences_dialog_init(TrgPreferencesDialog * pref_dlg) -{ -} - GtkWidget *trg_preferences_dialog_get_instance(TrgMainWindow * win, - GConfClient * client) + trg_client * client) { if (instance == NULL) { instance = g_object_new(TRG_TYPE_PREFERENCES_DIALOG, "main-window", win, - "trg-client", client, - "gconf-client", client, NULL); + "trg-client", client, NULL); } return GTK_WIDGET(instance); diff --git a/src/trg-preferences-dialog.h b/src/trg-preferences-dialog.h index 5a493b4..d99f911 100644 --- a/src/trg-preferences-dialog.h +++ b/src/trg-preferences-dialog.h @@ -50,6 +50,6 @@ struct _TrgPreferencesDialogClass { GType trg_preferences_dialog_get_type(void); GtkWidget *trg_preferences_dialog_get_instance(TrgMainWindow * win, - GConfClient * client); + trg_client * client); G_END_DECLS #endif /* TRG_PREFERENCES_WINDOW_H_ */ diff --git a/src/trg-preferences.h b/src/trg-preferences.h index f04657f..bd37195 100644 --- a/src/trg-preferences.h +++ b/src/trg-preferences.h @@ -42,6 +42,7 @@ #define TRG_GCONF_KEY_LAST_TORRENT_DIR "/apps/transmission-remote-gtk/last-torrent-dir" #define TRG_GCONF_KEY_ADD_OPTIONS_DIALOG "/apps/transmission-remote-gtk/add-options-dialog" #define TRG_GCONF_KEY_START_PAUSED "/apps/transmission-remote-gtk/start-paused" +#define TRG_GCONF_KEY_UPDATE_ACTIVE_ONLY "/apps/transmission-remote-gtk/update-active-only" gboolean pref_get_start_paused(GConfClient * gcc); gboolean pref_get_add_options_dialog(GConfClient * gcc); diff --git a/src/trg-state-selector.c b/src/trg-state-selector.c index 80fadde..8a6c4bc 100644 --- a/src/trg-state-selector.c +++ b/src/trg-state-selector.c @@ -24,6 +24,7 @@ #include "torrent.h" #include "trg-state-selector.h" +#include "trg-torrent-model.h" #include "util.h" #include "trg-preferences.h" #include "trg-client.h" @@ -216,22 +217,38 @@ void trg_state_selector_update(TrgStateSelector * s) GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(s)); trg_client *client = priv->client; GtkTreeIter iter; - int i, j; + GList *trackerItem, *li; + GList *torrentItemRefs = g_hash_table_get_values(client->torrentTable); + struct cruft_remove_args cruft; if (!client->session) return; - for (i = 0; i < json_array_get_length(client->torrents); i++) { - JsonObject *t = json_array_get_object_element(client->torrents, i); + for (li = torrentItemRefs; li; li = g_list_next(li)) { + GtkTreeRowReference *rr = (GtkTreeRowReference*)li->data; + GtkTreePath *path = gtk_tree_row_reference_get_path(rr); + GtkTreeModel *torrentModel = gtk_tree_row_reference_get_model(rr); + JsonObject *t = NULL; gpointer result; + if (path) { + GtkTreeIter iter; + if (gtk_tree_model_get_iter(torrentModel, &iter, path)) { + gtk_tree_model_get(torrentModel, &iter, TORRENT_COLUMN_JSON, &t, -1); + } + gtk_tree_path_free(path); + } + + if (!t) + continue; + if (priv->showTrackers) { JsonArray *trackers = torrent_get_trackers(t); - for (j = 0; j < json_array_get_length(trackers); j++) { + for (trackerItem = json_array_get_elements(trackers); trackerItem; trackerItem = g_list_next(trackerItem)) { JsonObject *tracker = - json_array_get_object_element(trackers, j); + json_node_get_object((JsonNode*)trackerItem->data); const gchar *announceUrl = tracker_get_announce(tracker); gchar *announceHost = trg_gregex_get_first(priv->urlHostRegex, announceUrl); @@ -290,6 +307,8 @@ void trg_state_selector_update(TrgStateSelector * s) } } + g_list_free(torrentItemRefs); + cruft.serial = client->updateSerial; if (priv->showTrackers) { diff --git a/src/trg-torrent-add-dialog.c b/src/trg-torrent-add-dialog.c index 446f41e..06b1c9b 100644 --- a/src/trg-torrent-add-dialog.c +++ b/src/trg-torrent-add-dialog.c @@ -189,25 +189,39 @@ static GtkWidget *trg_destination_folder_new(trg_client * client) { const gchar *defaultDownDir = json_object_get_string_member(client->session, SGET_DOWNLOAD_DIR); GtkWidget *combo = gtk_combo_box_entry_new_text(); - int i; GSList *dirs = NULL; - GSList *li; + GSList *sli; + GList *li; + GList *torrentItemRefs; g_slist_str_set_add(&dirs, defaultDownDir); g_mutex_lock(client->updateMutex); - for (i = 0; i < json_array_get_length(client->torrents); i++) { - JsonObject *t = json_node_get_object(json_array_get_element(client->torrents, i)); - const gchar *dd = torrent_get_download_dir(t); - if (dd) { - g_printf("dd: %s\n", dd); - g_slist_str_set_add(&dirs, dd); + torrentItemRefs = g_hash_table_get_values(client->torrentTable); + for (li = torrentItemRefs; li; li = g_list_next(li)) { + GtkTreeRowReference *rr = (GtkTreeRowReference*)li->data; + GtkTreeModel *model = gtk_tree_row_reference_get_model(rr); + GtkTreePath *path = gtk_tree_row_reference_get_path(rr); + JsonObject *t = NULL; + + if (path) { + GtkTreeIter iter; + if (gtk_tree_model_get_iter(model, &iter, path)) { + const gchar *dd; + gtk_tree_model_get(model, &iter, TORRENT_COLUMN_JSON, &t, -1); + dd = torrent_get_download_dir(t); + if (dd) + g_slist_str_set_add(&dirs, dd); + + } + gtk_tree_path_free(path); } } + g_list_free(torrentItemRefs); g_mutex_unlock(client->updateMutex); - for (li = dirs; li != NULL; li = g_slist_next(li)) - gtk_combo_box_append_text(GTK_COMBO_BOX(combo), (gchar*)li->data); + for (sli = dirs; sli != NULL; sli = g_slist_next(sli)) + gtk_combo_box_append_text(GTK_COMBO_BOX(combo), (gchar*)sli->data); gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0); g_str_slist_free(dirs); diff --git a/src/trg-torrent-model.c b/src/trg-torrent-model.c index ca33d6c..12da8de 100644 --- a/src/trg-torrent-model.c +++ b/src/trg-torrent-model.c @@ -41,11 +41,27 @@ static guint signals[TMODEL_SIGNAL_COUNT] = { 0 }; G_DEFINE_TYPE(TrgTorrentModel, trg_torrent_model, GTK_TYPE_LIST_STORE) -static guint32 torrent_get_flags(JsonObject * t, gint64 status, - trg_torrent_model_update_stats * stats); +#define TRG_TORRENT_MODEL_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_TORRENT_MODEL, TrgTorrentModelPrivate)) + +typedef struct _TrgTorrentModelPrivate TrgTorrentModelPrivate; + +struct _TrgTorrentModelPrivate { + GHashTable *ht; +}; + +static void +trg_torrent_model_dispose (GObject *object) +{ + TrgTorrentModelPrivate *priv = TRG_TORRENT_MODEL_GET_PRIVATE(object); + g_hash_table_destroy(priv->ht); + G_OBJECT_CLASS (trg_torrent_model_parent_class)->dispose (object); +} + +static guint32 torrent_get_flags(JsonObject * t, gint64 status); static void -update_torrent_iter(gint64 serial, TrgTorrentModel * model, +update_torrent_iter(TrgTorrentModel * model, gint64 serial, GtkTreeIter * iter, JsonObject * t, trg_torrent_model_update_stats * stats); @@ -53,6 +69,9 @@ static void trg_torrent_model_class_init(TrgTorrentModelClass * klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); + g_type_class_add_private(klass, sizeof(TrgTorrentModelPrivate)); + object_class->dispose = trg_torrent_model_dispose; + signals[TMODEL_TORRENT_COMPLETED] = g_signal_new("torrent-completed", G_TYPE_FROM_CLASS(object_class), @@ -87,17 +106,15 @@ static void trg_torrent_model_count_peers(TrgTorrentModel * model, { JsonArray *peers; gint seeders, leechers; - guint j; + GList *li; peers = torrent_get_peers(t); seeders = 0; leechers = 0; - for (j = 0; j < json_array_get_length(peers); j++) { - JsonObject *peer; - - peer = json_node_get_object(json_array_get_element(peers, j)); + for (li = json_array_get_elements(peers); li; li = g_list_next(li)) { + JsonObject *peer = json_node_get_object((JsonNode*)li->data); if (peer_get_is_downloading_from(peer)) seeders++; @@ -111,8 +128,26 @@ static void trg_torrent_model_count_peers(TrgTorrentModel * model, TORRENT_COLUMN_LEECHERS, leechers, -1); } +static void trg_torrent_model_ref_free(gpointer data) +{ + GtkTreeRowReference *rr = (GtkTreeRowReference*)data; + GtkTreeModel *model = gtk_tree_row_reference_get_model(rr); + GtkTreePath *path = gtk_tree_row_reference_get_path(rr); + if (path) { + GtkTreeIter iter; + if (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_torrent_model_init(TrgTorrentModel * self) { + TrgTorrentModelPrivate *priv = TRG_TORRENT_MODEL_GET_PRIVATE(self); + GType column_types[TORRENT_COLUMN_COLUMNS]; column_types[TORRENT_COLUMN_ICON] = G_TYPE_STRING; @@ -136,24 +171,22 @@ static void trg_torrent_model_init(TrgTorrentModel * self) gtk_list_store_set_column_types(GTK_LIST_STORE(self), TORRENT_COLUMN_COLUMNS, column_types); + + priv->ht = g_hash_table_new_full(g_int64_hash, g_int64_equal, (GDestroyNotify)g_free, trg_torrent_model_ref_free); } -static guint32 torrent_get_flags(JsonObject * t, gint64 status, - trg_torrent_model_update_stats * stats) +static guint32 torrent_get_flags(JsonObject * t, gint64 status) { guint32 flags = 0; switch (status) { case STATUS_DOWNLOADING: flags |= TORRENT_FLAG_DOWNLOADING; - stats->down++; break; case STATUS_PAUSED: flags |= TORRENT_FLAG_PAUSED; - stats->paused++; break; case STATUS_SEEDING: flags |= TORRENT_FLAG_SEEDING; - stats->seeding++; break; case STATUS_CHECKING: flags |= TORRENT_FLAG_CHECKING; @@ -175,12 +208,35 @@ static guint32 torrent_get_flags(JsonObject * t, gint64 status, return flags; } +static gboolean +trg_torrent_model_stats_scan_foreachfunc(GtkTreeModel * model, + GtkTreePath * path G_GNUC_UNUSED, + GtkTreeIter * iter, gpointer gdata) +{ + trg_torrent_model_update_stats *stats = (trg_torrent_model_update_stats*)gdata; + guint flags; + gtk_tree_model_get(model, iter, TORRENT_COLUMN_FLAGS, &flags, -1); + if (flags & TORRENT_FLAG_SEEDING) + stats->seeding++; + else if (flags & TORRENT_FLAG_DOWNLOADING) + stats->down++; + else if (flags & TORRENT_FLAG_PAUSED) + stats->paused++; + return FALSE; +} + +void trg_torrent_model_stats_scan(TrgTorrentModel *model, trg_torrent_model_update_stats *stats) +{ + gtk_tree_model_foreach(GTK_TREE_MODEL(model), trg_torrent_model_stats_scan_foreachfunc, stats); +} + static void -update_torrent_iter(gint64 serial, TrgTorrentModel * model, +update_torrent_iter(TrgTorrentModel * model, gint64 serial, GtkTreeIter * iter, JsonObject * t, trg_torrent_model_update_stats * stats) { guint lastFlags, newFlags; + JsonObject *lastJson; gchar *statusString, *statusIcon; gint64 downRate, upRate, downloaded, uploaded, id, status; @@ -197,11 +253,14 @@ update_torrent_iter(gint64 serial, TrgTorrentModel * model, status = torrent_get_status(t); statusString = torrent_get_status_string(status); - newFlags = torrent_get_flags(t, status, stats); + newFlags = torrent_get_flags(t, status); statusIcon = torrent_get_status_icon(newFlags); gtk_tree_model_get(GTK_TREE_MODEL(model), iter, - TORRENT_COLUMN_FLAGS, &lastFlags, -1); + TORRENT_COLUMN_FLAGS, &lastFlags, + TORRENT_COLUMN_JSON, &lastJson, -1); + + json_object_ref(t); #ifdef DEBUG gtk_list_store_set(GTK_LIST_STORE(model), iter, @@ -264,6 +323,9 @@ update_torrent_iter(gint64 serial, TrgTorrentModel * model, TORRENT_COLUMN_UPDATESERIAL, serial, -1); #endif + if (lastJson) + json_object_unref(lastJson); + if ((lastFlags & TORRENT_FLAG_DOWNLOADING) && (newFlags & TORRENT_FLAG_COMPLETE)) g_signal_emit(model, signals[TMODEL_TORRENT_COMPLETED], 0, iter); @@ -279,50 +341,123 @@ TrgTorrentModel *trg_torrent_model_new(void) return g_object_new(TRG_TYPE_TORRENT_MODEL, NULL); } +struct TrgModelRemoveData { + GList *toRemove; + gint64 currentSerial; +}; + +GHashTable *get_torrent_table(TrgTorrentModel *model) +{ + TrgTorrentModelPrivate *priv = TRG_TORRENT_MODEL_GET_PRIVATE(model); + return priv->ht; +} + +gboolean +trg_model_find_removed_foreachfunc(GtkTreeModel * model, + GtkTreePath * path G_GNUC_UNUSED, + GtkTreeIter * iter, gpointer gdata) +{ + struct TrgModelRemoveData *args = (struct TrgModelRemoveData *)gdata; + gint64 rowSerial; + gtk_tree_model_get(model, iter, TORRENT_COLUMN_UPDATESERIAL, &rowSerial, -1); + if (rowSerial != args->currentSerial) { + gint64 *id = g_new(gint64, 1); + gtk_tree_model_get(model, iter, TORRENT_COLUMN_ID, id, -1); + args->toRemove = + g_list_append(args->toRemove, id); + } + + return FALSE; +} + +GList *trg_torrent_model_find_removed(GtkTreeModel * model, + gint64 currentSerial) +{ + struct TrgModelRemoveData args; + + args.toRemove = NULL; + args.currentSerial = currentSerial; + gtk_tree_model_foreach(GTK_TREE_MODEL(model), + trg_model_find_removed_foreachfunc, &args); + + return args.toRemove; +} + void trg_torrent_model_update(TrgTorrentModel * model, trg_client * tc, JsonObject * response, trg_torrent_model_update_stats * stats, - gboolean first) + gint mode) { - int i; - JsonArray *newTorrents; + TrgTorrentModelPrivate *priv = TRG_TORRENT_MODEL_GET_PRIVATE(model); + + JsonObject *args, *t; + GList *li; + gint64 id; + gint64 *idCopy; + JsonArray *newTorrents, *removedTorrents; + GtkTreeIter iter; + GtkTreePath *path; + GtkTreeRowReference *rr; + gpointer *result; gboolean added = FALSE; + gboolean removed = FALSE; - newTorrents = get_torrents(get_arguments(response)); - stats->count = json_array_get_length(newTorrents); + args = get_arguments(response); + newTorrents = get_torrents(args); - for (i = 0; i < stats->count; i++) { - GtkTreeIter iter; - JsonObject *t; + for (li = json_array_get_elements(newTorrents); li; li = g_list_next(li)) { + t = json_node_get_object((JsonNode*)li->data); + id = torrent_get_id(t); - t = json_array_get_object_element(newTorrents, i); + result = mode == TORRENT_GET_MODE_FIRST ? NULL : g_hash_table_lookup(priv->ht, &id); - if (first == TRUE - || find_existing_model_item(GTK_TREE_MODEL(model), - TORRENT_COLUMN_ID, - torrent_get_id(t), - &iter) == FALSE) { - added = TRUE; + if (!result) { gtk_list_store_append(GTK_LIST_STORE(model), &iter); - update_torrent_iter(tc->updateSerial, model, &iter, t, stats); - if (!first) + update_torrent_iter(model, tc->updateSerial, &iter, t, stats); + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(model), &iter); + rr = gtk_tree_row_reference_new(GTK_TREE_MODEL(model), path); + idCopy = g_new(gint64, 1); + *idCopy = id; + g_hash_table_insert(priv->ht, idCopy, rr); + gtk_tree_path_free(path); + added = TRUE; + if (mode != TORRENT_GET_MODE_FIRST) g_signal_emit(model, signals[TMODEL_TORRENT_ADDED], 0, &iter); } else { - update_torrent_iter(tc->updateSerial, model, &iter, t, stats); + path = gtk_tree_row_reference_get_path((GtkTreeRowReference*)result); + if (path) { + if (gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path)) { + update_torrent_iter(model, tc->updateSerial, &iter, t, stats); + } + gtk_tree_path_free(path); + } } } - json_array_ref(newTorrents); - - if (tc->torrents != NULL) - json_array_unref(tc->torrents); - - tc->torrents = newTorrents; + if (mode == TORRENT_GET_MODE_ACTIVE) { + removedTorrents = get_torrents_removed(args); + if (removedTorrents) { + for (li = json_array_get_elements(removedTorrents); li != NULL; li = g_list_next(li)) { + id = json_node_get_int((JsonNode*)li->data); + g_hash_table_remove(priv->ht, &id); + removed = TRUE; + } + } + } else if (mode >= TORRENT_GET_MODE_INTERACTION) { + GList *hitlist = trg_torrent_model_find_removed(GTK_TREE_MODEL(model), tc->updateSerial); + if (hitlist) { + for (li = hitlist; li; li = g_list_next(li)) { + g_hash_table_remove(priv->ht, li->data); + g_free(li->data); + } + removed = TRUE; + g_list_free(hitlist); + } + } - if (trg_model_remove_removed(GTK_LIST_STORE(model), - TORRENT_COLUMN_UPDATESERIAL, - tc->updateSerial) > 0 || added) + if (added || removed) g_signal_emit(model, signals[TMODEL_TORRENT_ADDREMOVE], 0); } diff --git a/src/trg-torrent-model.h b/src/trg-torrent-model.h index eb58449..2f9b99a 100644 --- a/src/trg-torrent-model.h +++ b/src/trg-torrent-model.h @@ -73,7 +73,10 @@ find_existing_peer_item(GtkListStore * model, JsonObject * p, void trg_torrent_model_update(TrgTorrentModel * model, trg_client * tc, JsonObject * response, trg_torrent_model_update_stats * stats, - gboolean first); + gint mode); + +void trg_torrent_model_stats_scan(TrgTorrentModel *model, trg_torrent_model_update_stats *stats); +GHashTable *get_torrent_table(TrgTorrentModel *model); enum { TORRENT_COLUMN_ICON, diff --git a/src/trg-torrent-props-dialog.h b/src/trg-torrent-props-dialog.h index e8bce84..9d1efed 100644 --- a/src/trg-torrent-props-dialog.h +++ b/src/trg-torrent-props-dialog.h @@ -17,7 +17,6 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ - #ifndef TRG_TORRENT_PROPS_DIALOG_H_ #define TRG_TORRENT_PROPS_DIALOG_H_ diff --git a/src/trg-torrent-tree-view.c b/src/trg-torrent-tree-view.c index 196a74d..5c22fd4 100644 --- a/src/trg-torrent-tree-view.c +++ b/src/trg-torrent-tree-view.c @@ -65,7 +65,6 @@ static void trg_torrent_tree_view_init(TrgTorrentTreeView * tv) TORRENT_COLUMN_ADDED, -1); gtk_tree_view_set_search_column(GTK_TREE_VIEW(tv), TORRENT_COLUMN_NAME); - } gint get_first_selected(trg_client * client, TrgTorrentTreeView * view, @@ -76,7 +75,6 @@ gint get_first_selected(trg_client * client, TrgTorrentTreeView * view, GList *selectionList; GList *firstNode; gint64 id = -1; - gint64 updateSerial = -1; model = gtk_tree_view_get_model(GTK_TREE_VIEW(view)); @@ -87,14 +85,8 @@ gint get_first_selected(trg_client * client, TrgTorrentTreeView * view, if (gtk_tree_model_get_iter (model, iter, (GtkTreePath *) firstNode->data)) { gtk_tree_model_get(model, iter, TORRENT_COLUMN_JSON, json, - TORRENT_COLUMN_ID, &id, - TORRENT_COLUMN_UPDATESERIAL, &updateSerial, - -1); - - /* This is about to be removed and won't have valid JSON pointed - * to by the model. */ - if (updateSerial < client->updateSerial) - id = -1; + TORRENT_COLUMN_ID, &id, -1); + } } diff --git a/src/trg-trackers-model.c b/src/trg-trackers-model.c index 3ea8254..7a8feb7 100644 --- a/src/trg-trackers-model.c +++ b/src/trg-trackers-model.c @@ -22,6 +22,7 @@ #include "config.h" #include "torrent.h" +#include "trg-client.h" #include "trg-model.h" #include "trg-trackers-model.h" @@ -49,16 +50,16 @@ gint64 trg_trackers_model_get_torrent_id(TrgTrackersModel * model) void trg_trackers_model_update(TrgTrackersModel * model, gint64 updateSerial, JsonObject * t, - gboolean first) + gint mode) { TrgTrackersModelPrivate *priv = TRG_TRACKERS_MODEL_GET_PRIVATE(model); - guint j; JsonArray *trackers; + GList *li; const gchar *announce; const gchar *scrape; - if (first) { + if (mode == TORRENT_GET_MODE_FIRST) { gtk_list_store_clear(GTK_LIST_STORE(model)); priv->torrentId = torrent_get_id(t); priv->accept = TRUE; @@ -68,16 +69,16 @@ void trg_trackers_model_update(TrgTrackersModel * model, trackers = torrent_get_trackers(t); - for (j = 0; j < json_array_get_length(trackers); j++) { + for (li = json_array_get_elements(trackers); li; li = g_list_next(li)) { GtkTreeIter trackIter; JsonObject *tracker = - json_node_get_object(json_array_get_element(trackers, j)); + json_node_get_object((JsonNode*)li->data); gint64 trackerId = tracker_get_id(tracker); announce = tracker_get_announce(tracker); scrape = tracker_get_scrape(tracker); - if (first + if (mode == TORRENT_GET_MODE_FIRST || find_existing_model_item(GTK_TREE_MODEL(model), TRACKERCOL_ID, trackerId, &trackIter) == FALSE) diff --git a/src/trg-trackers-model.h b/src/trg-trackers-model.h index 324d270..61a64a9 100644 --- a/src/trg-trackers-model.h +++ b/src/trg-trackers-model.h @@ -48,9 +48,9 @@ GType trg_trackers_model_get_type(void); TrgTrackersModel *trg_trackers_model_new(void); G_END_DECLS - void trg_trackers_model_update(TrgTrackersModel * model, +void trg_trackers_model_update(TrgTrackersModel * model, gint64 updateSerial, JsonObject * t, - gboolean first); + gint mode); void trg_trackers_model_set_accept(TrgTrackersModel * model, gboolean accept); gint64 trg_trackers_model_get_torrent_id(TrgTrackersModel * model); diff --git a/src/trg-trackers-tree-view.c b/src/trg-trackers-tree-view.c index f5fe71a..acc20eb 100644 --- a/src/trg-trackers-tree-view.c +++ b/src/trg-trackers-tree-view.c @@ -105,7 +105,7 @@ static void trg_tracker_announce_edited(GtkCellRendererText * renderer, req = torrent_set(torrentIds); args = node_get_arguments(req); - if (g_strcmp0(icon, GTK_STOCK_ADD) == 0) { + if (!g_strcmp0(icon, GTK_STOCK_ADD)) { json_array_add_string_element(trackerModifiers, new_text); json_object_set_array_member(args, "trackerAdd", trackerModifiers); } else { -- cgit v1.2.3