2 * This file Copyright (C) Mnemosyne LLC
4 * This file is licensed by the GPL version 2. Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
10 * $Id: open-dialog.c 11721 2011-01-20 19:57:42Z jordan $
13 #include <glib/gi18n.h>
18 #include <libtransmission/transmission.h>
19 #include <libtransmission/utils.h> /* tr_is_same_file() */
22 #include "file-list.h"
24 #include "open-dialog.h"
34 get_recent_destinations( void )
39 for( i
=0; i
<N_RECENT
; ++i
)
43 g_snprintf( key
, sizeof( key
), "recent-download-dir-%d", i
+1 );
44 if(( val
= gtr_pref_string_get( key
)))
45 list
= g_slist_append( list
, (void*)val
);
51 save_recent_destination( TrCore
* core
, const char * dir
)
55 GSList
* list
= get_recent_destinations( );
60 /* if it was already in the list, remove it */
61 if(( l
= g_slist_find_custom( list
, dir
, (GCompareFunc
)strcmp
)))
62 list
= g_slist_delete_link( list
, l
);
64 /* add it to the front of the list */
65 list
= g_slist_prepend( list
, (void*)dir
);
67 /* make local copies of the strings that aren't
68 * invalidated by gtr_pref_string_set() */
69 for( l
=list
; l
; l
=l
->next
)
70 l
->data
= g_strdup( l
->data
);
72 /* save the first N_RECENT directories */
73 for( l
=list
, i
=0; l
&& ( i
<N_RECENT
); ++i
, l
=l
->next
) {
75 g_snprintf( key
, sizeof( key
), "recent-download-dir-%d", i
+ 1 );
76 gtr_pref_string_set( key
, l
->data
);
78 gtr_pref_save( tr_core_session( core
) );
81 g_slist_foreach( list
, (GFunc
)g_free
, NULL
);
92 GtkWidget
* file_list
;
93 GtkWidget
* run_check
;
94 GtkWidget
* trash_check
;
95 GtkWidget
* priority_combo
;
103 removeOldTorrent( struct OpenData
* data
)
107 gtr_file_list_clear( data
->file_list
);
108 tr_torrent_set_remove_flag( data
->gtor
, TRUE
);
109 g_object_unref( G_OBJECT( data
->gtor
) );
115 addResponseCB( GtkDialog
* dialog
,
119 struct OpenData
* data
= gdata
;
123 if( response
!= GTK_RESPONSE_ACCEPT
)
125 removeOldTorrent( data
);
129 tr_torrent
* tor
= tr_torrent_handle( data
->gtor
);
131 tr_torrentSetPriority( tor
, gtr_priority_combo_get_value( GTK_COMBO_BOX( data
->priority_combo
) ) );
133 if( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( data
->run_check
) ) )
134 tr_torrentStart( tor
);
136 tr_core_add_torrent( data
->core
, data
->gtor
, FALSE
);
138 if( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( data
->trash_check
) ) )
139 gtr_file_trash_or_remove( data
->filename
);
141 save_recent_destination( data
->core
, data
->downloadDir
);
145 tr_ctorFree( data
->ctor
);
146 g_free( data
->filename
);
147 g_free( data
->downloadDir
);
149 gtk_widget_destroy( GTK_WIDGET( dialog
) );
153 updateTorrent( struct OpenData
* o
)
155 const gboolean isLocalFile
= tr_ctorGetSourceFile( o
->ctor
) != NULL
;
156 gtk_widget_set_sensitive( o
->trash_check
, isLocalFile
);
160 gtr_file_list_clear( o
->file_list
);
161 gtk_widget_set_sensitive( o
->file_list
, FALSE
);
165 tr_torrent
* tor
= tr_torrent_handle( o
->gtor
);
166 tr_torrentSetDownloadDir( tor
, o
->downloadDir
);
167 gtk_widget_set_sensitive( o
->file_list
, tr_torrentHasMetadata( tor
) );
168 gtr_file_list_set_torrent( o
->file_list
, tr_torrentId( tor
) );
169 tr_torrentVerify( tor
);
174 * When the source .torrent file is deleted
175 * (such as, if it was a temp file that a web browser passed to us),
176 * gtk invokes this callback and `filename' will be NULL.
177 * The `filename' tests here are to prevent us from losing the current
178 * metadata when that happens.
181 sourceChanged( GtkFileChooserButton
* b
, gpointer gdata
)
183 struct OpenData
* data
= gdata
;
184 char * filename
= gtk_file_chooser_get_filename( GTK_FILE_CHOOSER( b
) );
186 /* maybe instantiate a torrent */
187 if( filename
|| !data
->gtor
)
191 tr_torrent
* torrent
;
193 if( filename
&& ( !data
->filename
|| !tr_is_same_file( filename
, data
->filename
) ) )
195 g_free( data
->filename
);
196 data
->filename
= g_strdup( filename
);
197 tr_ctorSetMetainfoFromFile( data
->ctor
, data
->filename
);
201 tr_ctorSetDownloadDir( data
->ctor
, TR_FORCE
, data
->downloadDir
);
202 tr_ctorSetPaused( data
->ctor
, TR_FORCE
, TRUE
);
203 tr_ctorSetDeleteSource( data
->ctor
, FALSE
);
205 if( ( torrent
= tr_torrentNew( data
->ctor
, &err
) ) )
207 removeOldTorrent( data
);
208 data
->gtor
= tr_torrent_new_preexisting( torrent
);
212 gtr_add_torrent_error_dialog( GTK_WIDGET( b
), err
, data
->filename
);
215 updateTorrent( data
);
222 downloadDirChanged( GtkFileChooserButton
* b
, gpointer gdata
)
224 struct OpenData
* data
= gdata
;
225 char * fname
= gtk_file_chooser_get_filename( GTK_FILE_CHOOSER( b
) );
227 if( fname
&& ( !data
->downloadDir
|| !tr_is_same_file( fname
, data
->downloadDir
) ) )
229 g_free( data
->downloadDir
);
230 data
->downloadDir
= g_strdup( fname
);
231 updateTorrent( data
);
238 addTorrentFilters( GtkFileChooser
* chooser
)
240 GtkFileFilter
* filter
;
242 filter
= gtk_file_filter_new( );
243 gtk_file_filter_set_name( filter
, _( "Torrent files" ) );
244 gtk_file_filter_add_pattern( filter
, "*.torrent" );
245 gtk_file_chooser_add_filter( chooser
, filter
);
247 filter
= gtk_file_filter_new( );
248 gtk_file_filter_set_name( filter
, _( "All files" ) );
249 gtk_file_filter_add_pattern( filter
, "*" );
250 gtk_file_chooser_add_filter( chooser
, filter
);
258 gtr_torrent_options_dialog_new( GtkWindow
* parent
, TrCore
* core
, tr_ctor
* ctor
)
268 GtkWidget
* source_chooser
;
269 struct OpenData
* data
;
274 /* make the dialog */
275 d
= gtk_dialog_new_with_buttons( _( "Torrent Options" ), parent
,
276 GTK_DIALOG_DESTROY_WITH_PARENT
,
278 gtk_dialog_add_button( GTK_DIALOG( d
), GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
);
279 grab
= gtk_dialog_add_button( GTK_DIALOG( d
), GTK_STOCK_OPEN
, GTK_RESPONSE_ACCEPT
);
280 gtk_dialog_set_default_response( GTK_DIALOG( d
),
281 GTK_RESPONSE_ACCEPT
);
282 gtk_dialog_set_alternative_button_order( GTK_DIALOG( d
),
287 if( tr_ctorGetDownloadDir( ctor
, TR_FORCE
, &str
) )
288 g_assert_not_reached( );
291 data
= g_new0( struct OpenData
, 1 );
294 data
->filename
= g_strdup( tr_ctorGetSourceFile( ctor
) );
295 data
->downloadDir
= g_strdup( str
);
296 data
->file_list
= gtr_file_list_new( core
, 0 );
297 str
= _( "Mo_ve .torrent file to the trash" );
298 data
->trash_check
= gtk_check_button_new_with_mnemonic( str
);
299 str
= _( "_Start when added" );
300 data
->run_check
= gtk_check_button_new_with_mnemonic( str
);
302 w
= data
->priority_combo
= gtr_priority_combo_new( );
303 gtr_priority_combo_set_value( GTK_COMBO_BOX( w
), TR_PRI_NORMAL
);
305 g_signal_connect( G_OBJECT( d
), "response",
306 G_CALLBACK( addResponseCB
), data
);
308 t
= gtk_table_new( 6, 2, FALSE
);
309 gtk_container_set_border_width( GTK_CONTAINER( t
), GUI_PAD_BIG
);
310 gtk_table_set_row_spacings( GTK_TABLE( t
), GUI_PAD
);
311 gtk_table_set_col_spacings( GTK_TABLE( t
), GUI_PAD_BIG
);
314 l
= gtk_label_new_with_mnemonic( _( "_Torrent file:" ) );
315 gtk_misc_set_alignment( GTK_MISC( l
), 0.0f
, 0.5f
);
316 gtk_table_attach( GTK_TABLE( t
), l
, col
, col
+ 1, row
, row
+ 1, GTK_FILL
, 0, 0, 0 );
318 w
= gtk_file_chooser_button_new( _( "Select Source File" ),
319 GTK_FILE_CHOOSER_ACTION_OPEN
);
321 gtk_table_attach( GTK_TABLE( t
), w
, col
, col
+ 1, row
, row
+ 1, ~0, 0, 0, 0 );
322 gtk_label_set_mnemonic_widget( GTK_LABEL( l
), w
);
323 addTorrentFilters( GTK_FILE_CHOOSER( w
) );
324 g_signal_connect( w
, "selection-changed",
325 G_CALLBACK( sourceChanged
), data
);
329 l
= gtk_label_new_with_mnemonic( _( "_Destination folder:" ) );
330 gtk_misc_set_alignment( GTK_MISC( l
), 0.0f
, 0.5f
);
331 gtk_table_attach( GTK_TABLE( t
), l
, col
, col
+ 1, row
, row
+ 1, GTK_FILL
, 0, 0, 0 );
333 w
= gtk_file_chooser_button_new( _( "Select Destination Folder" ),
334 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
);
335 if( !gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER( w
),
336 data
->downloadDir
) )
337 g_warning( "couldn't select '%s'", data
->downloadDir
);
338 list
= get_recent_destinations( );
339 for( walk
= list
; walk
; walk
= walk
->next
)
340 gtk_file_chooser_add_shortcut_folder( GTK_FILE_CHOOSER( w
), walk
->data
, NULL
);
341 g_slist_free( list
);
342 gtk_table_attach( GTK_TABLE( t
), w
, col
, col
+ 1, row
, row
+ 1, ~0, 0, 0, 0 );
343 gtk_label_set_mnemonic_widget( GTK_LABEL( l
), w
);
344 g_signal_connect( w
, "selection-changed",
345 G_CALLBACK( downloadDirChanged
), data
);
350 gtk_widget_set_size_request ( w
, 466u, 300u );
351 gtk_table_attach_defaults( GTK_TABLE( t
), w
, col
, col
+ 2, row
, row
+ 1 );
355 w
= gtk_label_new_with_mnemonic( _( "Torrent _priority:" ) );
356 gtk_misc_set_alignment( GTK_MISC( w
), 0.0f
, 0.5f
);
357 gtk_table_attach( GTK_TABLE( t
), w
, col
, col
+ 1, row
, row
+ 1, ~0, 0, 0, 0 );
359 gtk_table_attach( GTK_TABLE( t
), data
->priority_combo
, col
, col
+ 1, row
, row
+ 1, ~0, 0, 0, 0 );
360 gtk_label_set_mnemonic_widget( GTK_LABEL( w
), data
->priority_combo
);
365 if( tr_ctorGetPaused( ctor
, TR_FORCE
, &flag
) )
366 g_assert_not_reached( );
367 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( w
), !flag
);
368 gtk_table_attach( GTK_TABLE( t
), w
, col
, col
+ 2, row
, row
+ 1, GTK_FILL
, 0, 0, 0 );
372 w
= data
->trash_check
;
373 if( tr_ctorGetDeleteSource( ctor
, &flag
) )
374 g_assert_not_reached( );
375 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( w
), flag
);
376 gtk_table_attach( GTK_TABLE( t
), w
, col
, col
+ 2, row
, row
+ 1, GTK_FILL
, 0, 0, 0 );
378 /* trigger sourceChanged, either directly or indirectly,
379 * so that it creates the tor/gtor objects */
382 gtk_file_chooser_set_filename( GTK_FILE_CHOOSER( w
), data
->filename
);
384 sourceChanged( GTK_FILE_CHOOSER_BUTTON( w
), data
);
386 gtr_dialog_set_content( GTK_DIALOG( d
), t
);
387 gtk_widget_grab_focus( grab
);
396 onOpenDialogResponse( GtkDialog
* dialog
, int response
, gpointer core
)
400 /* remember this folder the next time we use this dialog */
401 folder
= gtk_file_chooser_get_current_folder( GTK_FILE_CHOOSER( dialog
) );
402 gtr_pref_string_set( PREF_KEY_OPEN_DIALOG_FOLDER
, folder
);
405 if( response
== GTK_RESPONSE_ACCEPT
)
407 GtkFileChooser
* chooser
= GTK_FILE_CHOOSER( dialog
);
408 GtkWidget
* w
= gtk_file_chooser_get_extra_widget( chooser
);
409 GtkToggleButton
* tb
= GTK_TOGGLE_BUTTON( w
);
410 const gboolean doStart
= gtr_pref_flag_get( TR_PREFS_KEY_START
);
411 const gboolean doPrompt
= gtk_toggle_button_get_active( tb
);
412 const gboolean doNotify
= FALSE
;
413 GSList
* l
= gtk_file_chooser_get_filenames( chooser
);
415 tr_core_add_list( core
, l
, doStart
, doPrompt
, doNotify
);
418 gtk_widget_destroy( GTK_WIDGET( dialog
) );
422 gtr_torrent_open_from_file_dialog_new( GtkWindow
* parent
, TrCore
* core
)
428 w
= gtk_file_chooser_dialog_new( _( "Open a Torrent" ), parent
,
429 GTK_FILE_CHOOSER_ACTION_OPEN
,
430 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
431 GTK_STOCK_OPEN
, GTK_RESPONSE_ACCEPT
,
433 gtk_dialog_set_alternative_button_order( GTK_DIALOG( w
),
437 gtk_file_chooser_set_select_multiple( GTK_FILE_CHOOSER( w
), TRUE
);
438 addTorrentFilters( GTK_FILE_CHOOSER( w
) );
439 g_signal_connect( w
, "response", G_CALLBACK( onOpenDialogResponse
), core
);
441 if( ( folder
= gtr_pref_string_get( PREF_KEY_OPEN_DIALOG_FOLDER
) ) )
442 gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER( w
), folder
);
444 c
= gtk_check_button_new_with_mnemonic( _( "Show _options dialog" ) );
445 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( c
),
446 gtr_pref_flag_get( PREF_KEY_OPTIONS_PROMPT
) );
447 gtk_file_chooser_set_extra_widget( GTK_FILE_CHOOSER( w
), c
);
448 gtk_widget_show( c
);
458 onOpenURLResponse( GtkDialog
* dialog
, int response
, gpointer user_data
)
460 gboolean destroy
= TRUE
;
462 if( response
== GTK_RESPONSE_ACCEPT
)
464 GtkWidget
* e
= GTK_WIDGET( g_object_get_data( G_OBJECT( dialog
), "url-entry" ) );
465 char * url
= g_strdup( gtk_entry_get_text( GTK_ENTRY( e
) ) );
470 TrCore
* core
= user_data
;
472 if( gtr_is_supported_url( url
) || gtr_is_magnet_link( url
)
473 || gtr_is_hex_hashcode( url
) )
475 tr_core_add_from_url( core
, url
);
479 gtr_unrecognized_url_dialog( GTK_WIDGET( dialog
), url
);
488 gtk_widget_destroy( GTK_WIDGET( dialog
) );
492 gtr_torrent_open_from_url_dialog_new( GtkWindow
* parent
, TrCore
* core
)
499 w
= gtk_dialog_new_with_buttons( _( "Open URL" ), parent
,
500 GTK_DIALOG_DESTROY_WITH_PARENT
,
501 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
502 GTK_STOCK_OPEN
, GTK_RESPONSE_ACCEPT
,
504 gtk_dialog_set_alternative_button_order( GTK_DIALOG( w
),
508 g_signal_connect( w
, "response", G_CALLBACK( onOpenURLResponse
), core
);
511 t
= hig_workarea_create( );
512 hig_workarea_add_section_title( t
, &row
, _( "Open torrent from URL" ) );
513 e
= gtk_entry_new( );
514 gtk_widget_set_size_request( e
, 400, -1 );
515 gtr_paste_clipboard_url_into_entry( e
);
516 g_object_set_data( G_OBJECT( w
), "url-entry", e
);
517 hig_workarea_add_row( t
, &row
, _( "_URL" ), e
, NULL
);
519 gtr_dialog_set_content( GTK_DIALOG( w
), t
);