(trunk gtk) first cut at using GApplication. This lets glib replace hundreds of lines of homegrown code. Whee!

This commit is contained in:
Jordan Lee
2011-08-09 02:30:31 +00:00
parent 8d89a16f4f
commit 4d4eeae682
8 changed files with 309 additions and 793 deletions
+3 -88
View File
@@ -44,96 +44,11 @@
#define MY_READABLE_NAME "transmission-gtk"
static char * gl_confdir = NULL;
static char * gl_lockpath = NULL;
/* errstr may be NULL, this might be called before GTK is initialized */
gboolean
cf_init( const char * configDir, char ** errstr )
void
gtr_pref_init( const char * config_dir )
{
if( errstr != NULL )
*errstr = NULL;
gl_confdir = g_strdup( configDir );
if( g_file_test( gl_confdir, G_FILE_TEST_IS_DIR ) )
return true;
if( g_mkdir_with_parents( gl_confdir, 0755 ) )
return true;
if( errstr != NULL )
*errstr = g_strdup_printf( _( "Couldn't create \"%1$s\": %2$s" ),
gl_confdir, g_strerror( errno ) );
return FALSE;
}
/***
****
**** Lockfile
****
***/
/* errstr may be NULL, this might be called before GTK is initialized */
static gboolean
lockfile( const char * filename, gtr_lockfile_state_t * tr_state, char ** errstr )
{
const gtr_lockfile_state_t state = gtr_lockfile( filename );
const gboolean success = state == GTR_LOCKFILE_SUCCESS;
if( errstr ) switch( state )
{
case GTR_LOCKFILE_EOPEN:
*errstr =
g_strdup_printf( _( "Couldn't open \"%1$s\": %2$s" ),
filename, g_strerror( errno ) );
break;
case GTR_LOCKFILE_ELOCK:
*errstr = g_strdup_printf( _( "%s is already running." ),
g_get_application_name( ) );
break;
case GTR_LOCKFILE_SUCCESS:
*errstr = NULL;
break;
}
if( tr_state != NULL )
*tr_state = state;
return success;
}
static char*
getLockFilename( void )
{
g_assert( gl_confdir != NULL );
return g_build_filename( gl_confdir, "lock", NULL );
}
static void
cf_removelocks( void )
{
if( gl_lockpath )
{
g_unlink( gl_lockpath );
g_free( gl_lockpath );
}
}
/* errstr may be NULL, this might be called before GTK is initialized */
gboolean
cf_lock( gtr_lockfile_state_t * tr_state, char ** errstr )
{
char * path = getLockFilename( );
const gboolean didLock = lockfile( path, tr_state, errstr );
if( didLock )
gl_lockpath = g_strdup( path );
g_atexit( cf_removelocks );
g_free( path );
return didLock;
gl_confdir = g_strdup( config_dir );
}
/***
+3 -10
View File
@@ -26,8 +26,9 @@
#define GTR_CONFIG_H
#include <inttypes.h>
#include <libtransmission/transmission.h>
#include "util.h" /* gtr_lockfile */
#include <libtransmission/transmission.h> /* tr_benc, tr_session */
void gtr_pref_init ( const char * config_dir );
int64_t gtr_pref_int_get ( const char * key );
void gtr_pref_int_set ( const char * key, int64_t value );
@@ -44,12 +45,4 @@ void gtr_pref_string_set ( const char * key, const char * val
void gtr_pref_save ( tr_session * );
struct tr_benc* gtr_pref_get_all ( void );
/**
***
**/
gboolean cf_init( const char *confdir, char ** errstr );
gboolean cf_lock( gtr_lockfile_state_t * tr_state, char ** errstr );
#endif /* GTR_CONFIG_H */
+165 -258
View File
@@ -73,7 +73,10 @@ static const char * LICENSE =
struct cbdata
{
char * config_dir;
gboolean start_paused;
gboolean is_iconified;
guint timer;
guint update_model_soon_tag;
guint refresh_actions_tag;
@@ -348,11 +351,7 @@ on_selection_changed( GtkTreeSelection * s UNUSED, gpointer gdata )
****
***/
static void app_setup( TrWindow * wind,
GSList * torrent_files,
struct cbdata * cbdata,
gboolean paused,
gboolean minimized );
static void app_setup( TrWindow * wind, struct cbdata * cbdata );
static void main_window_setup( struct cbdata * cbdata, TrWindow * wind );
@@ -560,240 +559,181 @@ signal_handler( int sig )
}
}
/****
*****
*****
****/
static void
setupsighandlers( void )
on_startup( GApplication * application, gpointer user_data )
{
const char * str;
GtkWindow * win;
GtkUIManager * ui_manager;
tr_session * session;
struct cbdata * cbdata = user_data;
signal( SIGINT, signal_handler );
signal( SIGKILL, signal_handler );
sighandler_cbdata = cbdata;
/* ensure the directories are created */
if(( str = gtr_pref_string_get( TR_PREFS_KEY_DOWNLOAD_DIR )))
g_mkdir_with_parents( str, 0777 );
if(( str = gtr_pref_string_get( TR_PREFS_KEY_INCOMPLETE_DIR )))
g_mkdir_with_parents( str, 0777 );
/* initialize the libtransmission session */
session = tr_sessionInit( "gtk", cbdata->config_dir, TRUE, gtr_pref_get_all( ) );
gtr_pref_flag_set( TR_PREFS_KEY_ALT_SPEED_ENABLED, tr_sessionUsesAltSpeed( session ) );
gtr_pref_int_set( TR_PREFS_KEY_PEER_PORT, tr_sessionGetPeerPort( session ) );
cbdata->core = gtr_core_new( session );
/* init the ui manager */
ui_manager = gtk_ui_manager_new ( );
gtr_actions_init ( ui_manager, cbdata );
gtk_ui_manager_add_ui_from_string ( ui_manager, fallback_ui_file, -1, NULL );
gtk_ui_manager_ensure_update ( ui_manager );
/* create main window now to be a parent to any error dialogs */
win = GTK_WINDOW( gtr_window_new( ui_manager, cbdata->core ) );
g_signal_connect( win, "size-allocate", G_CALLBACK( on_main_window_size_allocated ), cbdata );
g_application_hold( application );
g_object_weak_ref( G_OBJECT( win ), (GWeakNotify)g_application_release, application );
app_setup( win, cbdata );
tr_sessionSetRPCCallback( session, on_rpc_changed, cbdata );
/* check & see if it's time to update the blocklist */
if( gtr_pref_flag_get( TR_PREFS_KEY_BLOCKLIST_ENABLED ) ) {
if( gtr_pref_flag_get( PREF_KEY_BLOCKLIST_UPDATES_ENABLED ) ) {
const int64_t last_time = gtr_pref_int_get( "blocklist-date" );
const int SECONDS_IN_A_WEEK = 7 * 24 * 60 * 60;
const time_t now = time( NULL );
if( last_time + SECONDS_IN_A_WEEK < now )
gtr_core_blocklist_update( cbdata->core );
}
}
/* if there's no magnet link handler registered, register us */
register_magnet_link_handler( );
}
static void
on_activate( GApplication * app UNUSED, gpointer unused UNUSED )
{
gtr_action_activate( "present-main-window" );
}
static void
open_files( GSList * files, gpointer gdata )
{
struct cbdata * cbdata = gdata;
const gboolean do_start = gtr_pref_flag_get( TR_PREFS_KEY_START ) && !cbdata->start_paused;
const gboolean do_prompt = gtr_pref_flag_get( PREF_KEY_OPTIONS_PROMPT );
const gboolean do_notify = TRUE;
gtr_core_add_files( cbdata->core, files, do_start, do_prompt, do_notify );
}
static void
on_open (GApplication * application UNUSED,
GFile ** f,
gint file_count,
gchar * hint UNUSED,
gpointer gdata )
{
int i;
GSList * files = NULL;
for( i=0; i<file_count; ++i )
files = g_slist_append( files, f[i] );
open_files( files, gdata );
g_slist_free( files );
}
/***
****
***/
static GSList *
checkfilenames( int argc, char **argv )
{
int i;
GSList * ret = NULL;
char * pwd = g_get_current_dir( );
for( i=0; i<argc; ++i )
{
const char * arg = argv[i];
if( gtr_is_supported_url( arg ) || gtr_is_magnet_link( arg ) )
{
ret = g_slist_prepend( ret, g_strdup( arg ) );
}
else /* local file */
{
char * filename;
if( g_path_is_absolute( arg ) )
filename = g_strdup( arg );
else {
filename = g_filename_from_uri( arg, NULL, NULL );
if( filename == NULL )
filename = g_build_filename( pwd, arg, NULL );
}
if( g_file_test( filename, G_FILE_TEST_EXISTS ) )
ret = g_slist_prepend( ret, filename );
else {
if( gtr_is_hex_hashcode( argv[i] ) )
ret = g_slist_prepend( ret, g_strdup_printf( "magnet:?xt=urn:btih:%s", argv[i] ) );
g_free( filename );
}
}
}
g_free( pwd );
return g_slist_reverse( ret );
}
/****
*****
*****
****/
int
main( int argc, char ** argv )
{
char * err = NULL;
GSList * argfiles;
GError * gerr;
gboolean didinit = FALSE;
gboolean didlock = FALSE;
gboolean showversion = FALSE;
gboolean startpaused = FALSE;
gboolean startminimized = FALSE;
const char * domain = MY_READABLE_NAME;
char * configDir = NULL;
gtr_lockfile_state_t tr_state;
int ret;
struct stat sb;
char * application_id;
GApplication * app;
GOptionContext * option_context;
bool show_version = false;
GError * error = NULL;
struct cbdata cbdata;
GOptionEntry entries[] = {
{ "paused", 'p', 0, G_OPTION_ARG_NONE,
&startpaused, _( "Start with all torrents paused" ), NULL },
{ "version", '\0', 0, G_OPTION_ARG_NONE,
&showversion, _( "Show version number and exit" ), NULL },
{ "minimized", 'm', 0, G_OPTION_ARG_NONE,
&startminimized,
_( "Start minimized in notification area" ), NULL },
{ "config-dir", 'g', 0, G_OPTION_ARG_FILENAME, &configDir,
_( "Where to look for configuration files" ), NULL },
GOptionEntry option_entries[] = {
{ "config-dir", 'g', 0, G_OPTION_ARG_FILENAME, &cbdata.config_dir, _( "Where to look for configuration files" ), NULL },
{ "paused", 'p', 0, G_OPTION_ARG_NONE, &cbdata.start_paused, _( "Start with all torrents paused" ), NULL },
{ "minimized", 'm', 0, G_OPTION_ARG_NONE, &cbdata.is_iconified, _( "Start minimized in notification area" ), NULL },
{ "version", 'v', 0, G_OPTION_ARG_NONE, &show_version, _( "Show version number and exit" ), NULL },
{ NULL, 0, 0, 0, NULL, NULL, NULL }
};
/* bind the gettext domain */
/* default settings */
memset( &cbdata, 0, sizeof( struct cbdata ) );
cbdata.config_dir = (char*) tr_getDefaultConfigDir( MY_CONFIG_NAME );
/* init i18n */
setlocale( LC_ALL, "" );
bindtextdomain( domain, TRANSMISSIONLOCALEDIR );
bind_textdomain_codeset( domain, "UTF-8" );
textdomain( domain );
g_set_application_name( _( "Transmission" ) );
tr_formatter_mem_init( mem_K, _(mem_K_str), _(mem_M_str), _(mem_G_str), _(mem_T_str) );
tr_formatter_size_init( disk_K, _(disk_K_str), _(disk_M_str), _(disk_G_str), _(disk_T_str) );
tr_formatter_speed_init( speed_K, _(speed_K_str), _(speed_M_str), _(speed_G_str), _(speed_T_str) );
bindtextdomain( MY_READABLE_NAME, TRANSMISSIONLOCALEDIR );
bind_textdomain_codeset( MY_READABLE_NAME, "UTF-8" );
textdomain( MY_READABLE_NAME );
/* initialize gtk */
if( !g_thread_supported( ) )
g_thread_init( NULL );
/* init glib/gtk */
g_thread_init (NULL);
g_type_init ();
gtk_init (&argc, &argv);
g_set_application_name (_( "Transmission" ));
gtk_window_set_default_icon_name (MY_CONFIG_NAME);
gerr = NULL;
if( !gtk_init_with_args( &argc, &argv, (char*)_( "[torrent files or urls]" ), entries,
(char*)domain, &gerr ) )
{
fprintf( stderr, "%s\n", gerr->message );
g_clear_error( &gerr );
return 0;
/* parse the command line */
option_context = g_option_context_new( _( "[torrent files or urls]" ) );
g_option_context_add_main_entries( option_context, option_entries, GETTEXT_PACKAGE );
g_option_context_set_translation_domain( option_context, GETTEXT_PACKAGE );
if( !g_option_context_parse( option_context, &argc, &argv, &error ) ) {
g_print (_("%s\nRun '%s --help' to see a full list of available command line options.\n"), error->message, argv[0]);
g_error_free (error);
g_option_context_free (option_context);
return 1;
}
g_option_context_free (option_context);
if( showversion )
{
/* handle the trivial "version" option */
if( show_version ) {
fprintf( stderr, "%s %s\n", MY_READABLE_NAME, LONG_VERSION_STRING );
return 0;
}
if( configDir == NULL )
configDir = (char*) tr_getDefaultConfigDir( MY_CONFIG_NAME );
/* init the unit formatters */
tr_formatter_mem_init( mem_K, _(mem_K_str), _(mem_M_str), _(mem_G_str), _(mem_T_str) );
tr_formatter_size_init( disk_K, _(disk_K_str), _(disk_M_str), _(disk_G_str), _(disk_T_str) );
tr_formatter_speed_init( speed_K, _(speed_K_str), _(speed_M_str), _(speed_G_str), _(speed_T_str) );
didinit = cf_init( configDir, NULL ); /* must come before actions_init */
/* set up the config dir */
gtr_pref_init( cbdata.config_dir );
g_mkdir_with_parents( cbdata.config_dir, 0755 );
setupsighandlers( ); /* set up handlers for fatal signals */
didlock = cf_lock( &tr_state, &err );
argfiles = checkfilenames( argc - 1, argv + 1 );
if( !didlock && argfiles )
{
/* We have torrents to add but there's another copy of Transmsision
* running... chances are we've been invoked from a browser, etc.
* So send the files over to the "real" copy of Transmission, and
* if that goes well, then our work is done. */
GSList * l;
gboolean delegated = FALSE;
const gboolean trash_originals = gtr_pref_flag_get( TR_PREFS_KEY_TRASH_ORIGINAL );
for( l=argfiles; l!=NULL; l=l->next )
{
const char * filename = l->data;
const gboolean added = gtr_dbus_add_torrent( filename );
if( added && trash_originals )
gtr_file_trash_or_remove( filename );
delegated |= added;
}
if( delegated ) {
g_slist_foreach( argfiles, (GFunc)g_free, NULL );
g_slist_free( argfiles );
argfiles = NULL;
if( err ) {
g_free( err );
err = NULL;
}
}
}
else if( ( !didlock ) && ( tr_state == GTR_LOCKFILE_ELOCK ) )
{
/* There's already another copy of Transmission running,
* so tell it to present its window to the user */
err = NULL;
if( !gtr_dbus_present_window( ) )
err = g_strdup( _( "Transmission is already running, but is not responding. To start a new session, you must first close the existing Transmission process." ) );
}
if( didlock && ( didinit || cf_init( configDir, &err ) ) )
{
/* No other copy of Transmission running...
* so we're going to be the primary. */
const char * str;
GtkWindow * win;
GtkUIManager * myUIManager;
tr_session * session;
struct cbdata * cbdata = g_new0( struct cbdata, 1 );
sighandler_cbdata = cbdata;
/* ensure the directories are created */
if(( str = gtr_pref_string_get( TR_PREFS_KEY_DOWNLOAD_DIR )))
g_mkdir_with_parents( str, 0777 );
if(( str = gtr_pref_string_get( TR_PREFS_KEY_INCOMPLETE_DIR )))
g_mkdir_with_parents( str, 0777 );
/* initialize the libtransmission session */
session = tr_sessionInit( "gtk", configDir, TRUE, gtr_pref_get_all( ) );
gtr_pref_flag_set( TR_PREFS_KEY_ALT_SPEED_ENABLED, tr_sessionUsesAltSpeed( session ) );
gtr_pref_int_set( TR_PREFS_KEY_PEER_PORT, tr_sessionGetPeerPort( session ) );
cbdata->core = gtr_core_new( session );
/* init the ui manager */
myUIManager = gtk_ui_manager_new ( );
gtr_actions_init ( myUIManager, cbdata );
gtk_ui_manager_add_ui_from_string ( myUIManager, fallback_ui_file, -1, NULL );
gtk_ui_manager_ensure_update ( myUIManager );
gtk_window_set_default_icon_name ( MY_CONFIG_NAME );
/* create main window now to be a parent to any error dialogs */
win = GTK_WINDOW( gtr_window_new( myUIManager, cbdata->core ) );
g_signal_connect( win, "size-allocate", G_CALLBACK( on_main_window_size_allocated ), cbdata );
app_setup( win, argfiles, cbdata, startpaused, startminimized );
tr_sessionSetRPCCallback( session, on_rpc_changed, cbdata );
/* on startup, check & see if it's time to update the blocklist */
if( gtr_pref_flag_get( TR_PREFS_KEY_BLOCKLIST_ENABLED ) ) {
if( gtr_pref_flag_get( PREF_KEY_BLOCKLIST_UPDATES_ENABLED ) ) {
const int64_t last_time = gtr_pref_int_get( "blocklist-date" );
const int SECONDS_IN_A_WEEK = 7 * 24 * 60 * 60;
const time_t now = time( NULL );
if( last_time + SECONDS_IN_A_WEEK < now )
gtr_core_blocklist_update( cbdata->core );
}
}
/* if there's no magnet link handler registered, register us */
register_magnet_link_handler( );
gtk_main( );
}
else if( err )
{
const char * primary_text = _( "Transmission cannot be started." );
GtkWidget * w = gtk_message_dialog_new( NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, primary_text, NULL );
gtk_message_dialog_format_secondary_text( GTK_MESSAGE_DIALOG( w ), "%s", err );
g_signal_connect( w, "response", G_CALLBACK(gtk_main_quit), NULL );
gtk_widget_show( w );
g_free( err );
gtk_main( );
}
return 0;
/* init the application for the specified config dir */
stat( cbdata.config_dir, &sb );
application_id = g_strdup_printf( "com.transmissionbt.transmission_%lu_%lu", (unsigned long)sb.st_dev, (unsigned long)sb.st_ino );
app = g_application_new( application_id, G_APPLICATION_HANDLES_OPEN );
g_signal_connect( app, "open", G_CALLBACK(on_open), &cbdata );
g_signal_connect( app, "startup", G_CALLBACK(on_startup), &cbdata );
g_signal_connect( app, "activate", G_CALLBACK(on_activate), &cbdata );
ret = g_application_run (app, argc, argv);
g_object_unref( app );
return ret;
}
static void
@@ -803,19 +743,9 @@ on_core_busy( TrCore * core UNUSED, gboolean busy, struct cbdata * c )
}
static void
app_setup( TrWindow * wind,
GSList * files,
struct cbdata * cbdata,
gboolean pause_all,
gboolean is_iconified )
app_setup( TrWindow * wind, struct cbdata * cbdata )
{
const gboolean do_start = gtr_pref_flag_get( TR_PREFS_KEY_START ) && !pause_all;
const gboolean do_prompt = gtr_pref_flag_get( PREF_KEY_OPTIONS_PROMPT );
const gboolean do_notify = TRUE;
cbdata->is_iconified = is_iconified;
if( is_iconified )
if( cbdata->is_iconified )
gtr_pref_flag_set( PREF_KEY_SHOW_TRAY_ICON, TRUE );
gtr_actions_set_core( cbdata->core );
@@ -827,9 +757,7 @@ app_setup( TrWindow * wind,
g_signal_connect( cbdata->core, "prefs-changed", G_CALLBACK( on_prefs_changed ), cbdata );
/* add torrents from command-line and saved state */
gtr_core_load( cbdata->core, pause_all );
gtr_core_add_list( cbdata->core, files, do_start, do_prompt, do_notify );
files = NULL;
gtr_core_load( cbdata->core, cbdata->start_paused );
gtr_core_torrents_added( cbdata->core );
/* set up main window */
@@ -843,7 +771,7 @@ app_setup( TrWindow * wind,
update_model_once( cbdata );
/* either show the window or iconify it */
if( !is_iconified )
if( !cbdata->is_iconified )
gtk_widget_show( GTK_WIDGET( wind ) );
else
{
@@ -954,40 +882,22 @@ on_drag_data_received( GtkWidget * widget UNUSED,
guint time_,
gpointer gdata )
{
int i;
gboolean success = FALSE;
GSList * files = NULL;
struct cbdata * data = gdata;
guint i;
char ** uris = gtk_selection_data_get_uris( selection_data );
const guint file_count = g_strv_length( uris );
GSList * files = NULL;
/* try to add the filename URIs... */
for( i=0; uris && uris[i]; ++i )
{
const char * uri = uris[i];
char * filename = g_filename_from_uri( uri, NULL, NULL );
for( i=0; i<file_count; ++i )
files = g_slist_append( files, g_file_new_for_uri( uris[i] ) );
if( filename && g_file_test( filename, G_FILE_TEST_EXISTS ) )
{
files = g_slist_append( files, g_strdup( filename ) );
success = TRUE;
}
else if( tr_urlIsValid( uri, -1 ) || gtr_is_magnet_link( uri ) )
{
gtr_core_add_from_url( data->core, uri );
success = TRUE;
}
g_free( filename );
}
if( files )
gtr_core_add_list_defaults( data->core, g_slist_reverse( files ), TRUE );
gtr_core_torrents_added( data->core );
gtk_drag_finish( drag_context, success, FALSE, time_ );
open_files( files, gdata );
/* cleanup */
g_slist_foreach( files, (GFunc)g_object_unref, NULL );
g_slist_free( files );
g_strfreev( uris );
gtk_drag_finish( drag_context, true, FALSE, time_ );
}
static void
@@ -1037,9 +947,6 @@ on_session_closed( gpointer gdata )
g_slist_free( cbdata->error_list );
g_slist_foreach( cbdata->duplicates_list, (GFunc)g_free, NULL );
g_slist_free( cbdata->duplicates_list );
g_free( cbdata );
gtk_main_quit( );
return FALSE;
}
+14 -22
View File
@@ -23,6 +23,7 @@
#include "hig.h"
#include "open-dialog.h"
#include "tr-prefs.h"
#include "util.h" /* gtr_priority_combo_get_value() */
/****
*****
@@ -403,12 +404,14 @@ onOpenDialogResponse( GtkDialog * dialog, int response, gpointer core )
GtkFileChooser * chooser = GTK_FILE_CHOOSER( dialog );
GtkWidget * w = gtk_file_chooser_get_extra_widget( chooser );
GtkToggleButton * tb = GTK_TOGGLE_BUTTON( w );
const gboolean doStart = gtr_pref_flag_get( TR_PREFS_KEY_START );
const gboolean doPrompt = gtk_toggle_button_get_active( tb );
const gboolean doNotify = FALSE;
GSList * l = gtk_file_chooser_get_filenames( chooser );
const gboolean do_start = gtr_pref_flag_get( TR_PREFS_KEY_START );
const gboolean do_prompt = gtk_toggle_button_get_active( tb );
const gboolean do_notify = FALSE;
GSList * files = gtk_file_chooser_get_files( chooser );
gtr_core_add_list( core, l, doStart, doPrompt, doNotify );
gtr_core_add_files( core, files, do_start, do_prompt, do_notify );
g_slist_foreach( files, (GFunc)g_object_unref, NULL );
g_slist_free( files );
}
gtk_widget_destroy( GTK_WIDGET( dialog ) );
@@ -453,7 +456,7 @@ gtr_torrent_open_from_file_dialog_new( GtkWindow * parent, TrCore * core )
static void
onOpenURLResponse( GtkDialog * dialog, int response, gpointer user_data )
{
gboolean destroy = TRUE;
bool handled = false;
if( response == GTK_RESPONSE_ACCEPT )
{
@@ -461,26 +464,15 @@ onOpenURLResponse( GtkDialog * dialog, int response, gpointer user_data )
char * url = g_strdup( gtk_entry_get_text( GTK_ENTRY( e ) ) );
g_strstrip( url );
if( url && *url )
{
TrCore * core = user_data;
if( gtr_is_supported_url( url ) || gtr_is_magnet_link( url )
|| gtr_is_hex_hashcode( url ) )
{
gtr_core_add_from_url( core, url );
}
else
{
if( url ) {
handled = gtr_core_add_from_url( user_data, url );
if( !handled )
gtr_unrecognized_url_dialog( GTK_WIDGET( dialog ), url );
destroy = FALSE;
}
g_free( url );
}
g_free( url );
}
if( destroy )
if( handled )
gtk_widget_destroy( GTK_WIDGET( dialog ) );
}
+117 -255
View File
@@ -78,8 +78,6 @@ struct TrCorePrivate
gboolean have_inhibit_cookie;
gboolean dbus_error;
guint inhibit_cookie;
guint dbus_session_owner_id;
guint dbus_display_owner_id;
gint busy_count;
GtkTreeModel * raw_model;
GtkTreeModel * sorted_model;
@@ -192,108 +190,6 @@ gtr_core_class_init( gpointer g_class, gpointer g_class_data UNUSED )
1, G_TYPE_STRING );
}
static void
handle_dbus_method( GDBusConnection * connection UNUSED,
const gchar * sender UNUSED,
const gchar * object_path,
const gchar * interface_name,
const gchar * method_name,
GVariant * parameters,
GDBusMethodInvocation * invocation,
gpointer core )
{
gboolean handled = false;
if( !g_strcmp0( interface_name, TR_DBUS_SESSION_INTERFACE ) )
{
if( !g_strcmp0( method_name, "TorrentAdd" ) )
{
GVariant * args = g_variant_get_child_value( parameters, 0 );
GVariant * filename_variant = g_variant_lookup_value ( args, "filename", G_VARIANT_TYPE_STRING );
char * filename = g_variant_dup_string( filename_variant, NULL );
GSList * files = g_slist_append( NULL, filename );
gtr_core_add_list_defaults( TR_CORE( core ), files, TRUE );
g_dbus_method_invocation_return_value( invocation, g_variant_new( "(b)", true ) );
handled = true;
}
}
else if( !g_strcmp0( interface_name, TR_DBUS_DISPLAY_INTERFACE ) )
{
if( !g_strcmp0( method_name, "PresentWindow" ) )
{
gtr_action_activate( "present-main-window" );
g_dbus_method_invocation_return_value( invocation, NULL );
handled = true;
}
}
if( !handled )
g_warning( "Unhandled method call:\n\tObject Path: %s\n\tInterface Name: %s\n\tMethod Name: %s",
object_path, interface_name, method_name );
};
static void
on_session_registered_in_dbus( GDBusConnection *connection, const gchar *name UNUSED, gpointer core )
{
GError * err = NULL;
GDBusNodeInfo * node_info;
GDBusInterfaceVTable vtable;
const char * interface_xml = "<node>"
" <interface name='" TR_DBUS_SESSION_INTERFACE "'>"
" <method name='TorrentAdd'>"
" <arg type='a{sv}' name='args' direction='in'/>"
" <arg type='b' name='response' direction='out'/>"
" </method>"
" </interface>"
"</node>";
node_info = g_dbus_node_info_new_for_xml( interface_xml, &err );
vtable.method_call = handle_dbus_method;
vtable.get_property = NULL;
vtable.set_property = NULL;
g_dbus_connection_register_object ( connection,
TR_DBUS_SESSION_OBJECT_PATH,
node_info->interfaces[0],
&vtable,
core,
NULL,
&err );
if( err != NULL ) {
g_warning( "%s:%d Error registering object: %s", __FILE__, __LINE__, err->message );
g_error_free( err );
}
}
static void
on_display_registered_in_dbus( GDBusConnection *connection, const gchar *name UNUSED, gpointer core )
{
GError * err = NULL;
const char * interface_xml = "<node>"
" <interface name='" TR_DBUS_DISPLAY_INTERFACE "'>"
" <method name='PresentWindow'>"
" </method>"
" </interface>"
"</node>";
GDBusInterfaceVTable vtable = { .method_call=handle_dbus_method };
GDBusNodeInfo * node_info = g_dbus_node_info_new_for_xml( interface_xml, &err );
g_dbus_connection_register_object ( connection,
TR_DBUS_DISPLAY_OBJECT_PATH,
node_info->interfaces[0],
&vtable,
core,
NULL,
&err );
if( err != NULL ) {
g_warning( "%s:%d Error registering object: %s", __FILE__, __LINE__, err->message );
g_error_free( err );
}
}
static void
core_init( GTypeInstance * instance, gpointer g_class UNUSED )
{
@@ -330,24 +226,6 @@ core_init( GTypeInstance * instance, gpointer g_class UNUSED )
p->sorted_model = gtk_tree_model_sort_new_with_model( p->raw_model );
p->string_chunk = g_string_chunk_new( 2048 );
g_object_unref( p->raw_model );
p->dbus_session_owner_id = g_bus_own_name( G_BUS_TYPE_SESSION,
TR_DBUS_SESSION_SERVICE_NAME,
G_BUS_NAME_OWNER_FLAGS_NONE,
NULL,
on_session_registered_in_dbus,
NULL,
self,
NULL );
p->dbus_display_owner_id = g_bus_own_name( G_BUS_TYPE_SESSION,
TR_DBUS_DISPLAY_SERVICE_NAME,
G_BUS_NAME_OWNER_FLAGS_NONE,
NULL,
on_display_registered_in_dbus,
NULL,
self,
NULL );
}
GType
@@ -755,6 +633,8 @@ core_watchdir_idle( gpointer gcore )
TrCore * core = TR_CORE( gcore );
const time_t now = tr_time( );
struct TrCorePrivate * p = core->priv;
const gboolean do_start = gtr_pref_flag_get( TR_PREFS_KEY_START );
const gboolean do_prompt = gtr_pref_flag_get( PREF_KEY_OPTIONS_PROMPT );
/* of the monitor_files, make a list of those that haven't
* changed lately, since they should be ready to add */
@@ -764,14 +644,16 @@ core_watchdir_idle( gpointer gcore )
if( f->mtime + 2 >= now )
monitor_files = g_slist_prepend( monitor_files, f );
else {
addme = g_slist_prepend( addme, g_strdup( f->filename ) );
addme = g_slist_prepend( addme, g_file_new_for_commandline_arg( f->filename ) );
watchdir_file_free( f );
}
}
/* add the torrents from that list */
core->priv->adding_from_watch_dir = TRUE;
gtr_core_add_list_defaults( core, addme, TRUE );
gtr_core_add_files( core, addme, do_start, do_prompt, TRUE );
g_slist_foreach( addme, (GFunc)g_object_unref, NULL );
g_slist_free( addme );
core->priv->adding_from_watch_dir = FALSE;
/* update the monitor_files list */
@@ -941,9 +823,6 @@ gtr_core_close( TrCore * core )
gtr_pref_save( session );
tr_sessionClose( session );
}
g_bus_unown_name( core->priv->dbus_display_owner_id );
g_bus_unown_name( core->priv->dbus_session_owner_id );
}
/***
@@ -1221,169 +1100,152 @@ gtr_core_add_ctor( TrCore * core, tr_ctor * ctor )
****
***/
struct url_dialog_data
struct add_from_url_data
{
TrCore * core;
tr_ctor * ctor;
char * url;
bool did_connect;
bool did_timeout;
long response_code;
bool do_prompt;
bool do_notify;
};
static gboolean
on_url_done_idle( gpointer vdata )
static void
add_file_async_callback( GObject * file, GAsyncResult * result, gpointer gdata )
{
struct url_dialog_data * data = vdata;
gsize length;
char * contents;
GError * error = NULL;
struct add_from_url_data * data = gdata;
if( data->response_code != 200 )
if( !g_file_load_contents_finish( G_FILE( file ), result, &contents, &length, NULL, &error ) )
{
gtr_http_failure_dialog( NULL, data->url, data->response_code );
g_message( _( "Couldn't read \"%s\": %s" ), g_file_get_parse_name( G_FILE( file ) ), error->message );
g_error_free( error );
}
else if( !tr_ctorSetMetainfo( data->ctor, (const uint8_t*)contents, length ) )
{
core_add_ctor( data->core, data->ctor, data->do_prompt, data->do_notify );
}
else
{
const gboolean do_prompt = gtr_pref_flag_get( PREF_KEY_OPTIONS_PROMPT );
const gboolean do_notify = FALSE;
const int err = core_add_ctor( data->core, data->ctor, do_prompt, do_notify );
if( err == TR_PARSE_ERR )
core_emit_err( data->core, TR_PARSE_ERR, data->url );
gtr_core_torrents_added( data->core );
tr_ctorFree( data->ctor );
}
/* cleanup */
core_dec_busy( data->core );
g_free( data->url );
g_free( data );
return FALSE;
}
static void
on_url_done( tr_session * session,
bool did_connect,
bool did_timeout,
long response_code,
const void * response,
size_t response_byte_count,
void * vdata )
{
struct url_dialog_data * data = vdata;
data->did_connect = did_connect;
data->did_timeout = did_timeout;
data->response_code = response_code;
data->ctor = tr_ctorNew( session );
core_apply_defaults( data->ctor );
tr_ctorSetMetainfo( data->ctor, response, response_byte_count );
gdk_threads_add_idle( on_url_done_idle, data );
}
void
gtr_core_add_from_url( TrCore * core, const char * url )
{
tr_session * session = gtr_core_session( core );
const gboolean is_magnet_link = gtr_is_magnet_link( url );
if( is_magnet_link || gtr_is_hex_hashcode( url ) )
{
int err;
char * tmp = NULL;
tr_ctor * ctor = tr_ctorNew( session );
if( gtr_is_hex_hashcode( url ) )
url = tmp = g_strdup_printf( "magnet:?xt=urn:btih:%s", url );
err = tr_ctorSetMetainfoFromMagnetLink( ctor, url );
if( !err )
gtr_core_add_ctor( core, ctor );
else {
gtr_unrecognized_url_dialog( NULL, url );
tr_ctorFree( ctor );
}
g_free( tmp );
}
else
{
struct url_dialog_data * data = g_new( struct url_dialog_data, 1 );
data->core = core;
data->url = g_strdup( url );
core_inc_busy( data->core );
tr_webRun( session, url, NULL, NULL, on_url_done, data );
}
}
/***
****
***/
static void
add_filename( TrCore * core,
const char * filename,
gboolean do_start,
gboolean do_prompt,
gboolean do_notify )
static bool
add_file( TrCore * core,
GFile * file,
gboolean do_start,
gboolean do_prompt,
gboolean do_notify )
{
bool handled = false;
tr_session * session = gtr_core_session( core );
if( session == NULL )
return;
if( gtr_is_supported_url( filename ) || gtr_is_magnet_link( filename ) )
if( session != NULL )
{
gtr_core_add_from_url( core, filename );
}
else if( g_file_test( filename, G_FILE_TEST_EXISTS ) )
{
int err;
tr_ctor * ctor;
bool tried = false;
bool loaded = false;
tr_ctor * ctor = tr_ctorNew( session );
tr_ctorSetMetainfoFromFile( ctor, filename );
ctor = tr_ctorNew( session );
core_apply_defaults( ctor );
tr_ctorSetPaused( ctor, TR_FORCE, !do_start );
err = core_add_ctor( core, ctor, do_prompt, do_notify );
if( err == TR_PARSE_ERR )
core_emit_err( core, TR_PARSE_ERR, filename );
}
else if( gtr_is_hex_hashcode( filename ) )
{
gtr_core_add_from_url( core, filename );
/* local files... */
if( !tried ) {
char * str = g_file_get_path( file );
if(( tried = g_file_test( str, G_FILE_TEST_EXISTS )))
loaded = !tr_ctorSetMetainfoFromFile( ctor, str );
g_free( str );
}
/* magnet links... */
if( !tried && g_file_has_uri_scheme( file, "magnet" ) ) {
/* GFile mangles the original string with /// so we have to un-mangle */
char * str = g_file_get_parse_name( file );
char * magnet = g_strdup_printf( "magnet:%s", strchr( str, '?' ) );
tried = true;
loaded = !tr_ctorSetMetainfoFromMagnetLink( ctor, magnet );
g_free( magnet );
g_free( str );
}
/* hashcodes that we can turn into magnet links... */
if( !tried ) {
char * str = g_file_get_basename( file );
if( gtr_is_hex_hashcode( str ) ) {
char * magnet = g_strdup_printf( "magnet:?xt=urn:btih:%s", str );
tried = true;
loaded = !tr_ctorSetMetainfoFromMagnetLink( ctor, magnet );
g_free( magnet );
}
g_free( str );
}
/* if we were able to load the metainfo, add the torrent */
if( loaded )
{
handled = true;
core_add_ctor( core, ctor, do_prompt, do_notify );
}
else if( g_file_has_uri_scheme( file, "http" ) ||
g_file_has_uri_scheme( file, "https" ) ||
g_file_has_uri_scheme( file, "ftp" ) )
{
struct add_from_url_data * data;
data = g_new0( struct add_from_url_data, 1 );
data->core = core;
data->ctor = ctor;
data->do_prompt = do_prompt;
data->do_notify = do_notify;
handled = true;
core_inc_busy( core );
g_file_load_contents_async( file, NULL, add_file_async_callback, data );
}
else
{
tr_ctorFree( ctor );
g_message( _( "Skipping unknown torrent \"%s\"" ), g_file_get_parse_name( file ) );
}
}
return handled;
}
bool
gtr_core_add_from_url( TrCore * core, const char * uri )
{
bool handled;
const bool do_start = gtr_pref_flag_get( TR_PREFS_KEY_START );
const bool do_prompt = gtr_pref_flag_get( PREF_KEY_OPTIONS_PROMPT );
const bool do_notify = false;
GFile * file = g_file_new_for_uri( uri );
handled = add_file( core, file, do_start, do_prompt, do_notify );
g_object_unref( file );
gtr_core_torrents_added( core );
return handled;
}
void
gtr_core_add_list( TrCore * core,
GSList * files,
gboolean do_start,
gboolean do_prompt,
gboolean do_notify )
gtr_core_add_files( TrCore * core,
GSList * files,
gboolean do_start,
gboolean do_prompt,
gboolean do_notify )
{
GSList * l;
for( l=files; l!=NULL; l=l->next )
{
char * filename = l->data;
add_filename( core, filename, do_start, do_prompt, do_notify );
g_free( filename );
}
add_file( core, l->data, do_start, do_prompt, do_notify );
gtr_core_torrents_added( core );
g_slist_free( files );
}
void
gtr_core_add_list_defaults( TrCore * core, GSList * files, gboolean do_notify )
{
const gboolean do_start = gtr_pref_flag_get( TR_PREFS_KEY_START );
const gboolean do_prompt = gtr_pref_flag_get( PREF_KEY_OPTIONS_PROMPT );
gtr_core_add_list( core, files, do_start, do_prompt, do_notify );
}
void
+6 -20
View File
@@ -106,19 +106,14 @@ void gtr_core_load( TrCore * self, gboolean forcepaused );
* May pop up dialogs for each torrent if that preference is enabled.
* May trigger one or more "error" signals with TR_CORE_ERR_ADD_TORRENT
*/
void gtr_core_add_list( TrCore * self,
GSList * torrentFiles,
gboolean do_start,
gboolean do_prompt,
gboolean do_notify );
void gtr_core_add_list_defaults( TrCore * core,
GSList * torrentFiles,
gboolean do_notify );
void gtr_core_add_files( TrCore * core,
GSList * files,
gboolean do_start,
gboolean do_prompt,
gboolean do_notify );
/** @brief Add a torrent from a URL */
void gtr_core_add_from_url( TrCore * core, const char * url );
bool gtr_core_add_from_url( TrCore * core, const char * url );
/** @brief Add a torrent.
@param ctor this function assumes ownership of the ctor */
@@ -200,13 +195,4 @@ enum
};
#define TR_DBUS_SESSION_SERVICE_NAME "com.transmissionbt.transmission.Session"
#define TR_DBUS_SESSION_INTERFACE "com.transmissionbt.transmission.Session"
#define TR_DBUS_SESSION_OBJECT_PATH "/com/transmissionbt/transmission/Session"
#define TR_DBUS_DISPLAY_SERVICE_NAME "com.transmissionbt.transmission.Display"
#define TR_DBUS_DISPLAY_INTERFACE "com.transmissionbt.transmission.Display"
#define TR_DBUS_DISPLAY_OBJECT_PATH "/com/transmissionbt/transmission/Display"
#endif /* GTR_CORE_H */
+1 -120
View File
@@ -15,10 +15,6 @@
#include <stdarg.h>
#include <string.h> /* strchr(), strrchr(), strlen(), strncmp(), strstr() */
#include <sys/types.h> /* for gtr_lockfile()'s open() */
#include <sys/stat.h> /* for gtr_lockfile()'s open() */
#include <fcntl.h> /* for gtr_lockfile()'s open() */
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h> /* g_unlink() */
@@ -59,54 +55,6 @@ const char * speed_T_str = N_("TiB/s");
****
***/
gtr_lockfile_state_t
gtr_lockfile( const char * filename )
{
gtr_lockfile_state_t ret;
#ifdef WIN32
HANDLE file = CreateFile( filename,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL );
if( file == INVALID_HANDLE_VALUE )
ret = GTR_LOCKFILE_EOPEN;
else if( !LockFile( file, 0, 0, 1, 1 ) )
ret = GTR_LOCKFILE_ELOCK;
else
ret = GTR_LOCKFILE_SUCCESS;
#else
int fd = open( filename, O_RDWR | O_CREAT, 0666 );
if( fd < 0 )
ret = GTR_LOCKFILE_EOPEN;
else {
struct flock lk;
memset( &lk, 0, sizeof( lk ) );
lk.l_start = 0;
lk.l_len = 0;
lk.l_type = F_WRLCK;
lk.l_whence = SEEK_SET;
if( -1 == fcntl( fd, F_SETLK, &lk ) )
ret = GTR_LOCKFILE_ELOCK;
else
ret = GTR_LOCKFILE_SUCCESS;
}
#endif
return ret;
}
/***
****
***/
const char*
gtr_get_unicode_string( int i )
{
@@ -218,7 +166,7 @@ gtr_get_host_from_url( char * buf, size_t buflen, const char * url )
}
}
gboolean
static gboolean
gtr_is_supported_url( const char * str )
{
return !strncmp( str, "ftp://", 6 )
@@ -432,73 +380,6 @@ gtr_open_uri( const char * uri )
}
}
gboolean
gtr_dbus_add_torrent( const char * filename )
{
GVariant * response;
GVariant * args;
GVariant * parameters;
GVariantBuilder * builder;
GDBusConnection * connection;
GError * err = NULL;
gboolean handled = FALSE;
/* "args" is a dict as described in the RPC spec's "torrent-add" section */
builder = g_variant_builder_new( G_VARIANT_TYPE( "a{sv}" ) );
g_variant_builder_add( builder, "{sv}", "filename", g_variant_new_string( filename ) );
args = g_variant_builder_end( builder );
parameters = g_variant_new_tuple( &args, 1 );
g_variant_builder_unref( builder );
connection = g_bus_get_sync( G_BUS_TYPE_SESSION, NULL, &err );
response = g_dbus_connection_call_sync( connection,
TR_DBUS_SESSION_SERVICE_NAME,
TR_DBUS_SESSION_OBJECT_PATH,
TR_DBUS_SESSION_INTERFACE,
"TorrentAdd",
parameters,
G_VARIANT_TYPE_TUPLE,
G_DBUS_CALL_FLAGS_NONE,
10000, /* wait 10 sec */
NULL,
&err );
handled = g_variant_get_boolean( g_variant_get_child_value( response, 0 ) );
g_object_unref( connection );
g_variant_unref( response );
return handled;
}
gboolean
gtr_dbus_present_window( void )
{
gboolean success;
GDBusConnection * connection;
GError * err = NULL;
connection = g_bus_get_sync( G_BUS_TYPE_SESSION, NULL, &err );
g_dbus_connection_call_sync( connection,
TR_DBUS_DISPLAY_SERVICE_NAME,
TR_DBUS_DISPLAY_OBJECT_PATH,
TR_DBUS_DISPLAY_INTERFACE,
"PresentWindow",
NULL,
NULL, G_DBUS_CALL_FLAGS_NONE,
1000, NULL, &err );
success = err == NULL;
/* cleanup */
if( err != NULL )
g_error_free( err );
g_object_unref( connection );
return success;
}
/***
****
***/
-20
View File
@@ -70,8 +70,6 @@ char* tr_strltime( char * buf, int secs, size_t buflen );
/* http://www.legaltorrents.com/some/announce/url --> legaltorrents.com */
void gtr_get_host_from_url( char * buf, size_t buflen, const char * url );
gboolean gtr_is_supported_url( const char * str );
gboolean gtr_is_magnet_link( const char * str );
gboolean gtr_is_hex_hashcode( const char * str );
@@ -80,28 +78,10 @@ gboolean gtr_is_hex_hashcode( const char * str );
****
***/
typedef enum
{
GTR_LOCKFILE_SUCCESS = 0,
GTR_LOCKFILE_EOPEN,
GTR_LOCKFILE_ELOCK
}
gtr_lockfile_state_t;
gtr_lockfile_state_t gtr_lockfile( const char * filename );
/***
****
***/
void gtr_open_uri( const char * uri );
void gtr_open_file( const char * path );
gboolean gtr_dbus_add_torrent( const char * filename );
gboolean gtr_dbus_present_window( void );
const char* gtr_get_help_uri( void );
/***