From 834a3f2151dd8738a1f878489f6207664c4af5aa Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Sun, 14 Jul 2013 17:14:53 -0700 Subject: Imported Upstream version 1.1.1 --- src/trg-state-selector.c | 815 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 815 insertions(+) create mode 100644 src/trg-state-selector.c (limited to 'src/trg-state-selector.c') diff --git a/src/trg-state-selector.c b/src/trg-state-selector.c new file mode 100644 index 0000000..cee0c56 --- /dev/null +++ b/src/trg-state-selector.c @@ -0,0 +1,815 @@ +/* + * transmission-remote-gtk - A GTK RPC client to Transmission + * Copyright (C) 2011-2013 Alan Fitton + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include + +#include "torrent.h" +#include "trg-cell-renderer-counter.h" +#include "trg-state-selector.h" +#include "trg-torrent-model.h" +#include "util.h" +#include "trg-prefs.h" +#include "trg-client.h" + +enum { + SELECTOR_STATE_CHANGED, SELECTOR_SIGNAL_COUNT +}; + +enum { + PROP_0, PROP_CLIENT +}; + +static guint signals[SELECTOR_SIGNAL_COUNT] = { 0 }; + +G_DEFINE_TYPE(TrgStateSelector, trg_state_selector, GTK_TYPE_TREE_VIEW) +#define TRG_STATE_SELECTOR_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_STATE_SELECTOR, TrgStateSelectorPrivate)) +typedef struct _TrgStateSelectorPrivate TrgStateSelectorPrivate; + +struct _TrgStateSelectorPrivate { + guint flag; + gboolean showDirs; + gboolean showTrackers; + TrgClient *client; + TrgPrefs *prefs; + GHashTable *trackers; + GHashTable *directories; + GRegex *urlHostRegex; + gint n_categories; + GtkListStore *store; + GtkTreeRowReference *error_rr; + GtkTreeRowReference *all_rr; + GtkTreeRowReference *paused_rr; + GtkTreeRowReference *down_rr; + GtkTreeRowReference *seeding_rr; + GtkTreeRowReference *complete_rr; + GtkTreeRowReference *incomplete_rr; + GtkTreeRowReference *checking_rr; + GtkTreeRowReference *active_rr; + GtkTreeRowReference *seed_wait_rr; + GtkTreeRowReference *down_wait_rr; +}; + +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); + return priv->flag; +} + +static void +state_selection_changed(GtkTreeSelection * selection, gpointer data) +{ + TrgStateSelectorPrivate *priv; + GtkTreeIter iter; + GtkTreeModel *stateModel; + guint index = 0; + + priv = TRG_STATE_SELECTOR_GET_PRIVATE(data); + + if (gtk_tree_selection_get_selected(selection, &stateModel, &iter)) + gtk_tree_model_get(stateModel, &iter, STATE_SELECTOR_BIT, + &priv->flag, STATE_SELECTOR_INDEX, &index, -1); + else + priv->flag = 0; + + trg_prefs_set_int(priv->prefs, TRG_PREFS_STATE_SELECTOR_LAST, index, + TRG_PREFS_GLOBAL); + + g_signal_emit(TRG_STATE_SELECTOR(data), + 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, ¤tSerial, + -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; +} + +static void +trg_state_selector_update_dynamic_filter(GtkTreeModel * model, + GtkTreeRowReference * + rr, gint64 serial) +{ + GtkTreeIter iter; + GtkTreePath *path = gtk_tree_row_reference_get_path(rr); + gint64 oldSerial; + GValue gvalue = { 0 }; + gint oldCount; + + gtk_tree_model_get_iter(model, &iter, path); + gtk_tree_model_get(model, &iter, STATE_SELECTOR_SERIAL, &oldSerial, + STATE_SELECTOR_COUNT, &oldCount, -1); + + if (oldSerial != serial) { + g_value_init(&gvalue, G_TYPE_INT); + g_value_set_int(&gvalue, 1); + gtk_list_store_set_value(GTK_LIST_STORE(model), &iter, + STATE_SELECTOR_COUNT, &gvalue); + + memset(&gvalue, 0, sizeof(GValue)); + g_value_init(&gvalue, G_TYPE_INT64); + g_value_set_int64(&gvalue, serial); + gtk_list_store_set_value(GTK_LIST_STORE(model), &iter, + STATE_SELECTOR_SERIAL, &gvalue); + } else { + g_value_init(&gvalue, G_TYPE_INT); + g_value_set_int(&gvalue, ++oldCount); + gtk_list_store_set_value(GTK_LIST_STORE(model), &iter, + STATE_SELECTOR_COUNT, &gvalue); + } + + gtk_tree_path_free(path); +} + +static void refresh_statelist_cb(GtkWidget * w, gpointer data) +{ + TrgStateSelectorPrivate *priv = TRG_STATE_SELECTOR_GET_PRIVATE(data); + trg_client_inc_serial(priv->client); + trg_state_selector_update(TRG_STATE_SELECTOR(data), + TORRENT_UPDATE_ADDREMOVE); +} + +static void +view_popup_menu(GtkWidget * treeview, GdkEventButton * event, + gpointer data G_GNUC_UNUSED) +{ + GtkWidget *menu, *item; + + menu = gtk_menu_new(); + + item = gtk_image_menu_item_new_with_label(GTK_STOCK_REFRESH); + gtk_image_menu_item_set_use_stock(GTK_IMAGE_MENU_ITEM(item), TRUE); + gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM + (item), TRUE); + g_signal_connect(item, "activate", G_CALLBACK(refresh_statelist_cb), + treeview); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + + gtk_widget_show_all(menu); + + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, + (event != NULL) ? event->button : 0, + gdk_event_get_time((GdkEvent *) event)); +} + +static gboolean view_onPopupMenu(GtkWidget * treeview, gpointer userdata) +{ + view_popup_menu(treeview, NULL, userdata); + return TRUE; +} + +static gboolean +view_onButtonPressed(GtkWidget * treeview, + GdkEventButton * event, gpointer userdata) +{ + if (event->type == GDK_BUTTON_PRESS && event->button == 3) { + view_popup_menu(treeview, event, userdata); + return TRUE; + } + + return FALSE; +} + +struct state_find_pos { + int offset; + int range; + int pos; + const gchar *name; +}; + +static gboolean +trg_state_selector_find_pos_foreach(GtkTreeModel * model, + GtkTreePath * path, + GtkTreeIter * iter, gpointer data) +{ + struct state_find_pos *args = (struct state_find_pos *) data; + gchar *name; + gboolean res; + + if (args->pos < args->offset) { + args->pos++; + return FALSE; + } else if (args->range >= 0 + && args->pos > args->offset + args->range - 1) { + return TRUE; + } + + gtk_tree_model_get(model, iter, STATE_SELECTOR_NAME, &name, -1); + res = g_strcmp0(name, args->name) >= 0; + g_free(name); + + if (!res) + args->pos++; + + return res; +} + +static void +trg_state_selector_insert(TrgStateSelector * s, int offset, + gint range, const gchar * name, + GtkTreeIter * iter) +{ + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(s)); + + struct state_find_pos args; + args.offset = offset; + args.pos = 0; + args.range = range; + args.name = name; + + gtk_tree_model_foreach(model, trg_state_selector_find_pos_foreach, + &args); + gtk_list_store_insert(GTK_LIST_STORE(model), iter, args.pos); +} + +void trg_state_selector_update(TrgStateSelector * s, guint whatsChanged) +{ + TrgStateSelectorPrivate *priv = TRG_STATE_SELECTOR_GET_PRIVATE(s); + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(s)); + TrgClient *client = priv->client; + gint64 updateSerial = trg_client_get_serial(client); + GList *torrentItemRefs; + GtkTreeIter torrentIter, iter; + GList *trackersList, *trackerItem, *li; + GtkTreeRowReference *rr; + GtkTreePath *path; + GtkTreeModel *torrentModel; + gpointer result; + struct cruft_remove_args cruft; + + if (!trg_client_is_connected(client)) + return; + + torrentItemRefs = + g_hash_table_get_values(trg_client_get_torrent_table(client)); + + for (li = torrentItemRefs; li; li = g_list_next(li)) { + JsonObject *t = NULL; + rr = (GtkTreeRowReference *) li->data; + path = gtk_tree_row_reference_get_path(rr); + torrentModel = gtk_tree_row_reference_get_model(rr); + + if (path) { + if (gtk_tree_model_get_iter(torrentModel, &torrentIter, path)) { + gtk_tree_model_get(torrentModel, &torrentIter, + TORRENT_COLUMN_JSON, &t, -1); + } + gtk_tree_path_free(path); + } + + if (!t) + continue; + + if (priv->showTrackers + && (whatsChanged & TORRENT_UPDATE_ADDREMOVE)) { + trackersList = + json_array_get_elements(torrent_get_tracker_stats(t)); + for (trackerItem = trackersList; trackerItem; + trackerItem = g_list_next(trackerItem)) { + JsonObject *tracker = + json_node_get_object((JsonNode *) trackerItem->data); + const gchar *announceUrl = + tracker_stats_get_announce(tracker); + gchar *announceHost = + trg_gregex_get_first(priv->urlHostRegex, + announceUrl); + + if (!announceHost) + continue; + + result = g_hash_table_lookup(priv->trackers, announceHost); + + if (result) { + trg_state_selector_update_dynamic_filter(model, + (GtkTreeRowReference + *) result, + updateSerial); + g_free(announceHost); + } else { + trg_state_selector_insert(s, priv->n_categories, + g_hash_table_size + (priv->trackers), + announceHost, &iter); + gtk_list_store_set(GTK_LIST_STORE(model), &iter, + STATE_SELECTOR_ICON, + GTK_STOCK_NETWORK, + STATE_SELECTOR_NAME, announceHost, + STATE_SELECTOR_SERIAL, updateSerial, + STATE_SELECTOR_COUNT, 1, + STATE_SELECTOR_BIT, + FILTER_FLAG_TRACKER, + STATE_SELECTOR_INDEX, 0, -1); + g_hash_table_insert(priv->trackers, announceHost, + quick_tree_ref_new(model, &iter)); + } + } + g_list_free(trackersList); + } + + if (priv->showDirs && ((whatsChanged & TORRENT_UPDATE_ADDREMOVE) + || (whatsChanged & + TORRENT_UPDATE_PATH_CHANGE))) { + gchar *dir; + gtk_tree_model_get(torrentModel, &torrentIter, + TORRENT_COLUMN_DOWNLOADDIR_SHORT, &dir, -1); + + result = g_hash_table_lookup(priv->directories, dir); + if (result) { + trg_state_selector_update_dynamic_filter(model, + (GtkTreeRowReference + *) result, + updateSerial); + } else { + trg_state_selector_insert(s, + priv->n_categories + + g_hash_table_size + (priv->trackers), -1, dir, + &iter); + gtk_list_store_set(GTK_LIST_STORE(model), &iter, + STATE_SELECTOR_ICON, + GTK_STOCK_DIRECTORY, + STATE_SELECTOR_NAME, dir, + STATE_SELECTOR_SERIAL, updateSerial, + STATE_SELECTOR_BIT, FILTER_FLAG_DIR, + STATE_SELECTOR_COUNT, 1, + STATE_SELECTOR_INDEX, 0, -1); + g_hash_table_insert(priv->directories, g_strdup(dir), + quick_tree_ref_new(model, &iter)); + } + + g_free(dir); + } + } + + g_list_free(torrentItemRefs); + + cruft.serial = trg_client_get_serial(client); + + if (priv->showTrackers && ((whatsChanged & TORRENT_UPDATE_ADDREMOVE))) { + cruft.table = priv->trackers; + g_hash_table_foreach_remove(priv->trackers, + trg_state_selector_remove_cruft, + &cruft); + } + + if (priv->showDirs && ((whatsChanged & TORRENT_UPDATE_ADDREMOVE) + || (whatsChanged & TORRENT_UPDATE_PATH_CHANGE))) { + cruft.table = priv->directories; + g_hash_table_foreach_remove(priv->directories, + trg_state_selector_remove_cruft, + &cruft); + } +} + +void trg_state_selector_set_show_dirs(TrgStateSelector * s, gboolean show) +{ + TrgStateSelectorPrivate *priv = TRG_STATE_SELECTOR_GET_PRIVATE(s); + priv->showDirs = show; + if (!show) + g_hash_table_remove_all(priv->directories); + else + trg_state_selector_update(s, TORRENT_UPDATE_PATH_CHANGE); +} + +static void +on_torrents_state_change(TrgTorrentModel * model, + guint whatsChanged, gpointer data) +{ + TrgStateSelector *selector = TRG_STATE_SELECTOR(data); + trg_state_selector_update(selector, whatsChanged); + + if ((whatsChanged & TORRENT_UPDATE_ADDREMOVE) + || (whatsChanged & TORRENT_UPDATE_STATE_CHANGE)) + trg_state_selector_stats_update(selector, + trg_torrent_model_get_stats + (model)); +} + +void +trg_state_selector_set_show_trackers(TrgStateSelector * s, gboolean show) +{ + TrgStateSelectorPrivate *priv = TRG_STATE_SELECTOR_GET_PRIVATE(s); + priv->showTrackers = show; + if (!show) + g_hash_table_remove_all(priv->trackers); + else + trg_state_selector_update(s, TORRENT_UPDATE_ADDREMOVE); +} + +static void +trg_state_selector_add_state(TrgStateSelector * selector, + GtkTreeIter * iter, gint pos, + gchar * icon, gchar * name, + guint32 flag, GtkTreeRowReference ** rr) +{ + TrgStateSelectorPrivate *priv = + TRG_STATE_SELECTOR_GET_PRIVATE(selector); + GtkListStore *model = + GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(selector))); + + if (pos < 0) + gtk_list_store_append(priv->store, iter); + else + gtk_list_store_insert(priv->store, iter, pos); + + gtk_list_store_set(model, iter, STATE_SELECTOR_ICON, icon, + STATE_SELECTOR_NAME, name, STATE_SELECTOR_BIT, flag, + STATE_SELECTOR_INDEX, + gtk_tree_model_iter_n_children(GTK_TREE_MODEL + (model), NULL) - 1, + -1); + + if (rr) + *rr = quick_tree_ref_new(GTK_TREE_MODEL(model), iter); + + priv->n_categories++; +} + +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_update_stat(GtkTreeRowReference * rr, gint count) +{ + if (rr) { + GValue gvalue = { 0 }; + GtkTreeIter iter; + GtkTreePath *path = gtk_tree_row_reference_get_path(rr); + GtkTreeModel *model = gtk_tree_row_reference_get_model(rr); + + gtk_tree_model_get_iter(model, &iter, path); + + g_value_init(&gvalue, G_TYPE_INT); + g_value_set_int(&gvalue, count); + gtk_list_store_set_value(GTK_LIST_STORE(model), &iter, + STATE_SELECTOR_COUNT, &gvalue); + + gtk_tree_path_free(path); + } +} + +void +trg_state_selector_stats_update(TrgStateSelector * s, + trg_torrent_model_update_stats * stats) +{ + TrgStateSelectorPrivate *priv = TRG_STATE_SELECTOR_GET_PRIVATE(s); + GtkTreeIter iter; + if (stats->error > 0 && !priv->error_rr) { + trg_state_selector_add_state(s, &iter, priv->n_categories - 1, + GTK_STOCK_DIALOG_WARNING, _("Error"), + TORRENT_FLAG_ERROR, &priv->error_rr); + + } else if (stats->error < 1 && priv->error_rr) { + remove_row_ref_and_free(priv->error_rr); + priv->error_rr = NULL; + priv->n_categories--; + } + + trg_state_selector_update_stat(priv->all_rr, stats->count); + trg_state_selector_update_stat(priv->down_rr, stats->down); + trg_state_selector_update_stat(priv->seeding_rr, stats->seeding); + trg_state_selector_update_stat(priv->error_rr, stats->error); + trg_state_selector_update_stat(priv->paused_rr, stats->paused); + trg_state_selector_update_stat(priv->complete_rr, stats->complete); + trg_state_selector_update_stat(priv->incomplete_rr, stats->incomplete); + trg_state_selector_update_stat(priv->active_rr, stats->active); + trg_state_selector_update_stat(priv->checking_rr, stats->checking); + trg_state_selector_update_stat(priv->down_wait_rr, stats->down_wait); + trg_state_selector_update_stat(priv->seed_wait_rr, stats->seed_wait); +} + +void trg_state_selector_disconnect(TrgStateSelector * s) +{ + TrgStateSelectorPrivate *priv = TRG_STATE_SELECTOR_GET_PRIVATE(s); + + if (priv->error_rr) { + remove_row_ref_and_free(priv->error_rr); + priv->error_rr = NULL; + priv->n_categories--; + } + + g_hash_table_remove_all(priv->trackers); + g_hash_table_remove_all(priv->directories); + + trg_state_selector_update_stat(priv->all_rr, -1); + trg_state_selector_update_stat(priv->down_rr, -1); + trg_state_selector_update_stat(priv->seeding_rr, -1); + trg_state_selector_update_stat(priv->error_rr, -1); + trg_state_selector_update_stat(priv->paused_rr, -1); + trg_state_selector_update_stat(priv->complete_rr, -1); + trg_state_selector_update_stat(priv->incomplete_rr, -1); + trg_state_selector_update_stat(priv->active_rr, -1); + trg_state_selector_update_stat(priv->checking_rr, -1); +} + +static void trg_state_selector_init(TrgStateSelector * self) +{ +} + +TrgStateSelector *trg_state_selector_new(TrgClient * client, + TrgTorrentModel * tmodel) +{ + TrgStateSelector *selector = + g_object_new(TRG_TYPE_STATE_SELECTOR, "client", + client, NULL); + g_signal_connect(tmodel, "torrents-state-change", + G_CALLBACK(on_torrents_state_change), selector); + return selector; +} + +static GObject *trg_state_selector_constructor(GType type, + guint + n_construct_properties, + GObjectConstructParam * + construct_params) +{ + GObject *object; + TrgStateSelector *selector; + TrgStateSelectorPrivate *priv; + GtkListStore *store; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + GtkTreeIter iter; + gint index; + GtkTreeSelection *selection; + + object = G_OBJECT_CLASS + (trg_state_selector_parent_class)->constructor(type, + n_construct_properties, + construct_params); + + selector = TRG_STATE_SELECTOR(object); + priv = TRG_STATE_SELECTOR_GET_PRIVATE(object); + + 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); + priv->directories = + 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(object), FALSE); + + column = gtk_tree_view_column_new(); + + renderer = gtk_cell_renderer_pixbuf_new(); + gtk_tree_view_column_pack_start(column, renderer, FALSE); + g_object_set(renderer, "stock-size", 4, NULL); + gtk_tree_view_column_set_attributes(column, renderer, "stock-id", + STATE_SELECTOR_ICON, NULL); + + renderer = trg_cell_renderer_counter_new(); + gtk_tree_view_column_pack_start(column, renderer, TRUE); + gtk_tree_view_column_set_attributes(column, renderer, "state-label", + STATE_SELECTOR_NAME, "state-count", + STATE_SELECTOR_COUNT, NULL); + + gtk_tree_view_append_column(GTK_TREE_VIEW(object), column); + + store = priv->store = gtk_list_store_new(STATE_SELECTOR_COLUMNS, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_INT, G_TYPE_UINT, + G_TYPE_INT64, G_TYPE_UINT); + gtk_tree_view_set_model(GTK_TREE_VIEW(object), GTK_TREE_MODEL(store)); + + trg_state_selector_add_state(selector, &iter, -1, GTK_STOCK_ABOUT, + _("All"), 0, &priv->all_rr); + trg_state_selector_add_state(selector, &iter, -1, GTK_STOCK_GO_DOWN, + _("Downloading"), + TORRENT_FLAG_DOWNLOADING, &priv->down_rr); + trg_state_selector_add_state(selector, &iter, -1, + GTK_STOCK_MEDIA_REWIND, _("Queue Down"), + TORRENT_FLAG_DOWNLOADING_WAIT, + &priv->down_wait_rr); + trg_state_selector_add_state(selector, &iter, -1, GTK_STOCK_GO_UP, + _("Seeding"), TORRENT_FLAG_SEEDING, + &priv->seeding_rr); + trg_state_selector_add_state(selector, &iter, -1, + GTK_STOCK_MEDIA_FORWARD, _("Queue Up"), + TORRENT_FLAG_SEEDING_WAIT, + &priv->seed_wait_rr); + trg_state_selector_add_state(selector, &iter, -1, + GTK_STOCK_MEDIA_PAUSE, _("Paused"), + TORRENT_FLAG_PAUSED, &priv->paused_rr); + trg_state_selector_add_state(selector, &iter, -1, GTK_STOCK_APPLY, + _("Complete"), TORRENT_FLAG_COMPLETE, + &priv->complete_rr); + trg_state_selector_add_state(selector, &iter, -1, GTK_STOCK_SELECT_ALL, + _("Incomplete"), TORRENT_FLAG_INCOMPLETE, + &priv->incomplete_rr); + trg_state_selector_add_state(selector, &iter, -1, GTK_STOCK_NETWORK, + _("Active"), TORRENT_FLAG_ACTIVE, + &priv->active_rr); + trg_state_selector_add_state(selector, &iter, -1, GTK_STOCK_REFRESH, + _("Checking"), TORRENT_FLAG_CHECKING_ANY, + &priv->checking_rr); + trg_state_selector_add_state(selector, &iter, -1, NULL, NULL, 0, NULL); + + gtk_tree_view_set_rubber_banding(GTK_TREE_VIEW(object), TRUE); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(object)); + + g_signal_connect(G_OBJECT(selection), "changed", + G_CALLBACK(state_selection_changed), object); + g_signal_connect(object, "button-press-event", + G_CALLBACK(view_onButtonPressed), NULL); + g_signal_connect(object, "popup-menu", G_CALLBACK(view_onPopupMenu), + NULL); + + gtk_tree_view_set_search_column(GTK_TREE_VIEW(object), + STATE_SELECTOR_NAME); + + index = trg_prefs_get_int(priv->prefs, TRG_PREFS_STATE_SELECTOR_LAST, + TRG_PREFS_GLOBAL); + if (index > 0 + && gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &iter, + NULL, index)) { + GtkTreeSelection *selection = + gtk_tree_view_get_selection(GTK_TREE_VIEW(object)); + gtk_tree_selection_select_iter(selection, &iter); + } + + priv->showDirs = + trg_prefs_get_bool(priv->prefs, TRG_PREFS_KEY_FILTER_DIRS, + TRG_PREFS_GLOBAL); + priv->showTrackers = + trg_prefs_get_bool(priv->prefs, TRG_PREFS_KEY_FILTER_TRACKERS, + TRG_PREFS_GLOBAL); + + return object; +} + +void +trg_state_selector_set_queues_enabled(TrgStateSelector * s, + gboolean enabled) +{ + TrgStateSelectorPrivate *priv = TRG_STATE_SELECTOR_GET_PRIVATE(s); + GtkTreeIter iter; + + if (enabled) { + trg_state_selector_add_state(s, &iter, 2, GTK_STOCK_MEDIA_REWIND, + _("Queue Down"), + TORRENT_FLAG_DOWNLOADING_WAIT, + &priv->down_wait_rr); + trg_state_selector_add_state(s, &iter, 4, GTK_STOCK_MEDIA_FORWARD, + _("Queue Up"), + TORRENT_FLAG_SEEDING_WAIT, + &priv->seed_wait_rr); + } else { + remove_row_ref_and_free(priv->seed_wait_rr); + remove_row_ref_and_free(priv->down_wait_rr); + priv->down_wait_rr = NULL; + priv->seed_wait_rr = NULL; + priv->n_categories -= 2; + } +} + +static void +trg_state_selector_get_property(GObject * object, + guint property_id, + GValue * value, GParamSpec * pspec) +{ + TrgStateSelectorPrivate *priv = TRG_STATE_SELECTOR_GET_PRIVATE(object); + switch (property_id) { + case PROP_CLIENT: + g_value_set_object(value, priv->client); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + break; + } +} + +static void +trg_state_selector_set_property(GObject * object, + guint prop_id, + const GValue * value, + GParamSpec * pspec G_GNUC_UNUSED) +{ + TrgStateSelectorPrivate *priv = TRG_STATE_SELECTOR_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_CLIENT: + priv->client = g_value_get_object(value); + priv->prefs = trg_client_get_prefs(priv->client); + break; + } +} + +static void trg_state_selector_class_init(TrgStateSelectorClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructor = trg_state_selector_constructor; + object_class->set_property = trg_state_selector_set_property; + object_class->get_property = trg_state_selector_get_property; + + signals[SELECTOR_STATE_CHANGED] = g_signal_new("torrent-state-changed", + G_TYPE_FROM_CLASS + (object_class), + G_SIGNAL_RUN_LAST | + G_SIGNAL_ACTION, + G_STRUCT_OFFSET + (TrgStateSelectorClass, + torrent_state_changed), + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, + G_TYPE_UINT); + + g_object_class_install_property(object_class, + PROP_CLIENT, + g_param_spec_object("client", + "Client", + "Client", + TRG_TYPE_CLIENT, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY + | + G_PARAM_STATIC_NAME + | + G_PARAM_STATIC_NICK + | + G_PARAM_STATIC_BLURB)); + + g_type_class_add_private(klass, sizeof(TrgStateSelectorPrivate)); +} -- cgit v1.2.3