/* * This file Copyright (C) Mnemosyne LLC * * This file is licensed by the GPL version 2. Works owned by the * Transmission project are granted a special exemption to clause 2(b) * so that the bulk of its code can remain under the MIT license. * This exemption does not extend to derived works not owned by * the Transmission project. * * $Id$ */ #include #include #include #include #include /* tr_is_same_file() */ #include "conf.h" #include "file-list.h" #include "hig.h" #include "open-dialog.h" #include "tr-prefs.h" #include "util.h" /* gtr_priority_combo_get_value() */ /**** ***** ****/ #define N_RECENT 4 static GSList* get_recent_destinations( void ) { int i; GSList * list = NULL; for( i=0; inext ) l->data = g_strdup( l->data ); /* save the first N_RECENT directories */ for( l=list, i=0; l && ( inext ) { char key[64]; g_snprintf( key, sizeof( key ), "recent-download-dir-%d", i + 1 ); gtr_pref_string_set( key, l->data ); } gtr_pref_save( gtr_core_session( core ) ); /* cleanup */ g_slist_foreach( list, (GFunc)g_free, NULL ); g_slist_free( list ); } /**** ***** ****/ struct OpenData { TrCore * core; GtkWidget * file_list; GtkWidget * run_check; GtkWidget * trash_check; GtkWidget * priority_combo; char * filename; char * downloadDir; tr_torrent * tor; tr_ctor * ctor; }; static void removeOldTorrent( struct OpenData * o ) { if( o->tor ) { gtr_file_list_clear( o->file_list ); tr_torrentRemove( o->tor, FALSE, NULL ); o->tor = NULL; } } static void addResponseCB( GtkDialog * dialog, gint response, gpointer gdata ) { struct OpenData * o = gdata; if( o->tor ) { if( response != GTK_RESPONSE_ACCEPT ) { removeOldTorrent( o ); } else { tr_torrentSetPriority( o->tor, gtr_priority_combo_get_value( GTK_COMBO_BOX( o->priority_combo ) ) ); if( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( o->run_check ) ) ) tr_torrentStart( o->tor ); gtr_core_add_torrent( o->core, o->tor, FALSE ); if( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( o->trash_check ) ) ) gtr_file_trash_or_remove( o->filename ); save_recent_destination( o->core, o->downloadDir ); } } tr_ctorFree( o->ctor ); g_free( o->filename ); g_free( o->downloadDir ); g_free( o ); gtk_widget_destroy( GTK_WIDGET( dialog ) ); } static void updateTorrent( struct OpenData * o ) { const gboolean isLocalFile = tr_ctorGetSourceFile( o->ctor ) != NULL; gtk_widget_set_sensitive( o->trash_check, isLocalFile ); if( !o->tor ) { gtr_file_list_clear( o->file_list ); gtk_widget_set_sensitive( o->file_list, FALSE ); } else { tr_torrentSetDownloadDir( o->tor, o->downloadDir ); gtk_widget_set_sensitive( o->file_list, tr_torrentHasMetadata( o->tor ) ); gtr_file_list_set_torrent( o->file_list, tr_torrentId( o->tor ) ); tr_torrentVerify( o->tor ); } } /** * When the source .torrent file is deleted * (such as, if it was a temp file that a web browser passed to us), * gtk invokes this callback and `filename' will be NULL. * The `filename' tests here are to prevent us from losing the current * metadata when that happens. */ static void sourceChanged( GtkFileChooserButton * b, gpointer gdata ) { struct OpenData * o = gdata; char * filename = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER( b ) ); /* maybe instantiate a torrent */ if( filename || !o->tor ) { int err = 0; int new_file = 0; tr_torrent * torrent; if( filename && ( !o->filename || !tr_is_same_file( filename, o->filename ) ) ) { g_free( o->filename ); o->filename = g_strdup( filename ); tr_ctorSetMetainfoFromFile( o->ctor, o->filename ); new_file = 1; } tr_ctorSetDownloadDir( o->ctor, TR_FORCE, o->downloadDir ); tr_ctorSetPaused( o->ctor, TR_FORCE, TRUE ); tr_ctorSetDeleteSource( o->ctor, FALSE ); if(( torrent = tr_torrentNew( o->ctor, &err ) ) ) { removeOldTorrent( o ); o->tor = torrent; } else if( new_file ) { gtr_add_torrent_error_dialog( GTK_WIDGET( b ), err, o->filename ); } updateTorrent( o ); } g_free( filename ); } static void downloadDirChanged( GtkFileChooserButton * b, gpointer gdata ) { struct OpenData * data = gdata; char * fname = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER( b ) ); if( fname && ( !data->downloadDir || !tr_is_same_file( fname, data->downloadDir ) ) ) { g_free( data->downloadDir ); data->downloadDir = g_strdup( fname ); updateTorrent( data ); } g_free( fname ); } static void addTorrentFilters( GtkFileChooser * chooser ) { GtkFileFilter * filter; filter = gtk_file_filter_new( ); gtk_file_filter_set_name( filter, _( "Torrent files" ) ); gtk_file_filter_add_pattern( filter, "*.torrent" ); gtk_file_chooser_add_filter( chooser, filter ); filter = gtk_file_filter_new( ); gtk_file_filter_set_name( filter, _( "All files" ) ); gtk_file_filter_add_pattern( filter, "*" ); gtk_file_chooser_add_filter( chooser, filter ); } /**** ***** ****/ GtkWidget* gtr_torrent_options_dialog_new( GtkWindow * parent, TrCore * core, tr_ctor * ctor ) { const char * str; GtkWidget * w; GtkWidget * d; GtkGrid * grid; int row; GtkWidget * l; GtkWidget * source_chooser; struct OpenData * data; bool flag; GSList * list; GSList * walk; /* make the dialog */ d = gtk_dialog_new_with_buttons( _( "Torrent Options" ), parent, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL ); gtk_dialog_set_default_response( GTK_DIALOG( d ), GTK_RESPONSE_ACCEPT ); gtk_dialog_set_alternative_button_order( GTK_DIALOG( d ), GTK_RESPONSE_ACCEPT, GTK_RESPONSE_CANCEL, -1 ); if( tr_ctorGetDownloadDir( ctor, TR_FORCE, &str ) ) g_assert_not_reached( ); g_assert( str ); data = g_new0( struct OpenData, 1 ); data->core = core; data->ctor = ctor; data->filename = g_strdup( tr_ctorGetSourceFile( ctor ) ); data->downloadDir = g_strdup( str ); data->file_list = gtr_file_list_new( core, 0 ); str = _( "Mo_ve .torrent file to the trash" ); data->trash_check = gtk_check_button_new_with_mnemonic( str ); str = _( "_Start when added" ); data->run_check = gtk_check_button_new_with_mnemonic( str ); w = data->priority_combo = gtr_priority_combo_new( ); gtr_priority_combo_set_value( GTK_COMBO_BOX( w ), TR_PRI_NORMAL ); g_signal_connect( G_OBJECT( d ), "response", G_CALLBACK( addResponseCB ), data ); row = 0; grid = GTK_GRID( gtk_grid_new( ) ); gtk_container_set_border_width( GTK_CONTAINER( grid ), GUI_PAD_BIG ); gtk_grid_set_row_spacing( grid, GUI_PAD ); gtk_grid_set_column_spacing( grid, GUI_PAD_BIG ); // "torrent file" row l = gtk_label_new_with_mnemonic( _( "_Torrent file:" ) ); gtk_misc_set_alignment( GTK_MISC( l ), 0.0f, 0.5f ); gtk_grid_attach( grid, l, 0, row, 1, 1 ); w = gtk_file_chooser_button_new( _( "Select Source File" ), GTK_FILE_CHOOSER_ACTION_OPEN ); source_chooser = w; gtk_widget_set_hexpand( w, TRUE ); gtk_grid_attach_next_to( grid, w, l, GTK_POS_RIGHT, 1, 1 ); gtk_label_set_mnemonic_widget( GTK_LABEL( l ), w ); addTorrentFilters( GTK_FILE_CHOOSER( w ) ); g_signal_connect( w, "selection-changed", G_CALLBACK( sourceChanged ), data ); // "destination folder" row row++; l = gtk_label_new_with_mnemonic( _( "_Destination folder:" ) ); gtk_misc_set_alignment( GTK_MISC( l ), 0.0f, 0.5f ); gtk_grid_attach( grid, l, 0, row, 1, 1 ); w = gtk_file_chooser_button_new( _( "Select Destination Folder" ), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ); if( !gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER( w ), data->downloadDir ) ) g_warning( "couldn't select '%s'", data->downloadDir ); list = get_recent_destinations( ); for( walk = list; walk; walk = walk->next ) gtk_file_chooser_add_shortcut_folder( GTK_FILE_CHOOSER( w ), walk->data, NULL ); g_slist_free( list ); gtk_grid_attach_next_to( grid, w, l, GTK_POS_RIGHT, 1, 1 ); gtk_label_set_mnemonic_widget( GTK_LABEL( l ), w ); g_signal_connect( w, "selection-changed", G_CALLBACK( downloadDirChanged ), data ); // file list row row++; w = data->file_list; gtk_widget_set_vexpand( w, TRUE ); gtk_widget_set_size_request ( w, 466u, 300u ); gtk_grid_attach( grid, w, 0, row, 2, 1 ); // torrent priority row row++; l = gtk_label_new_with_mnemonic( _( "Torrent _priority:" ) ); gtk_misc_set_alignment( GTK_MISC( l ), 0.0f, 0.5f ); gtk_grid_attach( grid, l, 0, row, 1, 1 ); w = data->priority_combo; gtk_label_set_mnemonic_widget( GTK_LABEL( l ), w ); gtk_grid_attach_next_to( grid, w, l, GTK_POS_RIGHT, 1, 1 ); // torrent priority row row++; w = data->run_check; if( tr_ctorGetPaused( ctor, TR_FORCE, &flag ) ) g_assert_not_reached( ); gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( w ), !flag ); gtk_grid_attach( grid, w, 0, row, 2, 1 ); // "trash .torrent file" row row++; w = data->trash_check; if( tr_ctorGetDeleteSource( ctor, &flag ) ) g_assert_not_reached( ); gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( w ), flag ); gtk_grid_attach( grid, w, 0, row, 2, 1 ); /* trigger sourceChanged, either directly or indirectly, * so that it creates the tor/gtor objects */ w = source_chooser; if( data->filename ) gtk_file_chooser_set_filename( GTK_FILE_CHOOSER( w ), data->filename ); else sourceChanged( GTK_FILE_CHOOSER_BUTTON( w ), data ); gtr_dialog_set_content( GTK_DIALOG( d ), GTK_WIDGET( grid ) ); w = gtk_dialog_get_widget_for_response( GTK_DIALOG( d ), GTK_RESPONSE_ACCEPT ); gtk_widget_grab_focus( w ); return d; } /**** ***** ****/ static void onOpenDialogResponse( GtkDialog * dialog, int response, gpointer core ) { char * folder; /* remember this folder the next time we use this dialog */ folder = gtk_file_chooser_get_current_folder( GTK_FILE_CHOOSER( dialog ) ); gtr_pref_string_set( PREF_KEY_OPEN_DIALOG_FOLDER, folder ); g_free( folder ); if( response == GTK_RESPONSE_ACCEPT ) { GtkFileChooser * chooser = GTK_FILE_CHOOSER( dialog ); GtkWidget * w = gtk_file_chooser_get_extra_widget( chooser ); GtkToggleButton * tb = GTK_TOGGLE_BUTTON( w ); 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_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 ) ); } GtkWidget* gtr_torrent_open_from_file_dialog_new( GtkWindow * parent, TrCore * core ) { GtkWidget * w; GtkWidget * c; const char * folder; w = gtk_file_chooser_dialog_new( _( "Open a Torrent" ), parent, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL ); gtk_dialog_set_alternative_button_order( GTK_DIALOG( w ), GTK_RESPONSE_ACCEPT, GTK_RESPONSE_CANCEL, -1 ); gtk_file_chooser_set_select_multiple( GTK_FILE_CHOOSER( w ), TRUE ); addTorrentFilters( GTK_FILE_CHOOSER( w ) ); g_signal_connect( w, "response", G_CALLBACK( onOpenDialogResponse ), core ); if( ( folder = gtr_pref_string_get( PREF_KEY_OPEN_DIALOG_FOLDER ) ) ) gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER( w ), folder ); c = gtk_check_button_new_with_mnemonic( _( "Show _options dialog" ) ); gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( c ), gtr_pref_flag_get( PREF_KEY_OPTIONS_PROMPT ) ); gtk_file_chooser_set_extra_widget( GTK_FILE_CHOOSER( w ), c ); gtk_widget_show( c ); return w; } /*** **** ***/ static void onOpenURLResponse( GtkDialog * dialog, int response, gpointer user_data ) { bool handled = false; if( response == GTK_RESPONSE_ACCEPT ) { GtkWidget * e = GTK_WIDGET( g_object_get_data( G_OBJECT( dialog ), "url-entry" ) ); char * url = g_strdup( gtk_entry_get_text( GTK_ENTRY( e ) ) ); g_strstrip( url ); if( url ) { handled = gtr_core_add_from_url( user_data, url ); if( !handled ) gtr_unrecognized_url_dialog( GTK_WIDGET( dialog ), url ); g_free( url ); } } else if( response == GTK_RESPONSE_CANCEL ) { handled = TRUE; } if( handled ) gtk_widget_destroy( GTK_WIDGET( dialog ) ); } GtkWidget* gtr_torrent_open_from_url_dialog_new( GtkWindow * parent, TrCore * core ) { guint row; GtkWidget * e; GtkWidget * t; GtkWidget * w; w = gtk_dialog_new_with_buttons( _( "Open URL" ), parent, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL ); gtk_dialog_set_alternative_button_order( GTK_DIALOG( w ), GTK_RESPONSE_ACCEPT, GTK_RESPONSE_CANCEL, -1 ); g_signal_connect( w, "response", G_CALLBACK( onOpenURLResponse ), core ); row = 0; t = hig_workarea_create( ); hig_workarea_add_section_title( t, &row, _( "Open torrent from URL" ) ); e = gtk_entry_new( ); gtk_widget_set_size_request( e, 400, -1 ); gtr_paste_clipboard_url_into_entry( e ); g_object_set_data( G_OBJECT( w ), "url-entry", e ); hig_workarea_add_row( t, &row, _( "_URL" ), e, NULL ); gtr_dialog_set_content( GTK_DIALOG( w ), t ); if( gtk_entry_get_text_length( GTK_ENTRY( e ) ) == 0 ) gtk_widget_grab_focus( e ); else gtk_widget_grab_focus( gtk_dialog_get_widget_for_response( GTK_DIALOG( w ), GTK_RESPONSE_ACCEPT ) ); return w; }