diff options
Diffstat (limited to 'src/trg-main-window.c')
-rw-r--r-- | src/trg-main-window.c | 1449 |
1 files changed, 1449 insertions, 0 deletions
diff --git a/src/trg-main-window.c b/src/trg-main-window.c new file mode 100644 index 0000000..caacd72 --- /dev/null +++ b/src/trg-main-window.c @@ -0,0 +1,1449 @@ +/* + * transmission-remote-gtk - A GTK RPC client to Transmission + * Copyright (C) 2010 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. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> +#include <glib.h> +#include <glib/gprintf.h> +#include <glib/gstdio.h> +#include <gtk/gtk.h> +#include <json-glib/json-glib.h> +#include <gdk/gdkkeysyms.h> +#include <curl/curl.h> +#include <libnotify/notify.h> + +#include "dispatch.h" +#include "trg-client.h" +#include "http.h" +#include "json.h" + +#include "trg-main-window.h" +#include "trg-about-window.h" +#include "trg-tree-view.h" +#include "trg-preferences.h" +#include "trg-torrent-model.h" +#include "trg-torrent-tree-view.h" +#include "trg-peers-model.h" +#include "trg-peers-tree-view.h" +#include "trg-files-tree-view.h" +#include "trg-files-model.h" +#include "trg-trackers-tree-view.h" +#include "trg-trackers-model.h" +#include "trg-state-selector.h" +#include "trg-torrent-props-dialog.h" +#include "trg-torrent-add-url-dialog.h" +#include "trg-toolbar.h" +#include "trg-menu-bar.h" +#include "trg-status-bar.h" +#include "trg-remote-prefs-dialog.h" + +#include "util.h" +#include "requests.h" +#include "trg-preferences-dialog.h" +#include "session-get.h" + +/* Events */ + +static void on_torrent_get(JsonObject * response, int status, + gpointer data); + +static void on_torrent_get_first(JsonObject * response, int status, + gpointer data); + +static void on_torrent_get_multipurpose(JsonObject * response, + gboolean first, int status, + gpointer data); + +static void trg_main_window_conn_changed(TrgMainWindow * win, + gboolean connected); +static void +trg_main_window_update_notebook_displays(TrgMainWindow * win, + JsonObject * t, + GtkTreeIter * iter, + gboolean first); + +static gboolean torrent_selection_changed(GtkWidget * w, gpointer data); + +static gboolean trg_update_torrents_timerfunc(gpointer data); + +static void destroy_window(); + +static void on_torrent_completed(TrgTorrentModel * model, + GtkTreeIter * iter, gpointer * data); + +static void on_session_get(JsonObject * response, int status, + gpointer data); + +static void entry_filter_changed_cb(GtkWidget * w, gpointer data); + +/* Actions */ + +static void connect_cb(GtkWidget * w, gpointer data); +static void disconnect_cb(GtkWidget * w, gpointer data); +static void add_cb(GtkWidget * w, gpointer data); +static void resume_cb(GtkWidget * w, gpointer data); +static void pause_cb(GtkWidget * w, gpointer data); +static void verify_cb(GtkWidget * w, gpointer data); +static void remove_cb(GtkWidget * w, gpointer data); +static void delete_cb(GtkWidget * w, gpointer data); +static void open_about_cb(GtkWidget * w, GtkWindow * parent); + +/* Utility */ + +static +void trg_main_window_torrent_scrub(TrgMainWindow * win); +static const gchar *make_error_message(JsonObject * response, int status); +static GtkWidget *my_scrolledwin_new(GtkWidget * child); +static gboolean +trg_dialog_error_handler(TrgMainWindow * win, JsonObject * response, + int status); +static void response_unref(JsonObject * response); + +G_DEFINE_TYPE(TrgMainWindow, trg_main_window, GTK_TYPE_WINDOW) +#define TRG_MAIN_WINDOW_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_MAIN_WINDOW, TrgMainWindowPrivate)) +typedef struct _TrgMainWindowPrivate TrgMainWindowPrivate; + +struct _TrgMainWindowPrivate { + trg_client *client; + TrgToolbar *toolBar; + TrgMenuBar *menuBar; + + TrgStatusBar *statusBar; + GtkStatusIcon *statusIcon; + TrgStateSelector *stateSelector; + TrgGeneralPanel *genDetails; + GtkWidget *notebook; + + TrgTorrentModel *torrentModel; + TrgTorrentTreeView *torrentTreeView; + GtkTreeModel *filteredTorrentModel; + GtkTreeModel *sortedTorrentModel; + gint selectedTorrentId; + + TrgTrackersModel *trackersModel; + TrgTrackersTreeView *trackersTreeView; + + TrgFilesModel *filesModel; + TrgFilesTreeView *filesTreeView; + + TrgPeersModel *peersModel; + TrgPeersTreeView *peersTreeView; + + GtkWidget *hpaned, *vpaned; + GtkWidget *filterEntry, *filterEntryClearButton; +}; + +enum { + PROP_0, + PROP_CLIENT +}; + +static void trg_main_window_init(TrgMainWindow * self) +{ +} + +static gboolean update_selected_torrent_notebook(TrgMainWindow * win, + gboolean first) +{ + TrgMainWindowPrivate *priv; + GtkTreeIter iter; + gint newFirstSelected; + JsonObject *json = NULL; + + priv = TRG_MAIN_WINDOW_GET_PRIVATE(win); + newFirstSelected = + get_first_selected(priv->torrentTreeView, &iter, &json); + + if (priv->selectedTorrentId >= 0 + && (priv->selectedTorrentId != newFirstSelected + || newFirstSelected < 0)) { + trg_main_window_torrent_scrub(win); + } + + if ((priv->selectedTorrentId = newFirstSelected) >= 0) { + trg_main_window_update_notebook_displays(win, json, &iter, first); + return TRUE; + } + + return FALSE; +} + +static void response_unref(JsonObject * response) +{ + if (response != NULL) + json_object_unref(response); +} + +static void on_torrent_completed(TrgTorrentModel * model, + GtkTreeIter * iter, gpointer * data) +{ + TrgMainWindowPrivate *priv; + gchar *name; + NotifyNotification *notify; + + priv = TRG_MAIN_WINDOW_GET_PRIVATE(data); + + if (!gtk_status_icon_is_embedded(priv->statusIcon)) + return; + + gtk_tree_model_get(GTK_TREE_MODEL(model), iter, + TORRENT_COLUMN_NAME, &name, -1); + + notify = + notify_notification_new(name, + "This torrent has finished downloading", + GTK_STOCK_APPLY, NULL); + + notify_notification_attach_to_status_icon(notify, priv->statusIcon); + notify_notification_set_urgency(notify, NOTIFY_URGENCY_LOW); + notify_notification_set_timeout(notify, 8000); + + g_free(name); + + notify_notification_show(notify, NULL); +} + +static gboolean delete_event(GtkWidget * w, GdkEvent * event, + gpointer * data) +{ + TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(w); + int width, height; + gtk_window_get_size(GTK_WINDOW(w), &width, &height); + gconf_client_set_int(priv->client->gconf, TRG_GCONF_KEY_WINDOW_HEIGHT, + height, NULL); + gconf_client_set_int(priv->client->gconf, TRG_GCONF_KEY_WINDOW_WIDTH, + width, NULL); + + return FALSE; +} + +static void destroy_window(GtkWidget * w, gpointer data) +{ + gtk_main_quit(); +} + +static const gchar *make_error_message(JsonObject * response, int status) +{ + if (status == FAIL_JSON_DECODE) { + return g_strdup("JSON decoding error."); + } else if (status == FAIL_RESPONSE_UNSUCCESSFUL) { + const gchar *resultStr = + json_object_get_string_member(response, "result"); + if (resultStr == NULL) + return g_strdup("Server responded, but with no result."); + else + return g_strdup(resultStr); + } else if (status <= -100) { + return g_strdup_printf("Request failed with HTTP code %d", + -(status + 100)); + } else { + return g_strdup(curl_easy_strerror(status)); + } +} + +static void open_props_cb(GtkWidget * w, gpointer data) +{ + TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data); + TrgTorrentPropsDialog *dialog = + trg_torrent_props_dialog_new(GTK_WINDOW(data), + priv->torrentTreeView, + priv->client); + + gtk_widget_show_all(GTK_WIDGET(dialog)); +} + +/* Use synchronous dispatch() in our dedicated thread function. + * This means torrents are added in sequence, instead of dispatch_async() + * working concurrently for each upload. + */ + +static gpointer add_files_threadfunc(gpointer data) +{ + JsonObject *response; + JsonNode *request; + struct add_torrent_threadfunc_args *args; + gint status; + GSList *li; + + args = (struct add_torrent_threadfunc_args *) data; + + for (li = args->list; li != NULL; li = g_slist_next(li)) { + request = torrent_add((gchar *) li->data, FALSE); + g_free(li->data); + response = dispatch(args->client, request, &status); + on_generic_interactive_action(response, status, args->cb_data); + } + + g_slist_free(args->list); + g_free(args); + + return NULL; +} + +static void add_url_cb(GtkWidget * w, gpointer data) +{ + TrgMainWindow *win = TRG_MAIN_WINDOW(data); + TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data); + + TrgTorrentAddUrlDialog *dlg = + trg_torrent_add_url_dialog_new(win, priv->client); + gtk_widget_show_all(GTK_WIDGET(dlg)); +} + +static void add_cb(GtkWidget * w, gpointer data) +{ + TrgMainWindowPrivate *priv; + GtkWidget *dialog; + GtkFileFilter *filter; + + priv = TRG_MAIN_WINDOW_GET_PRIVATE(data); + + dialog = gtk_file_chooser_dialog_new("Open File", + GTK_WINDOW(data), + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, + GTK_RESPONSE_ACCEPT, NULL); + gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE); + + filter = gtk_file_filter_new(); + gtk_file_filter_add_pattern(filter, "*.torrent"); + gtk_file_filter_set_name(filter, "BitTorrent Metadata"); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + GThread *thread; + GError *error = NULL; + struct add_torrent_threadfunc_args *args; + + args = g_new(struct add_torrent_threadfunc_args, 1); + args->list = + gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); + args->cb_data = data; + args->client = priv->client; + + thread = + g_thread_create(add_files_threadfunc, args, FALSE, &error); + if (error != NULL) { + g_printf("thread creation error: %s\n", error->message); + g_error_free(error); + g_free(args); + } + } + + gtk_widget_destroy(dialog); +} + +static void pause_cb(GtkWidget * w, gpointer data) +{ + TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data); + + dispatch_async(priv->client, + torrent_pause(build_json_id_array + (priv->torrentTreeView)), + on_generic_interactive_action, data); +} + +gboolean trg_add_from_filename(TrgMainWindow * win, gchar * fileName) +{ + TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(win); + + if (g_file_test(fileName, G_FILE_TEST_EXISTS) == TRUE) { + JsonNode *torrentAddReq = torrent_add(fileName, FALSE); + dispatch_async(priv->client, torrentAddReq, + on_generic_interactive_action, win); + return TRUE; + } + + return FALSE; +} + +static void resume_cb(GtkWidget * w, gpointer data) +{ + TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data); + + dispatch_async(priv->client, + torrent_start(build_json_id_array + (priv->torrentTreeView)), + on_generic_interactive_action, data); +} + +static void disconnect_cb(GtkWidget * w, gpointer data) +{ + TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data); + + trg_main_window_conn_changed(TRG_MAIN_WINDOW(data), FALSE); + trg_status_bar_push_connection_msg(priv->statusBar, "Disconnected."); +} + +static void connect_cb(GtkWidget * w, gpointer data) +{ + TrgMainWindowPrivate *priv; + + priv = TRG_MAIN_WINDOW_GET_PRIVATE(data); + + if (trg_client_populate_with_settings + (priv->client, priv->client->gconf) == FALSE) { + GtkWidget *dialog; + + trg_status_bar_push_connection_msg(priv->statusBar, + "Unable to get gconf settings."); + + dialog = + gtk_message_dialog_new(GTK_WINDOW(data), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "Unable to retrieve connection settings from GConf. Schema not installed?"); + gtk_dialog_run(GTK_DIALOG(dialog)); + + return; + } + + trg_status_bar_push_connection_msg(priv->statusBar, "Connecting..."); + dispatch_async(priv->client, session_get(), on_session_get, data); +} + +static void open_local_prefs_cb(GtkWidget * w, gpointer data) +{ + TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data); + + GtkWidget *dlg = trg_preferences_dialog_get_instance(GTK_WINDOW(data), + priv->client-> + gconf); + gtk_widget_show_all(dlg); +} + +static void open_remote_prefs_cb(GtkWidget * w, gpointer data) +{ + TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data); + + TrgRemotePrefsDialog *dlg = + trg_remote_prefs_dialog_get_instance(TRG_MAIN_WINDOW(data), + priv->client); + + gtk_widget_show_all(GTK_WIDGET(dlg)); +} + +static TrgToolbar *trg_main_window_toolbar_new(TrgMainWindow * win) +{ + GObject *b_connect, *b_disconnect, *b_add, *b_resume, *b_pause; + GObject *b_remove, *b_delete, *b_props, *b_local_prefs, + *b_remote_prefs; + + TrgToolbar *toolBar = trg_toolbar_new(); + g_object_get(toolBar, + "connect-button", &b_connect, + "disconnect-button", &b_disconnect, + "add-button", &b_add, + "resume-button", &b_resume, + "pause-button", &b_pause, + "delete-button", &b_delete, + "remove-button", &b_remove, + "props-button", &b_props, + "remote-prefs-button", &b_remote_prefs, + "local-prefs-button", &b_local_prefs, NULL); + + g_signal_connect(b_connect, "clicked", G_CALLBACK(connect_cb), win); + g_signal_connect(b_disconnect, "clicked", + G_CALLBACK(disconnect_cb), win); + g_signal_connect(b_add, "clicked", G_CALLBACK(add_cb), win); + g_signal_connect(b_resume, "clicked", G_CALLBACK(resume_cb), win); + g_signal_connect(b_pause, "clicked", G_CALLBACK(pause_cb), win); + g_signal_connect(b_delete, "clicked", G_CALLBACK(delete_cb), win); + g_signal_connect(b_remove, "clicked", G_CALLBACK(remove_cb), win); + g_signal_connect(b_props, "clicked", G_CALLBACK(open_props_cb), win); + g_signal_connect(b_local_prefs, "clicked", + G_CALLBACK(open_local_prefs_cb), win); + g_signal_connect(b_remote_prefs, "clicked", + G_CALLBACK(open_remote_prefs_cb), win); + + return toolBar; +} + +static void verify_cb(GtkWidget * w, gpointer data) +{ + TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data); + + g_printf("verify_cb()\n"); + dispatch_async(priv->client, + torrent_verify(build_json_id_array + (priv->torrentTreeView)), + on_generic_interactive_action, data); +} + +static gint confirm_action_dialog(GtkWindow * win, + GtkTreeSelection * selection, + gchar * question_single, + gchar * question_multi, + gchar * action_stock) +{ + TrgMainWindowPrivate *priv; + gint selectCount; + gint response; + GtkWidget *dialog = NULL; + + priv = TRG_MAIN_WINDOW_GET_PRIVATE(win); + + selectCount = gtk_tree_selection_count_selected_rows(selection); + + if (selectCount == 1) { + GList *list; + GList *firstNode; + GtkTreeIter firstIter; + gchar *name = NULL; + + list = gtk_tree_selection_get_selected_rows(selection, NULL); + firstNode = g_list_first(list); + + gtk_tree_model_get_iter(GTK_TREE_MODEL + (priv->sortedTorrentModel), + &firstIter, firstNode->data); + gtk_tree_model_get(GTK_TREE_MODEL + (priv->sortedTorrentModel), + &firstIter, TORRENT_COLUMN_NAME, &name, -1); + g_list_free(list); + + dialog = + gtk_message_dialog_new_with_markup(GTK_WINDOW(win), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_NONE, + question_single, name); + g_free(name); + } else if (selectCount > 1) { + dialog = + gtk_message_dialog_new_with_markup(GTK_WINDOW(win), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_NONE, + question_multi, + selectCount); + + } else { + return 0; + } + + gtk_dialog_add_buttons(GTK_DIALOG(dialog), + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + action_stock, GTK_RESPONSE_ACCEPT, NULL); + gtk_dialog_set_default_response(GTK_DIALOG(dialog), + GTK_RESPONSE_CANCEL); + gtk_dialog_set_alternative_button_order(GTK_DIALOG(dialog), + GTK_RESPONSE_ACCEPT, + GTK_RESPONSE_CANCEL, -1); + + response = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + return response; +} + +static void remove_cb(GtkWidget * w, gpointer data) +{ + TrgMainWindowPrivate *priv; + GtkTreeSelection *selection; + + priv = TRG_MAIN_WINDOW_GET_PRIVATE(data); + + selection = + gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->torrentTreeView)); + + if (confirm_action_dialog + (GTK_WINDOW(data), selection, + "<big><b>Remove torrent \"%s\"?</b></big>", + "<big><b>Remove %d torrents?</b></big>", + GTK_STOCK_REMOVE) == GTK_RESPONSE_ACCEPT) + dispatch_async(priv->client, + torrent_remove(build_json_id_array + (priv->torrentTreeView), + FALSE), + on_generic_interactive_action, data); +} + +static void delete_cb(GtkWidget * w, gpointer data) +{ + TrgMainWindowPrivate *priv; + GtkTreeSelection *selection; + + priv = TRG_MAIN_WINDOW_GET_PRIVATE(data); + + selection = + gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->torrentTreeView)); + + if (confirm_action_dialog + (GTK_WINDOW(data), selection, + "<big><b>Remove and delete torrent \"%s\"?</b></big>", + "<big><b>Remove and delete %d torrents?</b></big>", + GTK_STOCK_DELETE) == GTK_RESPONSE_ACCEPT) + dispatch_async(priv->client, + torrent_remove(build_json_id_array + (priv->torrentTreeView), + TRUE), + on_generic_interactive_action, data); +} + +static +GtkWidget *my_scrolledwin_new(GtkWidget * child) +{ + GtkWidget *scrolled_win = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_win), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_container_add(GTK_CONTAINER(scrolled_win), child); + return scrolled_win; +} + +static void view_states_toggled_cb(GtkCheckMenuItem * w, gpointer data) +{ + TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data); + gtk_widget_set_visible(GTK_WIDGET(priv->stateSelector), + gtk_check_menu_item_get_active(w)); +} + +static void view_notebook_toggled_cb(GtkCheckMenuItem * w, gpointer data) +{ + TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data); + gtk_widget_set_visible(priv->notebook, + gtk_check_menu_item_get_active(w)); +} + +static +GtkWidget *trg_main_window_notebook_new(TrgMainWindow * win) +{ + TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(win); + + GtkWidget *notebook = gtk_notebook_new(); + + gtk_widget_set_size_request(notebook, -1, 200); + + priv->genDetails = trg_general_panel_new(priv->sortedTorrentModel); + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), + GTK_WIDGET(priv->genDetails), + gtk_label_new("General")); + + priv->trackersModel = trg_trackers_model_new(); + priv->trackersTreeView = + trg_trackers_tree_view_new(priv->trackersModel); + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), + my_scrolledwin_new(GTK_WIDGET + (priv->trackersTreeView)), + gtk_label_new("Trackers")); + + priv->filesModel = trg_files_model_new(); + priv->filesTreeView = + trg_files_tree_view_new(priv->filesModel, win, priv->client); + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), + my_scrolledwin_new(GTK_WIDGET + (priv->filesTreeView)), + gtk_label_new("Files")); + + priv->peersModel = trg_peers_model_new(); + priv->peersTreeView = trg_peers_tree_view_new(priv->peersModel); + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), + my_scrolledwin_new(GTK_WIDGET + (priv->peersTreeView)), + gtk_label_new("Peers")); + + return notebook; +} + +void on_session_set(JsonObject * response, int status, gpointer data) +{ + TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data); + + gdk_threads_enter(); + trg_dialog_error_handler(TRG_MAIN_WINDOW(data), response, status); + gdk_threads_leave(); + + response_unref(response); + + if (status == CURLE_OK || status == FAIL_RESPONSE_UNSUCCESSFUL) + dispatch_async(priv->client, session_get(), on_session_get, data); +} + +static void on_session_get(JsonObject * response, int status, + gpointer data) +{ + TrgMainWindow *win = TRG_MAIN_WINDOW(data); + TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data); + trg_client *client = priv->client; + JsonObject *newSession; + + gdk_threads_enter(); + + if (trg_dialog_error_handler(win, response, status) == TRUE) { + response_unref(response); + gdk_threads_leave(); + return; + } + + newSession = get_arguments(response); + + if (client->session != NULL) { + json_object_unref(client->session); + } else { + trg_status_bar_connect(priv->statusBar, newSession); + trg_main_window_conn_changed(win, TRUE); + + dispatch_async(client, torrent_get(), on_torrent_get_first, data); + } + + client->session = newSession; + + gdk_threads_leave(); + json_object_ref(newSession); + response_unref(response); +} + +static void +on_torrent_get_first(JsonObject * response, int status, gpointer data) +{ + on_torrent_get_multipurpose(response, TRUE, status, data); +} + +static void on_torrent_get(JsonObject * response, int status, + gpointer data) +{ + on_torrent_get_multipurpose(response, FALSE, status, data); +} + +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, data); + + return FALSE; +} + +static void +trg_main_window_update_notebook_displays(TrgMainWindow * win, + JsonObject * t, + GtkTreeIter * iter, + gboolean first) +{ + TrgMainWindowPrivate *priv; + trg_client *client; + + priv = TRG_MAIN_WINDOW_GET_PRIVATE(win); + client = priv->client; + + trg_general_panel_update(priv->genDetails, t, iter); + + if (first == TRUE) + trg_trackers_model_update(priv->trackersModel, t); + + trg_files_model_update(priv->filesModel, client->updateSerial, + t, first); + trg_peers_model_update(priv->peersModel, client->updateSerial, + t, first); +} + +static +void open_about_cb(GtkWidget * w, GtkWindow * parent) +{ + GtkWidget *aboutDialog = trg_about_window_new(parent); + + gtk_dialog_run(GTK_DIALOG(aboutDialog)); + gtk_widget_destroy(aboutDialog); +} + +static gboolean +trg_torrent_tree_view_visible_func(GtkTreeModel * model, + GtkTreeIter * iter, gpointer data) +{ + guint flags; + gchar *name = NULL; + gboolean visible = TRUE; + + TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data); + + guint32 criteria = trg_state_selector_get_flag(priv->stateSelector); + + gtk_tree_model_get(model, iter, + TORRENT_COLUMN_FLAGS, &flags, + TORRENT_COLUMN_NAME, &name, -1); + + if (criteria != 0 && !(flags & criteria)) { + visible = FALSE; + } else if (name != NULL) { + const gchar *filterText = + gtk_entry_get_text(GTK_ENTRY(priv->filterEntry)); + if (strlen(filterText) > 0 && strstr(name, filterText) == NULL) + visible = FALSE; + } + + g_free(name); + + return visible; +} + +static +TrgTorrentTreeView *trg_main_window_torrent_tree_view_new(TrgMainWindow * + win, + GtkTreeModel * + model, + TrgStateSelector + * selector) +{ + TrgTorrentTreeView *torrentTreeView = trg_torrent_tree_view_new(model); + + GtkTreeSelection *selection = + gtk_tree_view_get_selection(GTK_TREE_VIEW(torrentTreeView)); + + g_signal_connect(G_OBJECT(selection), "changed", + G_CALLBACK(torrent_selection_changed), win); + + return torrentTreeView; +} + +static gboolean +trg_dialog_error_handler(TrgMainWindow * win, JsonObject * response, + int status) +{ + TrgMainWindowPrivate *priv; + + priv = TRG_MAIN_WINDOW_GET_PRIVATE(win); + + if (status != CURLE_OK) { + GtkWidget *dialog; + const gchar *msg; + + msg = make_error_message(response, status); + trg_status_bar_push_connection_msg(priv->statusBar, msg); + dialog = gtk_message_dialog_new(GTK_WINDOW(win), + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, msg); + gtk_window_set_title(GTK_WINDOW(dialog), "Error"); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + g_free((gpointer) msg); + return TRUE; + } else { + return FALSE; + } +} + +static gboolean torrent_selection_changed(GtkWidget * w, gpointer data) +{ + TrgMainWindow *win; + TrgMainWindowPrivate *priv; + gboolean isSelected; + + priv = TRG_MAIN_WINDOW_GET_PRIVATE(data); + win = TRG_MAIN_WINDOW(data); + + isSelected = update_selected_torrent_notebook(win, TRUE); + + trg_toolbar_torrent_actions_sensitive(priv->toolBar, isSelected); + trg_menu_bar_torrent_actions_sensitive(priv->menuBar, isSelected); + + return TRUE; +} + +void +on_generic_interactive_action(JsonObject * response, int status, + gpointer data) +{ + gdk_threads_enter(); + trg_dialog_error_handler(TRG_MAIN_WINDOW(data), response, status); + gdk_threads_leave(); + + response_unref(response); +} + +static +void trg_main_window_torrent_scrub(TrgMainWindow * win) +{ + TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(win); + + gtk_list_store_clear(GTK_LIST_STORE(priv->filesModel)); + gtk_list_store_clear(GTK_LIST_STORE(priv->trackersModel)); + gtk_list_store_clear(GTK_LIST_STORE(priv->peersModel)); + trg_general_panel_clear(priv->genDetails); +} + +static void +on_torrent_get_multipurpose(JsonObject * response, gboolean first, + int status, gpointer data) +{ + TrgTorrentModelClassUpdateStats stats; + TrgMainWindowPrivate *priv; + trg_client *client; + + priv = TRG_MAIN_WINDOW_GET_PRIVATE(data); + client = priv->client; + + g_mutex_lock(client->updateMutex); + gdk_threads_enter(); + + /* Disconnected between request and response callback */ + if (client->session == NULL) { + gdk_threads_leave(); + g_mutex_unlock(client->updateMutex); + response_unref(response); + return; + } + + if (status != CURLE_OK) { + client->failCount++; + if (client->failCount >= 3) { + trg_main_window_conn_changed(TRG_MAIN_WINDOW(data), FALSE); + trg_dialog_error_handler(TRG_MAIN_WINDOW(data), + response, status); + } else { + const gchar *msg; + gchar *statusBarMsg; + + msg = make_error_message(response, status); + statusBarMsg = + g_strdup_printf("Request %d/%d failed: %s", + client->failCount, 3, msg); + trg_status_bar_push_connection_msg(priv->statusBar, + statusBarMsg); + g_free((gpointer) msg); + g_free(statusBarMsg); + g_timeout_add_seconds(3, trg_update_torrents_timerfunc, data); + } + gdk_threads_leave(); + g_mutex_unlock(client->updateMutex); + response_unref(response); + return; + } + + client->failCount = 0; + stats.downRateTotal = 0; + stats.upRateTotal = 0; + stats.seeding = 0; + stats.down = 0; + stats.paused = 0; + + trg_torrent_model_update(priv->torrentModel, priv->client, + response, &stats, first); + + trg_status_bar_update(priv->statusBar, &stats); + + update_selected_torrent_notebook(TRG_MAIN_WINDOW(data), first); + + g_timeout_add_seconds(3, trg_update_torrents_timerfunc, data); + + client->updateSerial++; + + gdk_threads_leave(); + g_mutex_unlock(client->updateMutex); + response_unref(response); +} + +static void entry_filter_changed_cb(GtkWidget * w, gpointer data) +{ + TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data); + gboolean clearSensitive = gtk_entry_get_text_length(GTK_ENTRY(w)) > 0; + + gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER + (priv->filteredTorrentModel)); + +#if GTK_CHECK_VERSION( 2,16,0 ) + g_object_set(priv->filterEntryClearButton, + "secondary-icon-sensitive", clearSensitive, NULL); +#else + gtk_widget_set_sensitive(priv->filterEntryClearButton, clearSensitive); +#endif +} + +static void torrent_state_selection_changed(TrgStateSelector * selector, + guint flag, gpointer data) +{ + gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(data)); +} + +static +void trg_main_window_conn_changed(TrgMainWindow * win, gboolean connected) +{ + TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(win); + trg_client *tc = priv->client; + + trg_toolbar_connected_change(priv->toolBar, connected); + trg_menu_bar_connected_change(priv->menuBar, connected); + + gtk_widget_set_sensitive(GTK_WIDGET(priv->torrentTreeView), connected); + gtk_widget_set_sensitive(GTK_WIDGET(priv->peersTreeView), connected); + gtk_widget_set_sensitive(GTK_WIDGET(priv->filesTreeView), connected); + gtk_widget_set_sensitive(GTK_WIDGET(priv->trackersTreeView), + connected); + gtk_widget_set_sensitive(GTK_WIDGET(priv->genDetails), connected); + + if (connected == FALSE) { + json_object_unref(tc->session); + tc->session = NULL; + + gtk_list_store_clear(GTK_LIST_STORE(priv->torrentModel)); + trg_main_window_torrent_scrub(win); + } +} + +static void +trg_main_window_get_property(GObject * object, guint property_id, + GValue * value, GParamSpec * pspec) +{ + TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(object); + switch (property_id) { + case PROP_CLIENT: + g_value_set_pointer(value, priv->client); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void +trg_main_window_set_property(GObject * object, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(object); + switch (property_id) { + case PROP_CLIENT: + priv->client = g_value_get_pointer(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void quit_cb(GtkWidget * w, gpointer data) +{ + gtk_widget_destroy(GTK_WIDGET(data)); +} + +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_add_url, *b_quit; + TrgMenuBar *menuBar; + + menuBar = trg_menu_bar_new(win); + g_object_get(menuBar, + "connect-button", &b_connect, + "disconnect-button", &b_disconnect, + "add-button", &b_add, + "add-url-button", &b_add_url, + "resume-button", &b_resume, + "pause-button", &b_pause, + "delete-button", &b_delete, + "remove-button", &b_remove, + "verify-button", &b_verify, + "props-button", &b_props, + "remote-prefs-button", &b_remote_prefs, + "local-prefs-button", &b_local_prefs, + "view-notebook-button", &b_view_notebook, + "view-states-button", &b_view_states, + "about-button", &b_about, "quit-button", &b_quit, NULL); + + g_signal_connect(b_connect, "activate", G_CALLBACK(connect_cb), win); + g_signal_connect(b_disconnect, "activate", + G_CALLBACK(disconnect_cb), 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_pause, "activate", G_CALLBACK(pause_cb), win); + g_signal_connect(b_verify, "activate", G_CALLBACK(verify_cb), win); + g_signal_connect(b_delete, "activate", G_CALLBACK(delete_cb), win); + g_signal_connect(b_remove, "activate", G_CALLBACK(remove_cb), win); + g_signal_connect(b_about, "activate", G_CALLBACK(open_about_cb), win); + g_signal_connect(b_local_prefs, "activate", + G_CALLBACK(open_local_prefs_cb), win); + g_signal_connect(b_remote_prefs, "activate", + G_CALLBACK(open_remote_prefs_cb), win); + g_signal_connect(b_view_notebook, "toggled", + G_CALLBACK(view_notebook_toggled_cb), win); + g_signal_connect(b_view_states, "toggled", + G_CALLBACK(view_states_toggled_cb), win); + g_signal_connect(b_props, "activate", G_CALLBACK(open_props_cb), win); + g_signal_connect(b_quit, "activate", G_CALLBACK(quit_cb), win); + + return menuBar; +} + +static void status_icon_activated(GtkStatusIcon * icon, gpointer data) +{ + gtk_window_present(GTK_WINDOW(data)); +} + +static void clear_filter_entry_cb(GtkWidget * w, gpointer data) +{ + gtk_entry_set_text(GTK_ENTRY(w), ""); +} + +static gboolean torrent_tv_key_press_event(GtkWidget * w, + GdkEventKey * key, + gpointer data) +{ + if (key->keyval == GDK_KEY_Delete) { + if (key->state & GDK_SHIFT_MASK) + delete_cb(w, data); + else + remove_cb(w, data); + } + return FALSE; +} + +static +GtkWidget *trg_imagemenuitem_new(GtkMenuShell * shell, char *text, + char *stock_id, gboolean sensitive, + GCallback cb, gpointer cbdata) +{ + GtkWidget *item = gtk_image_menu_item_new_with_label(stock_id); + + 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); + gtk_menu_item_set_label(GTK_MENU_ITEM(item), text); + g_signal_connect(item, "activate", cb, cbdata); + gtk_widget_set_sensitive(item, sensitive); + gtk_menu_shell_append(shell, item); + + return item; +} + +static void +trg_torrent_tv_view_menu(GtkWidget * treeview, GdkEventButton * event, + gpointer data) +{ + GtkWidget *menu; + + menu = gtk_menu_new(); + + trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Properties", + GTK_STOCK_PROPERTIES, TRUE, + G_CALLBACK(open_props_cb), data); + trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Resume", + GTK_STOCK_MEDIA_PLAY, TRUE, + G_CALLBACK(resume_cb), data); + trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Pause", + GTK_STOCK_MEDIA_PAUSE, TRUE, + G_CALLBACK(pause_cb), data); + trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Verify", + GTK_STOCK_REFRESH, TRUE, G_CALLBACK(verify_cb), + data); + trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Remove", GTK_STOCK_REMOVE, + TRUE, G_CALLBACK(remove_cb), data); + trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Remove & Delete", + GTK_STOCK_DELETE, TRUE, G_CALLBACK(delete_cb), + data); + + 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 void +trg_status_icon_view_menu(GtkStatusIcon * icon, GdkEventButton * event, + gpointer data) +{ + TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data); + gboolean connected = priv->client->session != NULL; + GtkWidget *menu; + + menu = gtk_menu_new(); + + trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Connect", + GTK_STOCK_CONNECT, !connected, + G_CALLBACK(connect_cb), data); + trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Disconnect", + GTK_STOCK_DISCONNECT, connected, + G_CALLBACK(disconnect_cb), data); + trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Add", GTK_STOCK_ADD, + connected, G_CALLBACK(add_cb), data); + trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Add from URL", + GTK_STOCK_ADD, connected, G_CALLBACK(add_url_cb), + data); + trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Quit", GTK_STOCK_QUIT, + TRUE, G_CALLBACK(quit_cb), data); + + 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 trg_status_icon_popup_menu_cb(GtkStatusIcon * icon, + gpointer userdata) +{ + trg_status_icon_view_menu(icon, NULL, userdata); + return TRUE; +} + +static gboolean status_icon_button_press_event(GtkStatusIcon * icon, + GdkEventButton * event, + gpointer data) +{ + if (event->type == GDK_BUTTON_PRESS && event->button == 3) { + trg_status_icon_view_menu(icon, event, data); + return TRUE; + } else { + return FALSE; + } +} + +static gboolean +torrent_tv_button_pressed_cb(GtkWidget * treeview, GdkEventButton * event, + gpointer userdata) +{ + GtkTreeSelection *selection; + GtkTreePath *path; + + if (event->type == GDK_BUTTON_PRESS && event->button == 3) { + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + + if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), + (gint) event->x, + (gint) event->y, &path, + NULL, NULL, NULL)) { + if (!gtk_tree_selection_path_is_selected(selection, path)) { + gtk_tree_selection_unselect_all(selection); + gtk_tree_selection_select_path(selection, path); + } + + gtk_tree_path_free(path); + + trg_torrent_tv_view_menu(treeview, event, userdata); + return TRUE; + } + } + + return FALSE; +} + +static gboolean torrent_tv_popup_menu_cb(GtkWidget * treeview, + gpointer userdata) +{ + trg_torrent_tv_view_menu(treeview, NULL, userdata); + return TRUE; +} + +static GObject *trg_main_window_constructor(GType type, + guint + n_construct_properties, + GObjectConstructParam + * construct_params) +{ + TrgMainWindow *self; + TrgMainWindowPrivate *priv; + GtkWidget *w; + GtkWidget *outerVbox; + GError *iconError = NULL; + GtkWidget *toolbarHbox; + gint width, height; + + self = TRG_MAIN_WINDOW(G_OBJECT_CLASS + (trg_main_window_parent_class)->constructor + (type, n_construct_properties, + construct_params)); + priv = TRG_MAIN_WINDOW_GET_PRIVATE(self); + + gtk_window_set_title(GTK_WINDOW(self), PACKAGE_NAME); + gtk_container_set_border_width(GTK_CONTAINER(self), 5); + gtk_window_set_default_size(GTK_WINDOW(self), 1000, 600); + g_signal_connect(G_OBJECT(self), "delete-event", + G_CALLBACK(delete_event), NULL); + g_signal_connect(G_OBJECT(self), "destroy", G_CALLBACK(destroy_window), + NULL); + + gtk_window_set_icon_from_file + (GTK_WINDOW(self), WINDOW_ICON_FILE, &iconError); + + if (iconError != NULL) { + g_printf("setting icon failed: %s\n", iconError->message); + g_error_free(iconError); + } + + priv->torrentModel = trg_torrent_model_new(); + g_signal_connect(priv->torrentModel, "torrent-completed", + G_CALLBACK(on_torrent_completed), self); + + priv->filteredTorrentModel = + gtk_tree_model_filter_new(GTK_TREE_MODEL(priv->torrentModel), + NULL); + gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER + (priv->filteredTorrentModel), + trg_torrent_tree_view_visible_func, + self, NULL); + + priv->sortedTorrentModel = + gtk_tree_model_sort_new_with_model(priv->filteredTorrentModel); + + priv->torrentTreeView = + trg_main_window_torrent_tree_view_new(self, + priv->sortedTorrentModel, + priv->stateSelector); + g_signal_connect(priv->torrentTreeView, "key-press-event", + G_CALLBACK(torrent_tv_key_press_event), self); + g_signal_connect(priv->torrentTreeView, "popup-menu", + G_CALLBACK(torrent_tv_popup_menu_cb), self); + g_signal_connect(priv->torrentTreeView, "button-press-event", + G_CALLBACK(torrent_tv_button_pressed_cb), self); + + + outerVbox = gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(self), outerVbox); + + priv->menuBar = trg_main_window_menu_bar_new(self); + gtk_box_pack_start(GTK_BOX(outerVbox), GTK_WIDGET(priv->menuBar), + FALSE, FALSE, 0); + + toolbarHbox = gtk_hbox_new(FALSE, 0); + priv->toolBar = trg_main_window_toolbar_new(self); + gtk_box_pack_start(GTK_BOX(toolbarHbox), GTK_WIDGET(priv->toolBar), + TRUE, TRUE, 0); + +#if GTK_CHECK_VERSION( 2,16,0 ) + w = gtk_entry_new(); + gtk_entry_set_icon_from_stock(GTK_ENTRY(w), + GTK_ENTRY_ICON_SECONDARY, + GTK_STOCK_CLEAR); + g_signal_connect(w, "icon-release", + G_CALLBACK(clear_filter_entry_cb), w); + gtk_box_pack_start(GTK_BOX(toolbarHbox), w, FALSE, FALSE, 0); + g_object_set(w, "secondary-icon-sensitive", FALSE, NULL); + priv->filterEntryClearButton = priv->filterEntry = w; +#else + priv->filterEntry = gtk_entry_new(); + gtk_box_pack_start(GTK_BOX(toolbarHbox), priv->filterEntry, FALSE, + FALSE, 0); + w = gtk_button_new(); + gtk_button_set_relief(GTK_BUTTON(w), GTK_RELIEF_NONE); + gtk_button_set_image(GTK_BUTTON(w), + gtk_image_new_from_stock(GTK_STOCK_CLEAR, + GTK_ICON_SIZE_MENU)); + gtk_box_pack_start(GTK_BOX(toolbarHbox), w, FALSE, FALSE, 0); + g_signal_connect_swapped(w, "clicked", + G_CALLBACK(clear_filter_entry_cb), + priv->filterEntry); + priv->filterEntryClearButton = w; +#endif + + g_signal_connect(G_OBJECT(priv->filterEntry), "changed", + G_CALLBACK(entry_filter_changed_cb), self); + + gtk_box_pack_start(GTK_BOX(outerVbox), GTK_WIDGET(toolbarHbox), + FALSE, FALSE, 0); + + priv->vpaned = gtk_vpaned_new(); + priv->hpaned = gtk_hpaned_new(); + gtk_box_pack_start(GTK_BOX(outerVbox), priv->vpaned, TRUE, TRUE, 0); + gtk_paned_pack1(GTK_PANED(priv->vpaned), priv->hpaned, TRUE, TRUE); + + priv->stateSelector = trg_state_selector_new(); + gtk_paned_pack1(GTK_PANED(priv->hpaned), + GTK_WIDGET(priv->stateSelector), FALSE, FALSE); + + gtk_paned_pack2(GTK_PANED(priv->hpaned), + my_scrolledwin_new(GTK_WIDGET + (priv->torrentTreeView)), TRUE, + TRUE); + + g_signal_connect(G_OBJECT(priv->stateSelector), + "torrent-state-changed", + G_CALLBACK(torrent_state_selection_changed), + priv->filteredTorrentModel); + + priv->notebook = trg_main_window_notebook_new(self); + gtk_paned_pack2(GTK_PANED(priv->vpaned), priv->notebook, FALSE, FALSE); + + priv->statusIcon = gtk_status_icon_new_from_file(WINDOW_ICON_FILE); + gtk_status_icon_set_screen(priv->statusIcon, + gtk_window_get_screen(GTK_WINDOW(self))); + g_signal_connect(priv->statusIcon, "activate", + G_CALLBACK(status_icon_activated), self); + g_signal_connect(priv->statusIcon, "button-press-event", + G_CALLBACK(status_icon_button_press_event), self); + g_signal_connect(priv->statusIcon, "popup-menu", + G_CALLBACK(trg_status_icon_popup_menu_cb), self); + + priv->statusBar = trg_status_bar_new(priv->statusIcon); + gtk_box_pack_start(GTK_BOX(outerVbox), GTK_WIDGET(priv->statusBar), + FALSE, FALSE, 2); + + width = + gconf_client_get_int(priv->client->gconf, + TRG_GCONF_KEY_WINDOW_WIDTH, NULL); + height = + gconf_client_get_int(priv->client->gconf, + TRG_GCONF_KEY_WINDOW_HEIGHT, NULL); + + if (width > 0 && height > 0) + gtk_window_set_default_size(GTK_WINDOW(self), width, height); + + return G_OBJECT(self); +} + +static void trg_main_window_class_init(TrgMainWindowClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + g_type_class_add_private(klass, sizeof(TrgMainWindowPrivate)); + + object_class->constructor = trg_main_window_constructor; + object_class->get_property = trg_main_window_get_property; + object_class->set_property = trg_main_window_set_property; + + g_object_class_install_property(object_class, + PROP_CLIENT, + g_param_spec_pointer + ("trg-client", "TClient", + "Client", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_NICK | + G_PARAM_STATIC_BLURB)); +} + +void auto_connect_if_required(TrgMainWindow * win, trg_client * tc) +{ + gchar *host = + gconf_client_get_string(tc->gconf, TRG_GCONF_KEY_HOSTNAME, + NULL); + if (host != NULL) { + gint len = strlen(host); + g_free(host); + if (len > 0 + && gconf_client_get_bool(tc->gconf, + TRG_GCONF_KEY_AUTO_CONNECT, + NULL) == TRUE) + connect_cb(NULL, win); + } +} + +TrgMainWindow *trg_main_window_new(trg_client * tc) +{ + return g_object_new(TRG_TYPE_MAIN_WINDOW, "trg-client", tc, NULL); +} |