From c427fa73c4f7853de992eda9de1c818c2ee525aa Mon Sep 17 00:00:00 2001 From: Alan Fitton Date: Wed, 18 Jan 2012 23:30:02 +0000 Subject: full and default gtk3 support, almost. the graph is disabled as the drawing api has changed quite a bit. yet another implementation to pass arguments, which made a cleanup of main.c necessary. --- configure.ac | 33 ++-- src/Makefile.am | 3 +- src/main.c | 388 +++++++++++--------------------------- src/trg-cell-renderer-file-icon.c | 2 +- src/trg-gtk-app.c | 236 +++++++++++++++++++++++ src/trg-gtk-app.h | 55 ++++++ src/trg-main-window.c | 70 +++---- src/trg-main-window.h | 3 +- src/trg-menu-bar.c | 14 ++ src/trg-preferences-dialog.c | 7 +- src/trg-remote-prefs-dialog.c | 6 +- src/trg-sortable-filtered-model.h | 1 - src/trg-stats-dialog.c | 5 +- src/trg-status-bar.c | 15 +- src/trg-status-bar.h | 3 +- src/trg-toolbar.c | 2 + src/trg-torrent-graph.c | 8 +- src/trg-torrent-graph.h | 10 +- src/trg-torrent-props-dialog.c | 6 +- src/trg-tree-view.c | 33 ++-- src/util.c | 56 ++++++ src/util.h | 4 + src/win32-mailslot.c | 192 +++++++++++++++++++ src/win32-mailslot.h | 29 +++ 24 files changed, 810 insertions(+), 371 deletions(-) create mode 100644 src/trg-gtk-app.c create mode 100644 src/trg-gtk-app.h create mode 100644 src/win32-mailslot.c create mode 100644 src/win32-mailslot.h diff --git a/configure.ac b/configure.ac index 217d32b..32c1108 100644 --- a/configure.ac +++ b/configure.ac @@ -55,12 +55,6 @@ AC_SUBST(GETTEXT_PACKAGE) AC_DEFINE(GETTEXT_PACKAGE, "AC_PACKAGE_NAME", [foo]) AC_DEFINE_DIR([LOCALEDIR], [datarootdir/locale], [gettext catalogs]) -PKG_CHECK_MODULES([jsonglib], [json-glib-1.0 >= 0.8]) -PKG_CHECK_MODULES([gthread], [gthread-2.0]) -PKG_CHECK_MODULES([gtk], [gtk+-2.0 >= 2.16]) -PKG_CHECK_MODULES([libcurl], [libcurl]) -PKG_CHECK_MODULES([gio], [gio-2.0 >= 2.22]) - AC_ARG_WITH([libgeoip], AC_HELP_STRING([--without-libgeoip], [disable GeoIP support])) have_libgeoip=no if test x$with_libgeoip != xno; then @@ -75,24 +69,21 @@ AM_CONDITIONAL([HAVE_GEOIP], [test "x$have_geoip" = "xyes"]) AC_ARG_WITH([libunique], AC_HELP_STRING([--without-libunique], [disable libunique])) have_libunique=no -if test x$with_libunique != xno; then - PKG_CHECK_MODULES([unique], [unique-1.0], AC_DEFINE(HAVE_LIBUNIQUE, 1, [Define if libunique is available]), AC_MSG_WARN([libunique is required for opening torrents on non-win32])) -fi - AC_ARG_WITH([libnotify], AC_HELP_STRING([--without-libnotify], [disable libnotify])) have_libnotify=no +AC_ARG_WITH([libproxy], AC_HELP_STRING([--without-libproxy], [disable libproxy])) +have_libproxy=no +AC_ARG_WITH([libappindicator], AC_HELP_STRING([--without-libappindicator], [disable libappindicator])) +have_libappindicator=no + if test x$with_libnotify != xno; then PKG_CHECK_MODULES([notify], [libnotify], AC_DEFINE(HAVE_LIBNOTIFY, 1, [Define if libnotify is available]), AC_MSG_WARN([libnotify is required for popup desktop notifications])) fi -AC_ARG_WITH([libproxy], AC_HELP_STRING([--without-libproxy], [disable libproxy])) -have_libproxy=no if test x$with_libproxy != xno; then PKG_CHECK_MODULES([libproxy], [libproxy-1.0], AC_DEFINE(HAVE_LIBPROXY, 1, [Define if libproxy is available]), AC_MSG_WARN([libproxy is required for HTTP proxy support])) fi -AC_ARG_WITH([libappindicator], AC_HELP_STRING([--without-libappindicator], [disable libappindicator])) -have_libappindicator=no if test x$with_libappindicator != xno; then PKG_CHECK_MODULES([libappindicator], [appindicator-0.1], AC_DEFINE(HAVE_LIBAPPINDICATOR, 1, [Define if libappindicator is available]), AC_MSG_WARN([Ubuntu Unity users should consider building with libappindicator])) fi @@ -103,4 +94,18 @@ if test x$enable_debug = xyes; then AC_DEFINE([DEBUG], [], [enable debugging]) fi +PKG_CHECK_MODULES([jsonglib], [json-glib-1.0 >= 0.8]) +PKG_CHECK_MODULES([gthread], [gthread-2.0]) + +PKG_CHECK_MODULES([gtk], [gtk+-3.0 >= 3.00], [], [ +AC_MSG_WARN([gtk+-3.0 not found, trying gtk+-2.0]) +PKG_CHECK_MODULES([gtk], [gtk+-2.0 >= 2.16]) +if test x$with_libunique != xno; then + PKG_CHECK_MODULES([unique], [unique-1.0], AC_DEFINE(HAVE_LIBUNIQUE, 1, [Define if libunique is available]), AC_MSG_WARN([libunique is required for opening torrents on gtk+-2.0. not needed for gtk+-3.0 or win32. ])) +fi +]) + +PKG_CHECK_MODULES([libcurl], [libcurl]) +PKG_CHECK_MODULES([gio], [gio-2.0 >= 2.22]) + AC_OUTPUT([Makefile po/Makefile.in src/Makefile]) diff --git a/src/Makefile.am b/src/Makefile.am index 7cf5167..d564181 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -101,6 +101,7 @@ transmission_remote_gtk_SOURCES = main.c \ trg-prefs.c \ trg-destination-combo.c \ remote-exec.c \ + trg-gtk-app.c \ $(NULL) transmission_remote_gtk_LDFLAGS = -lm $(jsonglib_LIBS) $(gtk_LIBS) $(gthread_LIBS) $(GEOIP_LIBS) $(gio_LIBS) $(unique_LIBS) $(notify_LIBS) $(libproxy_LIBS) $(libcurl_LIBS) $(libappindicator_LIBS) @@ -112,7 +113,7 @@ if WIN32 windres $^ -o $@ AM_CFLAGS = -mms-bitfields -mwin32 -mwindows -transmission_remote_gtk_SOURCES += win32.rc +transmission_remote_gtk_SOURCES += win32.rc win32-mailslot.c INCLUDES += -O2 else %.1: %.pod diff --git a/src/main.c b/src/main.c index e2c61f7..980e719 100644 --- a/src/main.c +++ b/src/main.c @@ -30,30 +30,32 @@ #include #include #include -#ifdef HAVE_LIBUNIQUE + +#if !GTK_CHECK_VERSION( 3, 0, 0 ) && HAVE_LIBUNIQUE #include +#elif GTK_CHECK_VERSION( 3, 0, 0 ) +#include "trg-gtk-app.h" #elif WIN32 -#include +#include "win32-mailslot.h" #endif #include "trg-main-window.h" #include "trg-client.h" #include "util.h" -/* The file with the main() function. It converts arguments into a NULL terminated - * array, with relative paths converted to absolute paths. +/* Handle arguments and start the main window. Unfortunately, there's three + * different ways to achieve a unique instance and pass arguments around. :( * - * Much of the code here is to optionally handle passing torrent files/URLs to - * a first instance. UNIX can use libunique (should alternatively use - * GApplication for GNOME3). Windows uses the mailslot feature in the - * native Win32 API, so there is no dependency on dbus etc. + * 1) libunique - GTK2 (non-win32). deprecated in GTK3 for GtkApplication. + * 2) GtkApplication - replaces libunique, GTK3 only, and non-win32. + * 3) win32 API mailslots. */ -#define TRG_LIBUNIQUE_DOMAIN "uk.org.eth0.trg" -#define TRG_MAILSLOT_NAME "\\\\.\\mailslot\\TransmissionRemoteGTK" //Name given to the Mailslot -#define MAILSLOT_BUFFER_SIZE 1024*32 +/* + * libunique. + */ -#ifdef HAVE_LIBUNIQUE +#if !GTK_CHECK_VERSION( 3, 0, 0 ) && HAVE_LIBUNIQUE enum { COMMAND_0, @@ -94,320 +96,152 @@ message_received_cb(UniqueApp * app G_GNUC_UNUSED, return res; } -#elif WIN32 - -struct trg_mailslot_recv_args { - TrgMainWindow *win; - gchar **uris; - gboolean present; -}; - -/* to be queued into the glib main loop with g_idle_add() */ - -static gboolean mailslot_recv_args(gpointer data) +static gint trg_libunique_init(TrgClient * client, int argc, + gchar * argv[], gchar ** args) { - struct trg_mailslot_recv_args *args = - (struct trg_mailslot_recv_args *) data; + UniqueApp *app = unique_app_new_with_commands("uk.org.eth0.trg", NULL, + "add", COMMAND_ADD, + NULL); + TrgMainWindow *window; - if (args->present) { - gtk_window_deiconify(GTK_WINDOW(args->win)); - gtk_window_present(GTK_WINDOW(args->win)); - } + if (unique_app_is_running(app)) { + UniqueCommand command; + UniqueResponse response; + UniqueMessageData *message; - if (args->uris) - trg_add_from_filename(args->win, args->uris); + if (args) { + command = COMMAND_ADD; + message = unique_message_data_new(); + unique_message_data_set_uris(message, args); + g_strfreev(args); + } else { + command = UNIQUE_ACTIVATE; + message = NULL; + } - g_free(args); + response = unique_app_send_message(app, command, message); + unique_message_data_free(message); - return FALSE; -} + if (response != UNIQUE_RESPONSE_OK) + return EXIT_FAILURE; + } else { + window = + trg_main_window_new(client, should_be_minimised(argc, argv)); + g_signal_connect(app, "message-received", + G_CALLBACK(message_received_cb), window); -static gpointer mailslot_recv_thread(gpointer data) -{ - TrgMainWindow *win = TRG_MAIN_WINDOW(data); - JsonParser *parser; - char szBuffer[MAILSLOT_BUFFER_SIZE]; - HANDLE hMailslot; - DWORD cbBytes; - BOOL bResult; - - hMailslot = CreateMailslot(TRG_MAILSLOT_NAME, // mailslot name - MAILSLOT_BUFFER_SIZE, // input buffer size - MAILSLOT_WAIT_FOREVER, // no timeout - NULL); // default security attribute - - if (INVALID_HANDLE_VALUE == hMailslot) { - g_error("\nError occurred while creating the mailslot: %d", - GetLastError()); - return NULL; //Error + auto_connect_if_required(window, args); + gtk_main(); } - while (1) { - bResult = ReadFile(hMailslot, // handle to mailslot - szBuffer, // buffer to receive data - sizeof(szBuffer), // size of buffer - &cbBytes, // number of bytes read - NULL); // not overlapped I/O + g_object_unref(app); - if ((!bResult) || (0 == cbBytes)) { - g_error("Mailslot error from client: %d", GetLastError()); - break; - } + return EXIT_SUCCESS; +} - parser = json_parser_new(); +#elif GTK_CHECK_VERSION( 3, 0, 0 ) - if (json_parser_load_from_data(parser, szBuffer, cbBytes, NULL)) { - JsonNode *node = json_parser_get_root(parser); - JsonObject *obj = json_node_get_object(node); - struct trg_mailslot_recv_args *args = - g_new0(struct trg_mailslot_recv_args, 1); +/* GtkApplication - the replacement for libunique. + * This is implemented in trg-gtk-app.c + */ - args->present = json_object_has_member(obj, "present") - && json_object_get_boolean_member(obj, "present"); - args->win = win; +static gint trg_gtkapp_init(TrgClient * client, int argc, char *argv[]) +{ + TrgGtkApp *gtk_app = trg_gtk_app_new(client); - if (json_object_has_member(obj, "args")) { - JsonArray *array = - json_object_get_array_member(obj, "args"); - GList *arrayList = json_array_get_elements(array); + gint exitCode = g_application_run(G_APPLICATION(gtk_app), argc, argv); - if (arrayList) { - guint arrayLength = g_list_length(arrayList); - guint i = 0; - GList *li; + g_object_unref(gtk_app); - args->uris = g_new0(gchar *, arrayLength + 1); + return exitCode; +} - for (li = arrayList; li; li = g_list_next(li)) { - const gchar *liStr = - json_node_get_string((JsonNode *) li->data); - args->uris[i++] = g_strdup(liStr); - } +#else - g_list_free(arrayList); - } - } +static gint trg_simple_init(TrgClient * client, int argc, char *argv[], gchar **args) +{ + TrgMainWindow *window = + trg_main_window_new(client, should_be_minimised(argc, argv)); + auto_connect_if_required(window, args); + gtk_main(); - json_node_free(node); + return EXIT_SUCCESS; +} - g_idle_add(mailslot_recv_args, args); - } +#endif - g_object_unref(parser); - } - CloseHandle(hMailslot); - return NULL; //Success -} +/* Win32 mailslots. I've implemented this in win32-mailslot.c */ -static int mailslot_send_message(HANDLE h, gchar ** args) +#ifdef WIN32 +static gint trg_win32_init(TrgClient * client, int argc, char *argv[], + gchar ** args) { - DWORD cbBytes; - JsonNode *node = json_node_new(JSON_NODE_OBJECT); - JsonObject *obj = json_object_new(); - JsonArray *array = json_array_new(); - JsonGenerator *generator; - gchar *msg; - int i; - - if (args) { - for (i = 0; args[i]; i++) - json_array_add_string_element(array, args[i]); + gchar *moddir = + g_win32_get_package_installation_directory_of_module(NULL); + gchar *localedir = + g_build_path(G_DIR_SEPARATOR_S, moddir, "share", "locale", + NULL); - json_object_set_array_member(obj, "args", array); + bindtextdomain(GETTEXT_PACKAGE, localedir); + g_free(moddir); + g_free(localedir); - g_strfreev(args); - } else { - json_object_set_boolean_member(obj, "present", TRUE); + if (!mailslot_send_message(args)) { + TrgMainWindow *window = + trg_main_window_new(client, should_be_minimised(argc, argv)); + auto_connect_if_required(window, args); + mailslot_start_background_listener(window); + gtk_main(); } - json_node_take_object(node, obj); - - generator = json_generator_new(); - json_generator_set_root(generator, node); - msg = json_generator_to_data(generator, NULL); - - json_node_free(node); - g_object_unref(generator); - - WriteFile(h, // handle to mailslot - msg, // buffer to write from - strlen(msg) + 1, // number of bytes to write, include the NULL - &cbBytes, // number of bytes written - NULL); - - CloseHandle(h); - g_free(msg); - - return 0; -} - -#endif - -static gboolean is_minimised_arg(gchar * arg) -{ - return !g_strcmp0(arg, "-m") - || !g_strcmp0(arg, "--minimized") - || !g_strcmp0(arg, "/m"); + return EXIT_SUCCESS; } - -static gboolean should_be_minimised(int argc, char *argv[]) +#else +static void trg_non_win32_init() { - int i; - for (i = 1; i < argc; i++) - if (is_minimised_arg(argv[i])) - return TRUE; - - return FALSE; + bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); } +#endif -static gchar **convert_args(int argc, char *argv[]) +static void trg_cleanup() { - gchar *cwd = g_get_current_dir(); - gchar **files = NULL; - - if (argc > 1) { - GSList *list = NULL; - int i; - - for (i = 1; i < argc; i++) { - if (is_minimised_arg(argv[i])) { - continue; - } else if (!is_url(argv[i]) && !is_magnet(argv[i]) - && g_file_test(argv[i], G_FILE_TEST_IS_REGULAR) - && !g_path_is_absolute(argv[i])) { - list = g_slist_append(list, - g_build_path(G_DIR_SEPARATOR_S, cwd, - argv[i], NULL)); - } else { - list = g_slist_append(list, g_strdup(argv[i])); - } - } - - if (list) { - GSList *li; - files = g_new0(gchar *, g_slist_length(list) + 1); - i = 0; - for (li = list; li; li = g_slist_next(li)) { - files[i++] = li->data; - } - g_slist_free(list); - } - } - - g_free(cwd); - - return files; + curl_global_cleanup(); } int main(int argc, char *argv[]) { - int returnValue = EXIT_SUCCESS; - TrgMainWindow *window; - TrgClient *client; +#if WIN32 || !GTK_CHECK_VERSION( 3, 0, 0 ) gchar **args = convert_args(argc, argv); - gboolean withUnique; -#ifdef HAVE_LIBUNIQUE - UniqueApp *app = NULL; -#endif -#ifdef WIN32 - gchar *localedir, *moddir; - HANDLE hMailSlot; -#endif -#ifdef TRG_MEMPROFILE - GMemVTable gmvt = { malloc, realloc, free, calloc, malloc, realloc }; - g_mem_set_vtable(&gmvt); - g_mem_set_vtable(glib_mem_profiler_table); - g_mem_profile(); #endif + gint exitCode = EXIT_SUCCESS; + TrgClient *client; g_type_init(); g_thread_init(NULL); gtk_init(&argc, &argv); + curl_global_init(CURL_GLOBAL_ALL); + client = trg_client_new(); + g_set_application_name(PACKAGE_NAME); -#ifdef WIN32 - moddir = g_win32_get_package_installation_directory_of_module(NULL); - localedir = g_build_path(G_DIR_SEPARATOR_S, moddir, "share", "locale", - NULL); - g_free(moddir); - bindtextdomain(GETTEXT_PACKAGE, localedir); - g_free(localedir); -#else - bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); -#endif bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); textdomain(GETTEXT_PACKAGE); - withUnique = (g_getenv("TRG_NOUNIQUE") == NULL); - -#ifdef HAVE_LIBUNIQUE - if (withUnique) - app = unique_app_new_with_commands(TRG_LIBUNIQUE_DOMAIN, NULL, - "add", COMMAND_ADD, NULL); - - if (withUnique && unique_app_is_running(app)) { - UniqueCommand command; - UniqueResponse response; - UniqueMessageData *message; - - if (args) { - command = COMMAND_ADD; - message = unique_message_data_new(); - unique_message_data_set_uris(message, args); - g_strfreev(args); - } else { - command = UNIQUE_ACTIVATE; - message = NULL; - } - - response = unique_app_send_message(app, command, message); - unique_message_data_free(message); - - if (response != UNIQUE_RESPONSE_OK) - returnValue = EXIT_FAILURE; - } else { -#elif WIN32 - hMailSlot = CreateFile(TRG_MAILSLOT_NAME, // mailslot name - GENERIC_WRITE, // mailslot write only - FILE_SHARE_READ, // required for mailslots - NULL, // default security attributes - OPEN_EXISTING, // opens existing mailslot - FILE_ATTRIBUTE_NORMAL, // normal attributes - NULL); // no template file - - if (INVALID_HANDLE_VALUE != hMailSlot) { - returnValue = mailslot_send_message(hMailSlot, args); - } else { +#ifdef WIN32 + exitCode = trg_win32_init(client, argc, argv, args); +#else + trg_non_win32_init(); +#if !GTK_CHECK_VERSION( 3, 0, 0 ) && HAVE_LIBUNIQUE + exitCode = trg_libunique_init(client, argc, argv, args); +#elif GTK_CHECK_VERSION( 3, 0, 0 ) + exitCode = trg_gtkapp_init(client, argc, argv); +#else + exitCode = trg_simple_init(client, argc, argv, args); #endif - client = trg_client_new(); - - curl_global_init(CURL_GLOBAL_ALL); - - window = - trg_main_window_new(client, should_be_minimised(argc, argv)); - -#ifdef HAVE_LIBUNIQUE - if (withUnique) { - g_signal_connect(app, "message-received", - G_CALLBACK(message_received_cb), window); - } -#elif WIN32 - g_thread_create(mailslot_recv_thread, window, FALSE, NULL); #endif - auto_connect_if_required(window, args); - gtk_main(); - - curl_global_cleanup(); -#ifdef HAVE_LIBUNIQUE - } - - if (withUnique) - g_object_unref(app); -#elif WIN32 - } -#endif + trg_cleanup(); - return returnValue; + return exitCode; } diff --git a/src/trg-cell-renderer-file-icon.c b/src/trg-cell-renderer-file-icon.c index d61729c..fddd0a5 100644 --- a/src/trg-cell-renderer-file-icon.c +++ b/src/trg-cell-renderer-file-icon.c @@ -91,7 +91,7 @@ static void trg_cell_renderer_file_icon_refresh(TrgCellRendererFileIcon * g_object_set(fi, "stock-id", GTK_STOCK_FILE, NULL); } #else - g_object_set(fi, "stock-id", GTK_STOCK_FILE, NULL); + g_object_set(fi, "stock-id", GTK_STOCK_FILE, NULL); #endif } } diff --git a/src/trg-gtk-app.c b/src/trg-gtk-app.c new file mode 100644 index 0000000..5a45a80 --- /dev/null +++ b/src/trg-gtk-app.c @@ -0,0 +1,236 @@ +/* + * transmission-remote-gtk - A GTK RPC client to Transmission + * Copyright (C) 2011 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 + +#if GTK_CHECK_VERSION( 3, 0, 0 ) + +#include "trg-main-window.h" +#include "trg-gtk-app.h" +#include "util.h" + +enum { + PROP_0, PROP_CLIENT, PROP_START_ARGS, PROP_MINIMISE_ON_START +}; + +G_DEFINE_TYPE(TrgGtkApp, trg_gtk_app, GTK_TYPE_APPLICATION) +#define GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_GTK_APP, TrgGtkAppPrivate)) +typedef struct _TrgGtkAppPrivate TrgGtkAppPrivate; + +struct _TrgGtkAppPrivate { + TrgClient *client; + gboolean min_start; +}; + +static void +trg_gtk_app_get_property(GObject * object, guint property_id, + GValue * value, GParamSpec * pspec) +{ + TrgGtkAppPrivate *priv = GET_PRIVATE(object); + switch (property_id) { + case PROP_CLIENT: + g_value_set_pointer(value, priv->client); + break; + case PROP_MINIMISE_ON_START: + g_value_set_boolean(value, priv->min_start); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + break; + } +} + +static void +trg_gtk_app_set_property(GObject * object, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + TrgGtkAppPrivate *priv = GET_PRIVATE(object); + switch (property_id) { + case PROP_CLIENT: + priv->client = g_value_get_pointer(value); + break; + case PROP_MINIMISE_ON_START: + priv->min_start = g_value_get_boolean(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + break; + } +} + +static void trg_gtk_app_dispose(GObject * object) +{ + G_OBJECT_CLASS(trg_gtk_app_parent_class)->dispose(object); +} + +static void trg_gtk_app_finalize(GObject * object) +{ + G_OBJECT_CLASS(trg_gtk_app_parent_class)->finalize(object); +} + +static void trg_gtk_app_startup(GtkApplication * app, gpointer data) +{ + TrgGtkAppPrivate *priv = GET_PRIVATE(app); + TrgMainWindow *window = + trg_main_window_new(priv->client, priv->min_start); + gtk_window_set_application(GTK_WINDOW(window), app); + +} + +static int +trg_gtk_app_command_line(GApplication * application, + GApplicationCommandLine * cmdline) +{ + GList *windows = + gtk_application_get_windows(GTK_APPLICATION(application)); + gchar **argv = g_application_command_line_get_arguments(cmdline, NULL); + + if (argv && argv[0]) + auto_connect_if_required(TRG_MAIN_WINDOW(windows->data), argv); + + /*if (argv && argv[0]) + trg_main_window_set_start_args(TRG_MAIN_WINDOW(windows->data), + argv);*/ + + + return 0; +} + +static void shift_args(gchar ** argv, int i) +{ + gint j; + g_free(argv[i]); + for (j = i; argv[j]; j++) + argv[j] = argv[j + 1]; +} + +static gboolean +test_local_cmdline(GApplication * application, + gchar *** arguments, gint * exit_status) +{ + TrgGtkAppPrivate *priv = GET_PRIVATE(application); + gchar **argv; + gchar *cwd = g_get_current_dir(); + gchar *tmp; + gint i; + + argv = *arguments; + shift_args(argv, 0); + + i = 1; + while (argv[i]) { + if (is_minimised_arg(argv[i])) { + shift_args(argv, i); + priv->min_start = TRUE; + } else if (!is_url(argv[i]) && !is_magnet(argv[i]) + && g_file_test(argv[i], G_FILE_TEST_IS_REGULAR) + && !g_path_is_absolute(argv[i])) { + tmp = g_build_path(G_DIR_SEPARATOR_S, cwd, argv[i], NULL); + g_free(argv[i]); + argv[i] = tmp; + } + i++; + } + + *exit_status = 0; + + g_free(cwd); + + return FALSE; +} + +static void trg_gtk_app_class_init(TrgGtkAppClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + GApplicationClass *app_class = G_APPLICATION_CLASS(klass); + + g_type_class_add_private(klass, sizeof(TrgGtkAppPrivate)); + + object_class->get_property = trg_gtk_app_get_property; + object_class->set_property = trg_gtk_app_set_property; + object_class->dispose = trg_gtk_app_dispose; + object_class->finalize = trg_gtk_app_finalize; + app_class->local_command_line = test_local_cmdline; + + 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)); + + g_object_class_install_property(object_class, + PROP_MINIMISE_ON_START, + g_param_spec_boolean("min-on-start", + "Min On Start", + "Min On Start", + FALSE, + G_PARAM_READWRITE + | + G_PARAM_CONSTRUCT_ONLY + | + G_PARAM_STATIC_NAME + | + G_PARAM_STATIC_NICK + | + G_PARAM_STATIC_BLURB)); + + g_object_class_install_property(object_class, + PROP_START_ARGS, + g_param_spec_pointer("start-args", + "start args", + "start args", + G_PARAM_READWRITE + | + G_PARAM_CONSTRUCT_ONLY + | + G_PARAM_STATIC_NAME + | + G_PARAM_STATIC_NICK + | + G_PARAM_STATIC_BLURB)); +} + +static void trg_gtk_app_init(TrgGtkApp * self) +{ + g_application_set_inactivity_timeout(G_APPLICATION(self), 10000); + g_signal_connect(self, "command-line", + G_CALLBACK(trg_gtk_app_command_line), NULL); + g_signal_connect(self, "startup", G_CALLBACK(trg_gtk_app_startup), + NULL); +} + +TrgGtkApp *trg_gtk_app_new(TrgClient * client) +{ + return g_object_new(TRG_TYPE_GTK_APP, + "application-id", "uk.org.eth0.trg", + "flags", G_APPLICATION_HANDLES_COMMAND_LINE, + "trg-client", client, NULL); +} + +#endif diff --git a/src/trg-gtk-app.h b/src/trg-gtk-app.h new file mode 100644 index 0000000..0374321 --- /dev/null +++ b/src/trg-gtk-app.h @@ -0,0 +1,55 @@ +/* + * transmission-remote-gtk - A GTK RPC client to Transmission + * Copyright (C) 2011 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 + +#ifndef TRG_GTKAPP_ +#define TRG_GTKAPP_ +#if GTK_CHECK_VERSION( 3, 0, 0 ) + +#include + +#include "trg-client.h" + +G_BEGIN_DECLS +#define TRG_TYPE_GTK_APP trg_gtk_app_get_type() +#define TRG_GTK_APP(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_GTK_APP, TrgGtkApp)) +#define TRG_GTK_APP_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_GTK_APP, TrgGtkAppClass)) +#define TRG_IS_GTK_APP(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_GTK_APP)) +#define TRG_IS_GTK_APP_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_GTK_APP)) +#define TRG_GTK_APP_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_GTK_APP, TrgGtkAppClass)) + typedef struct { + GtkApplication parent; +} TrgGtkApp; + +typedef struct { + GtkApplicationClass parent_class; +} TrgGtkAppClass; + +GType trg_gtk_app_get_type(void); + +TrgGtkApp *trg_gtk_app_new(TrgClient * client); + +#endif +#endif diff --git a/src/trg-main-window.c b/src/trg-main-window.c index 23c83e6..27b09a4 100644 --- a/src/trg-main-window.c +++ b/src/trg-main-window.c @@ -66,9 +66,7 @@ #include "trg-trackers-tree-view.h" #include "trg-trackers-model.h" #include "trg-state-selector.h" -#ifndef TRG_NO_GRAPH #include "trg-torrent-graph.h" -#endif #include "trg-torrent-move-dialog.h" #include "trg-torrent-props-dialog.h" #include "trg-torrent-add-url-dialog.h" @@ -200,6 +198,7 @@ static gboolean window_state_event(GtkWidget * widget, GdkEventWindowState * event, gpointer trayIcon); + 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)) @@ -240,7 +239,7 @@ struct _TrgMainWindowPrivate { TrgPeersModel *peersModel; TrgPeersTreeView *peersTreeView; -#ifndef TRG_NO_GRAPH +#if TRG_WITH_GRAPH TrgTorrentGraph *graph; #endif gint graphNotebookIndex; @@ -430,8 +429,7 @@ static void add_url_cb(GtkWidget * w G_GNUC_UNUSED, gpointer data) TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data); TrgTorrentAddUrlDialog *dlg = trg_torrent_add_url_dialog_new(win, - priv-> - client); + priv->client); gtk_widget_show_all(GTK_WIDGET(dlg)); } @@ -463,7 +461,7 @@ static void pause_all_cb(GtkWidget * w G_GNUC_UNUSED, gpointer data) on_generic_interactive_action, data); } -gboolean trg_add_from_filename(TrgMainWindow * win, gchar ** uris) +gint trg_add_from_filename(TrgMainWindow * win, gchar ** uris) { TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(win); TrgClient *client = priv->client; @@ -472,7 +470,8 @@ gboolean trg_add_from_filename(TrgMainWindow * win, gchar ** uris) int i; for (i = 0; uris[i]; i++) - filesList = g_slist_append(filesList, uris[i]); + if (uris[i]) + filesList = g_slist_append(filesList, uris[i]); if (trg_prefs_get_bool(prefs, TRG_PREFS_KEY_ADD_OPTIONS_DIALOG, TRG_PREFS_GLOBAL)) { @@ -497,9 +496,15 @@ gboolean trg_add_from_filename(TrgMainWindow * win, gchar ** uris) priv->args = NULL; - return TRUE; + return EXIT_SUCCESS; } +/*void trg_main_window_set_start_args(TrgMainWindow * window, gchar ** args) +{ + TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(window); + priv->args = args; +}*/ + static void resume_all_cb(GtkWidget * w G_GNUC_UNUSED, gpointer data) { TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data); @@ -874,11 +879,13 @@ static void view_stats_toggled_cb(GtkWidget * w, gpointer data) { TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data); - TrgStatsDialog *dlg = - trg_stats_dialog_get_instance(TRG_MAIN_WINDOW(data), - priv->client); + if (trg_client_is_connected(priv->client)) { + TrgStatsDialog *dlg = + trg_stats_dialog_get_instance(TRG_MAIN_WINDOW(data), + priv->client); - gtk_widget_show_all(GTK_WIDGET(dlg)); + gtk_widget_show_all(GTK_WIDGET(dlg)); + } } static void view_states_toggled_cb(GtkCheckMenuItem * w, gpointer data) @@ -895,7 +902,7 @@ static void view_notebook_toggled_cb(GtkCheckMenuItem * w, gpointer data) gtk_check_menu_item_get_active(w)); } -#ifndef TRG_NO_GRAPH +#if TRG_WITH_GRAPH static void trg_main_window_toggle_graph_cb(GtkCheckMenuItem * w, gpointer win) { @@ -963,7 +970,7 @@ static GtkWidget *trg_main_window_notebook_new(TrgMainWindow * win) (priv->peersTreeView)), gtk_label_new(_("Peers"))); -#ifndef TRG_NO_GRAPH +#if TRG_WITH_GRAPH if (trg_prefs_get_bool (prefs, TRG_PREFS_KEY_SHOW_GRAPH, TRG_PREFS_GLOBAL)) trg_main_window_add_graph(win, FALSE); @@ -1021,7 +1028,9 @@ static gboolean on_session_get(gpointer data) return FALSE; } - if ((version = session_get_version(newSession)) < TRANSMISSION_MIN_SUPPORTED) { + if ((version = + session_get_version(newSession)) < + TRANSMISSION_MIN_SUPPORTED) { gchar *msg = g_strdup_printf(_ ("This application supports Transmission %g and later, you have %g."), @@ -1088,8 +1097,8 @@ static void connchange_whatever_statusicon(TrgMainWindow * win, TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(win); TrgPrefs *prefs = trg_client_get_prefs(priv->client); const gchar *display = connected ? - trg_prefs_get_string(prefs, TRG_PREFS_KEY_PROFILE_NAME, - TRG_PREFS_CONNECTION) : _("Disconnected"); + trg_prefs_get_string(prefs, TRG_PREFS_KEY_PROFILE_NAME, + TRG_PREFS_CONNECTION) : _("Disconnected"); #ifdef HAVE_LIBAPPINDICATOR if (priv->appIndicator) { @@ -1126,16 +1135,16 @@ static void update_whatever_statusicon(TrgMainWindow * win, trg_strlsize(buf, stats->downRateTotal); downloadingLabel = g_strdup_printf(_("%d Downloading @ %s"), - stats->down, buf); + stats->down, buf); gtk_menu_item_set_label(GTK_MENU_ITEM(priv->iconDownloadingItem), downloadingLabel); g_free(downloadingLabel); trg_strlsize(buf, stats->upRateTotal); seedingLabel = g_strdup_printf(_("%d Seeding @ %s"), - stats->seeding, buf); + stats->seeding, buf); gtk_menu_item_set_label(GTK_MENU_ITEM(priv->iconSeedingItem), - seedingLabel); + seedingLabel); g_free(seedingLabel); } } @@ -1209,7 +1218,7 @@ static gboolean on_torrent_get(gpointer data, int mode) trg_status_bar_update(priv->statusBar, stats, client); update_whatever_statusicon(win, stats); -#ifndef TRG_NO_GRAPH +#if TRG_WITH_GRAPH if (priv->graphNotebookIndex >= 0) trg_torrent_graph_set_speed(priv->graph, stats); #endif @@ -1318,8 +1327,7 @@ static gboolean trg_torrent_tree_view_visible_func(GtkTreeModel * model, matchesTracker = (!json || !torrent_has_tracker(json, trg_state_selector_get_url_host_regex - (priv-> - stateSelector), + (priv->stateSelector), text)); g_free(text); if (matchesTracker) @@ -1531,7 +1539,7 @@ void trg_main_window_conn_changed(TrgMainWindow * win, gboolean connected) trg_main_window_torrent_scrub(win); trg_state_selector_disconnect(priv->stateSelector); -#ifndef TRG_NO_GRAPH +#if TRG_WITH_GRAPH if (priv->graphNotebookIndex >= 0) trg_torrent_graph_set_nothing(priv->graph); #endif @@ -1597,7 +1605,7 @@ static TrgMenuBar *trg_main_window_menu_bar_new(TrgMainWindow * win) *b_add_url, *b_quit, *b_move, *b_reannounce, *b_pause_all, *b_resume_all, *b_dir_filters, *b_tracker_filters, *b_up_queue, *b_down_queue, *b_top_queue, *b_bottom_queue, -#ifndef TRG_NO_GRAPH +#if TRG_WITH_GRAPH *b_show_graph, #endif *b_start_now; @@ -1624,7 +1632,7 @@ static TrgMenuBar *trg_main_window_menu_bar_new(TrgMainWindow * win) &b_view_stats, "about-button", &b_about, "quit-button", &b_quit, "dir-filters", &b_dir_filters, "tracker-filters", &b_tracker_filters, -#ifndef TRG_NO_GRAPH +#if TRG_WITH_GRAPH "show-graph", &b_show_graph, #endif "up-queue", &b_up_queue, "down-queue", &b_down_queue, @@ -1671,7 +1679,7 @@ static TrgMenuBar *trg_main_window_menu_bar_new(TrgMainWindow * win) G_CALLBACK(view_states_toggled_cb), win); g_signal_connect(b_view_stats, "activate", G_CALLBACK(view_stats_toggled_cb), win); -#ifndef TRG_NO_GRAPH +#if TRG_WITH_GRAPH g_signal_connect(b_show_graph, "toggled", G_CALLBACK(trg_main_window_toggle_graph_cb), win); #endif @@ -2300,7 +2308,7 @@ void trg_main_window_remove_status_icon(TrgMainWindow * win) #endif } -#ifndef TRG_NO_GRAPH +#if TRG_WITH_GRAPH void trg_main_window_add_graph(TrgMainWindow * win, gboolean show) { TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(win); @@ -2513,8 +2521,7 @@ static GObject *trg_main_window_constructor(GType type, self, NULL); priv->torrentTreeView = trg_main_window_torrent_tree_view_new(self, - priv-> - filteredTorrentModel); + priv->filteredTorrentModel); g_signal_connect(priv->torrentTreeView, "popup-menu", G_CALLBACK(torrent_tv_popup_menu_cb), self); g_signal_connect(priv->torrentTreeView, "button-press-event", @@ -2562,8 +2569,7 @@ static GObject *trg_main_window_constructor(GType type, FALSE, FALSE); gtk_paned_pack2(GTK_PANED(priv->hpaned), my_scrolledwin_new(GTK_WIDGET - (priv-> - torrentTreeView)), + (priv->torrentTreeView)), TRUE, TRUE); g_signal_connect(G_OBJECT(priv->stateSelector), diff --git a/src/trg-main-window.h b/src/trg-main-window.h index 9a60ea3..e9f3d6d 100644 --- a/src/trg-main-window.h +++ b/src/trg-main-window.h @@ -57,7 +57,7 @@ typedef struct { #define TORRENT_ADD_NOTIFY_TMOUT 3000 GType trg_main_window_get_type(void); -gboolean trg_add_from_filename(TrgMainWindow * win, gchar ** uris); +gint trg_add_from_filename(TrgMainWindow * win, gchar ** uris); gboolean on_session_set(gpointer data); gboolean on_generic_interactive_action(gpointer data); void auto_connect_if_required(TrgMainWindow * win, gchar ** args); @@ -73,6 +73,7 @@ void trg_main_window_notebook_set_visible(TrgMainWindow * win, gboolean visible); void connect_cb(GtkWidget * w, gpointer data); void trg_main_window_reload_dir_aliases(TrgMainWindow * win); +void trg_main_window_set_start_args(TrgMainWindow * window, gchar ** args); #if !GTK_CHECK_VERSION(2, 21, 1) #define gdk_drag_context_get_actions(context) context->actions diff --git a/src/trg-menu-bar.c b/src/trg-menu-bar.c index b5e049f..23ec34a 100644 --- a/src/trg-menu-bar.c +++ b/src/trg-menu-bar.c @@ -20,7 +20,11 @@ #include #include #include +#if GTK_CHECK_VERSION( 3, 0, 0 ) +#include +#endif +#include "trg-torrent-graph.h" #include "trg-main-window.h" #include "trg-menu-bar.h" @@ -52,7 +56,9 @@ enum { PROP_ACCEL_GROUP, PROP_DIR_FILTERS, PROP_TRACKER_FILTERS, +#if TRG_WITH_GRAPH PROP_VIEW_SHOW_GRAPH, +#endif PROP_MOVE_DOWN_QUEUE, PROP_MOVE_UP_QUEUE, PROP_MOVE_BOTTOM_QUEUE, @@ -91,7 +97,9 @@ struct _TrgMenuBarPrivate { GtkWidget *mb_quit; GtkWidget *mb_directory_filters; GtkWidget *mb_tracker_filters; +#if TRG_WITH_GRAPH GtkWidget *mb_view_graph; +#endif GtkWidget *mb_down_queue; GtkWidget *mb_up_queue; GtkWidget *mb_bottom_queue; @@ -241,9 +249,11 @@ trg_menu_bar_get_property(GObject * object, guint property_id, case PROP_ABOUT_BUTTON: g_value_set_object(value, priv->mb_about); break; +#if TRG_WITH_GRAPH case PROP_VIEW_SHOW_GRAPH: g_value_set_object(value, priv->mb_view_graph); break; +#endif case PROP_VIEW_STATES_BUTTON: g_value_set_object(value, priv->mb_view_states); break; @@ -412,11 +422,13 @@ static GtkWidget *trg_menu_bar_view_menu_new(TrgMenuBar * mb) gtk_menu_shell_append(GTK_MENU_SHELL(viewMenu), priv->mb_view_notebook); +#if TRG_WITH_GRAPH priv->mb_view_graph = trg_menu_bar_view_item_new(priv->prefs, TRG_PREFS_KEY_SHOW_GRAPH, _("Graph"), priv->mb_view_notebook); trg_menu_bar_accel_add(mb, priv->mb_view_graph, GDK_F6, 0); gtk_menu_shell_append(GTK_MENU_SHELL(viewMenu), priv->mb_view_graph); +#endif priv->mb_view_stats = gtk_menu_item_new_with_mnemonic(_("_Statistics")); @@ -774,8 +786,10 @@ static void trg_menu_bar_class_init(TrgMenuBarClass * klass) "dir-filters", "Dir Filters"); trg_menu_bar_install_widget_prop(object_class, PROP_TRACKER_FILTERS, "tracker-filters", "Tracker Filters"); +#if TRG_WITH_GRAPH trg_menu_bar_install_widget_prop(object_class, PROP_VIEW_SHOW_GRAPH, "show-graph", "Show Graph"); +#endif trg_menu_bar_install_widget_prop(object_class, PROP_MOVE_DOWN_QUEUE, "down-queue", "Down Queue"); trg_menu_bar_install_widget_prop(object_class, PROP_MOVE_UP_QUEUE, diff --git a/src/trg-preferences-dialog.c b/src/trg-preferences-dialog.c index 4c774ec..d4f5680 100644 --- a/src/trg-preferences-dialog.c +++ b/src/trg-preferences-dialog.c @@ -330,7 +330,7 @@ static void toggle_filter_trackers(GtkToggleButton * w, gpointer win) gtk_toggle_button_get_active(w)); } -#ifndef TRG_NO_GRAPH +#if TRG_WITH_GRAPH static void toggle_graph(GtkToggleButton * w, gpointer win) { if (gtk_toggle_button_get_active(w)) @@ -385,8 +385,7 @@ static void trgp_double_special_dependent(GtkWidget * widget, (priv->fullUpdateCheck) && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON - (priv-> - fullUpdateCheck))); + (priv->fullUpdateCheck))); } static GtkWidget *trg_prefs_generalPage(TrgPreferencesDialog * dlg) @@ -696,7 +695,7 @@ static GtkWidget *trg_prefs_viewPage(TrgPreferencesDialog * dlg) G_CALLBACK(notebook_toggled_cb), priv->win); hig_workarea_add_wide_control(t, &row, w); -#ifndef TRG_NO_GRAPH +#if TRG_WITH_GRAPH w = trgp_check_new(dlg, _("Show graph"), TRG_PREFS_KEY_SHOW_GRAPH, TRG_PREFS_GLOBAL, GTK_TOGGLE_BUTTON(w)); g_signal_connect(G_OBJECT(w), "toggled", G_CALLBACK(toggle_graph), diff --git a/src/trg-remote-prefs-dialog.c b/src/trg-remote-prefs-dialog.c index 8f866ea..8c921e4 100644 --- a/src/trg-remote-prefs-dialog.c +++ b/src/trg-remote-prefs-dialog.c @@ -159,12 +159,10 @@ static void trg_remote_prefs_double_special_dependent(GtkWidget * widget, gtk_widget_set_sensitive(GTK_WIDGET(data), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON - (priv-> - alt_time_check)) + (priv->alt_time_check)) || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON - (priv-> - alt_check))); + (priv->alt_check))); } static void trg_rprefs_time_widget_savefunc(GtkWidget * w, diff --git a/src/trg-sortable-filtered-model.h b/src/trg-sortable-filtered-model.h index 9440b32..55e525c 100644 --- a/src/trg-sortable-filtered-model.h +++ b/src/trg-sortable-filtered-model.h @@ -49,6 +49,5 @@ GtkTreeModel *trg_sortable_filtered_model_new(GtkTreeSortable * child_model, GtkTreePath * root); - G_END_DECLS #endif /* _TRG_SORTABLE_FILTERED_MODEL */ diff --git a/src/trg-stats-dialog.c b/src/trg-stats-dialog.c index 20d8ea2..7348d5e 100644 --- a/src/trg-stats-dialog.c +++ b/src/trg-stats-dialog.c @@ -232,8 +232,9 @@ static gboolean trg_update_stats_timerfunc(gpointer data) if (TRG_IS_STATS_DIALOG(data)) { priv = TRG_STATS_DIALOG_GET_PRIVATE(data); - dispatch_async(priv->client, session_stats(), on_stats_reply, - data); + if (trg_client_is_connected(priv->client)) + dispatch_async(priv->client, session_stats(), on_stats_reply, + data); } return FALSE; diff --git a/src/trg-status-bar.c b/src/trg-status-bar.c index 0f0b293..94ebab9 100644 --- a/src/trg-status-bar.c +++ b/src/trg-status-bar.c @@ -128,23 +128,26 @@ void trg_status_bar_push_connection_msg(TrgStatusBar * sb, gtk_label_set_text(GTK_LABEL(priv->info_lbl), msg); } -void trg_status_bar_connect(TrgStatusBar * sb, JsonObject * session, TrgClient *client) +void trg_status_bar_connect(TrgStatusBar * sb, JsonObject * session, + TrgClient * client) { TrgStatusBarPrivate *priv = TRG_STATUS_BAR_GET_PRIVATE(sb); TrgPrefs *prefs = trg_client_get_prefs(client); gdouble version = session_get_version(session); gchar *statusMsg = - g_strdup_printf(_ - ("Connected: %s (Transmission %g)"), - trg_prefs_get_string(prefs, TRG_PREFS_KEY_PROFILE_NAME, TRG_PREFS_CONNECTION), + g_strdup_printf(_("Connected: %s (Transmission %g)"), + trg_prefs_get_string(prefs, + TRG_PREFS_KEY_PROFILE_NAME, + TRG_PREFS_CONNECTION), version); g_message("%s", statusMsg); trg_status_bar_push_connection_msg(sb, statusMsg); g_free(statusMsg); - gtk_label_set_text(GTK_LABEL(priv->speed_lbl), _("Updating torrents...")); + gtk_label_set_text(GTK_LABEL(priv->speed_lbl), + _("Updating torrents...")); } void trg_status_bar_session_update(TrgStatusBar * sb, JsonObject * session) @@ -244,7 +247,9 @@ TrgStatusBar *trg_status_bar_new(TrgMainWindow * win, TrgClient * client) { TrgStatusBar *sb = g_object_new(TRG_TYPE_STATUS_BAR, NULL); TrgStatusBarPrivate *priv = TRG_STATUS_BAR_GET_PRIVATE(sb); + priv->client = client; priv->win = win; + return sb; } diff --git a/src/trg-status-bar.h b/src/trg-status-bar.h index e0ba8d5..c1ee82a 100644 --- a/src/trg-status-bar.h +++ b/src/trg-status-bar.h @@ -55,7 +55,8 @@ G_END_DECLS TrgClient * client); void trg_status_bar_session_update(TrgStatusBar * sb, JsonObject * session); -void trg_status_bar_connect(TrgStatusBar * sb, JsonObject * session, TrgClient *client); +void trg_status_bar_connect(TrgStatusBar * sb, JsonObject * session, + TrgClient * client); void trg_status_bar_push_connection_msg(TrgStatusBar * sb, const gchar * msg); void trg_status_bar_reset(TrgStatusBar * sb); diff --git a/src/trg-toolbar.c b/src/trg-toolbar.c index 2fc7149..d829dcc 100644 --- a/src/trg-toolbar.c +++ b/src/trg-toolbar.c @@ -261,7 +261,9 @@ static GObject *trg_toolbar_constructor(GType type, trg_toolbar_item_new(TRG_TOOLBAR(obj), _("Remote Preferences"), &position, GTK_STOCK_NETWORK, FALSE); +#if !GTK_CHECK_VERSION( 3, 0, 0 ) gtk_toolbar_set_tooltips(GTK_TOOLBAR(obj), TRUE); +#endif g_signal_connect(G_OBJECT(priv->prefs), "pref-profile-changed", G_CALLBACK(trg_toolbar_refresh_menu), obj); diff --git a/src/trg-torrent-graph.c b/src/trg-torrent-graph.c index 94c4ecc..0b47bab 100644 --- a/src/trg-torrent-graph.c +++ b/src/trg-torrent-graph.c @@ -27,13 +27,15 @@ * on this widget but with some improvements I didn't do. */ +#include "trg-torrent-graph.h" + +#if TRG_WITH_GRAPH + #include #include #include #include #include - -#include "trg-torrent-graph.h" #include "util.h" /* damn you freebsd */ @@ -633,3 +635,5 @@ unsigned trg_torrent_graph_get_num_bars(TrgTorrentGraph * g) return n; } + +#endif diff --git a/src/trg-torrent-graph.h b/src/trg-torrent-graph.h index c71f3b6..914a184 100644 --- a/src/trg-torrent-graph.h +++ b/src/trg-torrent-graph.h @@ -3,9 +3,13 @@ #ifndef _TRG_TORRENT_GRAPH #define _TRG_TORRENT_GRAPH -#include #include +#define TRG_WITH_GRAPH !GTK_CHECK_VERSION( 3, 0, 0 ) + +#if TRG_WITH_GRAPH + +#include #include "trg-torrent-model.h" G_BEGIN_DECLS @@ -49,4 +53,6 @@ void trg_torrent_graph_set_speed(TrgTorrentGraph * g, void trg_torrent_graph_set_nothing(TrgTorrentGraph * g); G_END_DECLS -#endif /* _TRG_TORRENT_GRAPH */ + +#endif +#endif /* _TRG_TORRENT_GRAPH */ diff --git a/src/trg-torrent-props-dialog.c b/src/trg-torrent-props-dialog.c index d18b577..a0ae22a 100644 --- a/src/trg-torrent-props-dialog.c +++ b/src/trg-torrent-props-dialog.c @@ -136,10 +136,12 @@ trg_torrent_props_response_cb(GtkDialog * dlg, gint res_id, json_object_set_int_member(args, FIELD_SEED_RATIO_MODE, gtk_combo_box_get_active(GTK_COMBO_BOX - (priv->seedRatioMode))); + (priv-> + seedRatioMode))); json_object_set_int_member(args, FIELD_BANDWIDTH_PRIORITY, gtk_combo_box_get_active(GTK_COMBO_BOX - (priv->bandwidthPriorityCombo)) + (priv-> + bandwidthPriorityCombo)) - 1); trg_json_widgets_save(priv->widgets, args); diff --git a/src/trg-tree-view.c b/src/trg-tree-view.c index 4265c7d..54c5cf5 100644 --- a/src/trg-tree-view.c +++ b/src/trg-tree-view.c @@ -313,8 +313,7 @@ static void trg_tree_view_add_column_after(TrgTreeView * tv, gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(desc->header, renderer, "text", - desc-> - model_column, + desc->model_column, NULL); break; @@ -323,8 +322,7 @@ static void trg_tree_view_add_column_after(TrgTreeView * tv, column = gtk_tree_view_column_new_with_attributes(desc->header, renderer, "speed-value", - desc-> - model_column, + desc->model_column, NULL); break; case TRG_COLTYPE_EPOCH: @@ -332,8 +330,7 @@ static void trg_tree_view_add_column_after(TrgTreeView * tv, column = gtk_tree_view_column_new_with_attributes(desc->header, renderer, "epoch-value", - desc-> - model_column, + desc->model_column, NULL); break; case TRG_COLTYPE_ETA: @@ -341,8 +338,7 @@ static void trg_tree_view_add_column_after(TrgTreeView * tv, column = gtk_tree_view_column_new_with_attributes(desc->header, renderer, "eta-value", - desc-> - model_column, + desc->model_column, NULL); break; case TRG_COLTYPE_SIZE: @@ -350,8 +346,7 @@ static void trg_tree_view_add_column_after(TrgTreeView * tv, column = gtk_tree_view_column_new_with_attributes(desc->header, renderer, "size-value", - desc-> - model_column, + desc->model_column, NULL); break; case TRG_COLTYPE_PROG: @@ -359,8 +354,7 @@ static void trg_tree_view_add_column_after(TrgTreeView * tv, column = gtk_tree_view_column_new_with_attributes(desc->header, renderer, "value", - desc-> - model_column, + desc->model_column, NULL); break; case TRG_COLTYPE_RATIO: @@ -368,8 +362,7 @@ static void trg_tree_view_add_column_after(TrgTreeView * tv, column = gtk_tree_view_column_new_with_attributes(desc->header, renderer, "ratio-value", - desc-> - model_column, + desc->model_column, NULL); break; case TRG_COLTYPE_WANTED: @@ -394,8 +387,7 @@ static void trg_tree_view_add_column_after(TrgTreeView * tv, column = gtk_tree_view_column_new_with_attributes(desc->header, renderer, "priority-value", - desc-> - model_column, + desc->model_column, NULL); break; case TRG_COLTYPE_NUMGTZERO: @@ -403,8 +395,7 @@ static void trg_tree_view_add_column_after(TrgTreeView * tv, column = gtk_tree_view_column_new_with_attributes(desc->header, renderer, "value", - desc-> - model_column, + desc->model_column, NULL); break; case TRG_COLTYPE_NUMGTEQZERO: @@ -412,8 +403,7 @@ static void trg_tree_view_add_column_after(TrgTreeView * tv, column = gtk_tree_view_column_new_with_attributes(desc->header, renderer, "value", - desc-> - model_column, + desc->model_column, NULL); break; } @@ -567,8 +557,7 @@ void trg_tree_view_setup_columns(TrgTreeView * tv) json_node_get_string ((JsonNode *) - cli-> - data)); + cli->data)); if (desc) { gint64 width = json_node_get_int((JsonNode *) wli->data); trg_tree_view_add_column(tv, desc, width); diff --git a/src/util.c b/src/util.c index 7594c46..944be9a 100644 --- a/src/util.c +++ b/src/util.c @@ -533,3 +533,59 @@ evutil_vsnprintf(char *buf, size_t buflen, const char *format, va_list ap) return r; #endif } + +gboolean is_minimised_arg(gchar * arg) +{ + return !g_strcmp0(arg, "-m") + || !g_strcmp0(arg, "--minimized") + || !g_strcmp0(arg, "/m"); +} + +gboolean should_be_minimised(int argc, char *argv[]) +{ + int i; + for (i = 1; i < argc; i++) + if (is_minimised_arg(argv[i])) + return TRUE; + + return FALSE; +} + +gchar **convert_args(int argc, char *argv[]) +{ + gchar *cwd = g_get_current_dir(); + gchar **files = NULL; + + if (argc > 1) { + GSList *list = NULL; + int i; + + for (i = 1; i < argc; i++) { + if (is_minimised_arg(argv[i])) { + continue; + } else if (!is_url(argv[i]) && !is_magnet(argv[i]) + && g_file_test(argv[i], G_FILE_TEST_IS_REGULAR) + && !g_path_is_absolute(argv[i])) { + list = g_slist_append(list, + g_build_path(G_DIR_SEPARATOR_S, cwd, + argv[i], NULL)); + } else { + list = g_slist_append(list, g_strdup(argv[i])); + } + } + + if (list) { + GSList *li; + files = g_new0(gchar *, g_slist_length(list) + 1); + i = 0; + for (li = list; li; li = g_slist_next(li)) { + files[i++] = li->data; + } + g_slist_free(list); + } + } + + g_free(cwd); + + return files; +} diff --git a/src/util.h b/src/util.h index 07ab0bf..d6187c1 100644 --- a/src/util.h +++ b/src/util.h @@ -90,4 +90,8 @@ gboolean is_url(gchar * string); gboolean is_magnet(gchar * string); GtkWidget *gtr_combo_box_new_enum(const char *text_1, ...); +gchar **convert_args(int argc, char *argv[]); +gboolean should_be_minimised(int argc, char *argv[]); +gboolean is_minimised_arg(gchar * arg); + #endif /* UTIL_H_ */ diff --git a/src/win32-mailslot.c b/src/win32-mailslot.c new file mode 100644 index 0000000..50fadad --- /dev/null +++ b/src/win32-mailslot.c @@ -0,0 +1,192 @@ +/* + * transmission-remote-gtk - A GTK RPC client to Transmission + * Copyright (C) 2011 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. + */ + +#define TRG_MAILSLOT_NAME "\\\\.\\mailslot\\TransmissionRemoteGTK" //Name given to the Mailslot +#define MAILSLOT_BUFFER_SIZE 1024*32 + +#include +#include +#include +#include + +#include "trg-main-window.h" +#include "win32-mailslot.h" + +struct trg_mailslot_recv_args { + TrgMainWindow *win; + gchar **uris; + gboolean present; +}; + +/* to be queued into the glib main loop with g_idle_add() */ + +static gboolean mailslot_recv_args(gpointer data) +{ + struct trg_mailslot_recv_args *args = + (struct trg_mailslot_recv_args *) data; + + if (args->present) { + gtk_window_deiconify(GTK_WINDOW(args->win)); + gtk_window_present(GTK_WINDOW(args->win)); + } + + if (args->uris) + trg_add_from_filename(args->win, args->uris); + + g_free(args); + + return FALSE; +} + +static gpointer mailslot_recv_thread(gpointer data) +{ + TrgMainWindow *win = TRG_MAIN_WINDOW(data); + JsonParser *parser; + char szBuffer[MAILSLOT_BUFFER_SIZE]; + HANDLE hMailslot; + DWORD cbBytes; + BOOL bResult; + + hMailslot = CreateMailslot(TRG_MAILSLOT_NAME, // mailslot name + MAILSLOT_BUFFER_SIZE, // input buffer size + MAILSLOT_WAIT_FOREVER, // no timeout + NULL); // default security attribute + + if (INVALID_HANDLE_VALUE == hMailslot) { + g_error("\nError occurred while creating the mailslot: %d", + GetLastError()); + return NULL; //Error + } + + while (1) { + bResult = ReadFile(hMailslot, // handle to mailslot + szBuffer, // buffer to receive data + sizeof(szBuffer), // size of buffer + &cbBytes, // number of bytes read + NULL); // not overlapped I/O + + if ((!bResult) || (0 == cbBytes)) { + g_error("Mailslot error from client: %d", GetLastError()); + break; + } + + parser = json_parser_new(); + + if (json_parser_load_from_data(parser, szBuffer, cbBytes, NULL)) { + JsonNode *node = json_parser_get_root(parser); + JsonObject *obj = json_node_get_object(node); + struct trg_mailslot_recv_args *args = + g_new0(struct trg_mailslot_recv_args, 1); + + args->present = json_object_has_member(obj, "present") + && json_object_get_boolean_member(obj, "present"); + args->win = win; + + if (json_object_has_member(obj, "args")) { + JsonArray *array = + json_object_get_array_member(obj, "args"); + GList *arrayList = json_array_get_elements(array); + + if (arrayList) { + guint arrayLength = g_list_length(arrayList); + guint i = 0; + GList *li; + + args->uris = g_new0(gchar *, arrayLength + 1); + + for (li = arrayList; li; li = g_list_next(li)) { + const gchar *liStr = + json_node_get_string((JsonNode *) li->data); + args->uris[i++] = g_strdup(liStr); + } + + g_list_free(arrayList); + } + } + + json_node_free(node); + + g_idle_add(mailslot_recv_args, args); + } + + g_object_unref(parser); + } + + CloseHandle(hMailslot); + return NULL; //Success +} + +void mailslot_start_background_listener(TrgMainWindow * win) +{ + g_thread_create(mailslot_recv_thread, win, FALSE, NULL); +} + +gboolean mailslot_send_message(gchar ** args) +{ + HANDLE hMailSlot = CreateFile(TRG_MAILSLOT_NAME, // mailslot name + GENERIC_WRITE, // mailslot write only + FILE_SHARE_READ, // required for mailslots + NULL, // default security attributes + OPEN_EXISTING, // opens existing mailslot + FILE_ATTRIBUTE_NORMAL, // normal attributes + NULL); // no template file + + if (hMailSlot != INVALID_HANDLE_VALUE) { + DWORD cbBytes; + JsonNode *node = json_node_new(JSON_NODE_OBJECT); + JsonObject *obj = json_object_new(); + JsonArray *array = json_array_new(); + JsonGenerator *generator; + gchar *msg; + int i; + + if (args) { + for (i = 0; args[i]; i++) + json_array_add_string_element(array, args[i]); + + json_object_set_array_member(obj, "args", array); + + g_strfreev(args); + } else { + json_object_set_boolean_member(obj, "present", TRUE); + } + + json_node_take_object(node, obj); + + generator = json_generator_new(); + json_generator_set_root(generator, node); + msg = json_generator_to_data(generator, NULL); + + json_node_free(node); + g_object_unref(generator); + + WriteFile(hMailSlot, // handle to mailslot + msg, // buffer to write from + strlen(msg) + 1, // number of bytes to write, include the NULL + &cbBytes, // number of bytes written + NULL); + + CloseHandle(hMailSlot); + g_free(msg); + + return TRUE; + } + + return FALSE; +} diff --git a/src/win32-mailslot.h b/src/win32-mailslot.h new file mode 100644 index 0000000..66ff2a5 --- /dev/null +++ b/src/win32-mailslot.h @@ -0,0 +1,29 @@ +/* + * transmission-remote-gtk - A GTK RPC client to Transmission + * Copyright (C) 2011 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. + */ + +#ifndef WIN32_MAILSLOT_H_ +#define WIN32_MAILSLOT_H_ + +#include +#include "trg-main-window.h" + +void mailslot_start_background_listener(TrgMainWindow * win); +int mailslot_send_message(gchar ** args); + +#endif /* WIN32_MAILSLOT_H_ */ -- cgit v1.2.3