source: branches/2.0x/gtk/add-dialog.c @ 11575

Last change on this file since 11575 was 11575, checked in by charles, 11 years ago

(2.0x) backport r11574 from #3844 "Error popup when adding a relative path"

  • Property svn:keywords set to Date Rev Author Id
File size: 17.4 KB
Line 
1/*
2 * This file Copyright (C) 2008-2010 Mnemosyne LLC
3 *
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.
9 *
10 * $Id: add-dialog.c 11575 2010-12-22 07:06:00Z charles $
11 */
12
13#include <glib/gi18n.h>
14#include <gtk/gtk.h>
15
16#include <string.h>
17
18#include <libtransmission/transmission.h>
19#include <libtransmission/utils.h> /* tr_is_same_file() */
20
21#include "add-dialog.h"
22#include "conf.h"
23#include "file-list.h"
24#include "hig.h"
25#include "tr-prefs.h"
26
27/****
28*****
29****/
30
31#define N_RECENT 4
32
33static GSList*
34get_recent_destinations( void )
35{
36    int i;
37    GSList * list = NULL;
38
39    for( i=0; i<N_RECENT; ++i )
40    {
41        char key[64];
42        const char * val;
43        g_snprintf( key, sizeof( key ), "recent-download-dir-%d", i+1 );
44        if(( val = pref_string_get( key )))
45            list = g_slist_append( list, (void*)val );
46    }
47    return list;
48}
49
50static void
51save_recent_destination( TrCore * core, const char * dir  )
52{
53    int i;
54    GSList * l;
55    GSList * list = get_recent_destinations( );
56
57    if( dir == NULL )
58        return;
59
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 );
63
64    /* add it to the front of the list */
65    list = g_slist_prepend( list, (void*)dir );
66
67    /* make local copies of the strings that aren't
68     * invalidated by pref_string_set() */
69    for( l=list; l; l=l->next )
70        l->data = g_strdup( l->data );
71
72    /* save the first N_RECENT directories */
73    for( l=list, i=0; l && ( i<N_RECENT ); ++i, l=l->next ) {
74        char key[64];
75        g_snprintf( key, sizeof( key ), "recent-download-dir-%d", i + 1 );
76        pref_string_set( key, l->data );
77    }
78    pref_save( tr_core_session( core ) );
79
80    /* cleanup */
81    g_slist_foreach( list, (GFunc)g_free, NULL );
82    g_slist_free( list );
83}
84
85/****
86*****
87****/
88
89struct AddData
90{
91    TrCore *     core;
92    GtkWidget *  file_list;
93    GtkWidget *  run_check;
94    GtkWidget *  trash_check;
95    GtkWidget *  priority_combo;
96    char *       filename;
97    char *       downloadDir;
98    TrTorrent *  gtor;
99    tr_ctor *    ctor;
100};
101
102static void
103removeOldTorrent( struct AddData * data )
104{
105    if( data->gtor )
106    {
107        file_list_clear( data->file_list );
108        tr_torrent_set_remove_flag( data->gtor, TRUE );
109        g_object_unref( G_OBJECT( data->gtor ) );
110        data->gtor = NULL;
111    }
112}
113
114static void
115addResponseCB( GtkDialog * dialog,
116               gint        response,
117               gpointer    gdata )
118{
119    struct AddData * data = gdata;
120
121    if( data->gtor )
122    {
123        if( response != GTK_RESPONSE_ACCEPT )
124        {
125            removeOldTorrent( data );
126        }
127        else
128        {
129            tr_torrent * tor = tr_torrent_handle( data->gtor );
130
131            tr_torrentSetPriority( tor, gtr_priority_combo_get_value( data->priority_combo ) );
132
133            if( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( data->run_check ) ) )
134                tr_torrentStart( tor );
135
136            tr_core_add_torrent( data->core, data->gtor, FALSE );
137
138            if( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( data->trash_check ) ) )
139                gtr_file_trash_or_remove( data->filename );
140
141            save_recent_destination( data->core, data->downloadDir );
142        }
143    }
144
145    tr_ctorFree( data->ctor );
146    g_free( data->filename );
147    g_free( data->downloadDir );
148    g_free( data );
149    gtk_widget_destroy( GTK_WIDGET( dialog ) );
150}
151
152static void
153updateTorrent( struct AddData * o )
154{
155    const gboolean isLocalFile = tr_ctorGetSourceFile( o->ctor ) != NULL;
156    gtk_widget_set_sensitive( o->trash_check, isLocalFile );
157
158    if( !o->gtor )
159    {
160        file_list_clear( o->file_list );
161        gtk_widget_set_sensitive( o->file_list, FALSE );
162    }
163    else
164    {
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        file_list_set_torrent( o->file_list, tr_torrentId( tor ) );
169        tr_torrentVerify( tor );
170    }
171}
172
173/**
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.
179 */
180static void
181sourceChanged( GtkFileChooserButton * b, gpointer gdata )
182{
183    struct AddData * data = gdata;
184    char * filename = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER( b ) );
185
186    /* maybe instantiate a torrent */
187    if( filename || !data->gtor )
188    {
189        int err = 0;
190        int new_file = 0;
191        tr_torrent * torrent;
192
193        if( filename && ( !data->filename || !tr_is_same_file( filename, data->filename ) ) )
194        {
195            g_free( data->filename );
196            data->filename = g_strdup( filename );
197            tr_ctorSetMetainfoFromFile( data->ctor, data->filename );
198            new_file = 1;
199        }
200
201        tr_ctorSetDownloadDir( data->ctor, TR_FORCE, data->downloadDir );
202        tr_ctorSetPaused( data->ctor, TR_FORCE, TRUE );
203        tr_ctorSetDeleteSource( data->ctor, FALSE );
204
205        if( ( torrent = tr_torrentNew( data->ctor, &err ) ) )
206        {
207            removeOldTorrent( data );
208            data->gtor = tr_torrent_new_preexisting( torrent );
209        }
210        else if( new_file )
211        {
212            addTorrentErrorDialog( GTK_WIDGET( b ), err, data->filename );
213        }
214
215        updateTorrent( data );
216    }
217
218    g_free( filename );
219}
220
221static void
222downloadDirChanged( GtkFileChooserButton * b, gpointer gdata )
223{
224    struct AddData * data = gdata;
225    char * fname = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER( b ) );
226
227    if( fname && ( !data->downloadDir || !tr_is_same_file( fname, data->downloadDir ) ) )
228    {
229        g_free( data->downloadDir );
230        data->downloadDir = g_strdup( fname );
231        updateTorrent( data );
232    }
233
234    g_free( fname );
235}
236
237static void
238addTorrentFilters( GtkFileChooser * chooser )
239{
240    GtkFileFilter * filter;
241
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 );
246
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 );
251}
252
253/****
254*****
255****/
256
257GtkWidget*
258addSingleTorrentDialog( GtkWindow * parent, TrCore * core, tr_ctor * ctor )
259{
260    int              row;
261    int              col;
262    const char *     str;
263    GtkWidget *      w;
264    GtkWidget *      d;
265    GtkWidget *      t;
266    GtkWidget *      l;
267    GtkWidget *      grab;
268    GtkWidget *      source_chooser;
269    struct AddData * data;
270    uint8_t          flag;
271    GSList *         list;
272    GSList *         walk;
273
274    /* make the dialog */
275    d = gtk_dialog_new_with_buttons( _( "Torrent Options" ), parent,
276                                     GTK_DIALOG_DESTROY_WITH_PARENT |
277                                     GTK_DIALOG_NO_SEPARATOR,
278                                     NULL );
279    gtk_dialog_add_button( GTK_DIALOG( d ), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL );
280    grab = gtk_dialog_add_button( GTK_DIALOG( d ), GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT );
281    gtk_dialog_set_default_response( GTK_DIALOG( d ),
282                                     GTK_RESPONSE_ACCEPT );
283    gtk_dialog_set_alternative_button_order( GTK_DIALOG( d ),
284                                             GTK_RESPONSE_ACCEPT,
285                                             GTK_RESPONSE_CANCEL,
286                                             -1 );
287
288    if( tr_ctorGetDownloadDir( ctor, TR_FORCE, &str ) )
289        g_assert_not_reached( );
290    g_assert( str );
291
292    data = g_new0( struct AddData, 1 );
293    data->core = core;
294    data->ctor = ctor;
295    data->filename = g_strdup( tr_ctorGetSourceFile( ctor ) );
296    data->downloadDir = g_strdup( str );
297    data->file_list = file_list_new( core, 0 );
298    str = _( "Mo_ve .torrent file to the trash" );
299    data->trash_check = gtk_check_button_new_with_mnemonic( str );
300    str = _( "_Start when added" );
301    data->run_check = gtk_check_button_new_with_mnemonic( str );
302
303    data->priority_combo = gtr_priority_combo_new( );
304    gtr_priority_combo_set_value( data->priority_combo, TR_PRI_NORMAL );
305
306    g_signal_connect( G_OBJECT( d ), "response",
307                      G_CALLBACK( addResponseCB ), data );
308
309    t = gtk_table_new( 6, 2, FALSE );
310    gtk_container_set_border_width( GTK_CONTAINER( t ), GUI_PAD_BIG );
311    gtk_table_set_row_spacings( GTK_TABLE( t ), GUI_PAD );
312    gtk_table_set_col_spacings( GTK_TABLE( t ), GUI_PAD_BIG );
313
314    row = col = 0;
315    l = gtk_label_new_with_mnemonic( _( "_Torrent file:" ) );
316    gtk_misc_set_alignment( GTK_MISC( l ), 0.0f, 0.5f );
317    gtk_table_attach( GTK_TABLE( t ), l, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0 );
318    ++col;
319    w = gtk_file_chooser_button_new( _( "Select Source File" ),
320                                     GTK_FILE_CHOOSER_ACTION_OPEN );
321    source_chooser = w;
322    gtk_table_attach( GTK_TABLE( t ), w, col, col + 1, row, row + 1, ~0, 0, 0, 0 );
323    gtk_label_set_mnemonic_widget( GTK_LABEL( l ), w );
324    addTorrentFilters( GTK_FILE_CHOOSER( w ) );
325    g_signal_connect( w, "selection-changed",
326                      G_CALLBACK( sourceChanged ), data );
327
328    ++row;
329    col = 0;
330    l = gtk_label_new_with_mnemonic( _( "_Destination folder:" ) );
331    gtk_misc_set_alignment( GTK_MISC( l ), 0.0f, 0.5f );
332    gtk_table_attach( GTK_TABLE( t ), l, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0 );
333    ++col;
334    w = gtk_file_chooser_button_new( _( "Select Destination Folder" ),
335                                     GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER );
336    if( !gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER( w ),
337                                              data->downloadDir ) )
338        g_warning( "couldn't select '%s'", data->downloadDir );
339    list = get_recent_destinations( );
340    for( walk = list; walk; walk = walk->next )
341        gtk_file_chooser_add_shortcut_folder( GTK_FILE_CHOOSER( w ), walk->data, NULL );
342    g_slist_free( list );
343    gtk_table_attach( GTK_TABLE( t ), w, col, col + 1, row, row + 1, ~0, 0, 0, 0 );
344    gtk_label_set_mnemonic_widget( GTK_LABEL( l ), w );
345    g_signal_connect( w, "selection-changed",
346                      G_CALLBACK( downloadDirChanged ), data );
347
348    ++row;
349    col = 0;
350    w = data->file_list;
351    gtk_widget_set_size_request ( w, 466u, 300u );
352    gtk_table_attach_defaults( GTK_TABLE( t ), w, col, col + 2, row, row + 1 );
353
354    ++row;
355    col = 0;
356    w = gtk_label_new_with_mnemonic( _( "Torrent _priority:" ) );
357    gtk_misc_set_alignment( GTK_MISC( w ), 0.0f, 0.5f );
358    gtk_table_attach( GTK_TABLE( t ), w, col, col + 1, row, row + 1, ~0, 0, 0, 0 );
359    ++col;
360    gtk_table_attach( GTK_TABLE( t ), data->priority_combo, col, col + 1, row, row + 1, ~0, 0, 0, 0 );
361    gtk_label_set_mnemonic_widget( GTK_LABEL( w ), data->priority_combo );
362
363    ++row;
364    col = 0;
365    w = data->run_check;
366    if( tr_ctorGetPaused( ctor, TR_FORCE, &flag ) )
367        g_assert_not_reached( );
368    gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( w ), !flag );
369    gtk_table_attach( GTK_TABLE( t ), w, col, col + 2, row, row + 1, GTK_FILL, 0, 0, 0 );
370
371    ++row;
372    col = 0;
373    w = data->trash_check;
374    if( tr_ctorGetDeleteSource( ctor, &flag ) )
375        g_assert_not_reached( );
376    gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( w ), flag );
377    gtk_table_attach( GTK_TABLE( t ), w, col, col + 2, row, row + 1, GTK_FILL, 0, 0, 0 );
378
379    ++row;
380    col = 0;
381    w = data->priority_combo;
382
383    /* trigger sourceChanged, either directly or indirectly,
384     * so that it creates the tor/gtor objects */
385    w = source_chooser;
386    if( data->filename )
387        gtk_file_chooser_set_filename( GTK_FILE_CHOOSER( w ), data->filename );
388    else
389        sourceChanged( GTK_FILE_CHOOSER_BUTTON( w ), data );
390
391    gtk_box_pack_start( GTK_BOX( GTK_DIALOG( d )->vbox ), t, TRUE, TRUE, 0 );
392
393    gtk_widget_grab_focus( grab );
394    gtk_widget_show_all( d );
395    return d;
396}
397
398/****
399*****
400****/
401
402static void
403onAddDialogResponse( GtkDialog * dialog,
404                     int         response,
405                     gpointer    core )
406{
407    char * folder;
408
409    /* remember this folder the next time we use this dialog */
410    folder = gtk_file_chooser_get_current_folder( GTK_FILE_CHOOSER( dialog ) );
411    pref_string_set( PREF_KEY_OPEN_DIALOG_FOLDER, folder );
412    g_free( folder );
413
414    if( response == GTK_RESPONSE_ACCEPT )
415    {
416        GtkFileChooser  * chooser = GTK_FILE_CHOOSER( dialog );
417        GtkWidget       * w = gtk_file_chooser_get_extra_widget( chooser );
418        GtkToggleButton * tb = GTK_TOGGLE_BUTTON( w );
419        const gboolean    showOptions = gtk_toggle_button_get_active( tb );
420        const pref_flag_t start = PREF_FLAG_DEFAULT;
421        const pref_flag_t prompt = showOptions
422                                 ? PREF_FLAG_TRUE
423                                 : PREF_FLAG_FALSE;
424        GSList * l = gtk_file_chooser_get_filenames( chooser );
425
426        tr_core_add_list( core, l, start, prompt, FALSE );
427    }
428
429    gtk_widget_destroy( GTK_WIDGET( dialog ) );
430}
431
432GtkWidget*
433addDialog( GtkWindow * parent,
434           TrCore *    core )
435{
436    GtkWidget *  w;
437    GtkWidget *  c;
438    const char * folder;
439
440    w = gtk_file_chooser_dialog_new( _( "Add a Torrent" ), parent,
441                                     GTK_FILE_CHOOSER_ACTION_OPEN,
442                                     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
443                                     GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT,
444                                     NULL );
445    gtk_dialog_set_alternative_button_order( GTK_DIALOG( w ),
446                                             GTK_RESPONSE_ACCEPT,
447                                             GTK_RESPONSE_CANCEL,
448                                             -1 );
449    gtk_file_chooser_set_select_multiple( GTK_FILE_CHOOSER( w ), TRUE );
450    addTorrentFilters( GTK_FILE_CHOOSER( w ) );
451    g_signal_connect( w, "response", G_CALLBACK(
452                          onAddDialogResponse ), core );
453
454    if( ( folder = pref_string_get( PREF_KEY_OPEN_DIALOG_FOLDER ) ) )
455        gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER( w ), folder );
456
457    c = gtk_check_button_new_with_mnemonic( _( "Show _options dialog" ) );
458    gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( c ),
459                                 pref_flag_get( PREF_KEY_OPTIONS_PROMPT ) );
460    gtk_file_chooser_set_extra_widget( GTK_FILE_CHOOSER( w ), c );
461    gtk_widget_show( c );
462
463    gtk_widget_show( w );
464    return w;
465}
466
467/***
468****
469***/
470
471static void
472onAddURLResponse( GtkDialog * dialog, int response, gpointer user_data )
473{
474    gboolean destroy = TRUE;
475
476    if( response == GTK_RESPONSE_ACCEPT )
477    {
478        GtkWidget * e = GTK_WIDGET( g_object_get_data( G_OBJECT( dialog ), "url-entry" ) );
479        char * url = g_strdup( gtk_entry_get_text( GTK_ENTRY( e ) ) );
480        g_strstrip( url );
481
482        if( url && *url )
483        {
484            TrCore * core = user_data;
485
486            if( gtr_is_supported_url( url ) || gtr_is_magnet_link( url )
487                                            || gtr_is_hex_hashcode( url ) )
488            {
489                tr_core_add_from_url( core, url );
490            }
491            else
492            {
493                gtr_unrecognized_url_dialog( GTK_WIDGET( dialog ), url );
494                destroy = FALSE;
495            }
496        }
497
498        g_free( url );
499    }
500
501    if( destroy )
502        gtk_widget_destroy( GTK_WIDGET( dialog ) );
503}
504
505static void
506paste_clipboard_url_into_entry( GtkWidget * e )
507{
508  size_t i;
509
510  char * text[] = {
511    gtk_clipboard_wait_for_text( gtk_clipboard_get( GDK_SELECTION_PRIMARY ) ),
512    gtk_clipboard_wait_for_text( gtk_clipboard_get( GDK_SELECTION_CLIPBOARD ) )
513  };
514
515  for( i=0; i<G_N_ELEMENTS(text); ++i ) {
516      char * s = text[i];
517      if( s && ( gtr_is_supported_url( s ) || gtr_is_magnet_link( s ) ) ) {
518          gtk_entry_set_text( GTK_ENTRY( e ), s );
519          break;
520      }
521  }
522
523  for( i=0; i<G_N_ELEMENTS(text); ++i )
524    g_free( text[i] );
525}
526
527GtkWidget*
528addURLDialog( GtkWindow * parent, TrCore * core )
529{
530    int row;
531    GtkWidget * e;
532    GtkWidget * t;
533    GtkWidget * w;
534
535    w = gtk_dialog_new_with_buttons( _( "Add URL" ), parent,
536                                     GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
537                                     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
538                                     GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT,
539                                     NULL );
540    gtk_dialog_set_alternative_button_order( GTK_DIALOG( w ),
541                                             GTK_RESPONSE_ACCEPT,
542                                             GTK_RESPONSE_CANCEL,
543                                             -1 );
544    g_signal_connect( w, "response", G_CALLBACK( onAddURLResponse ), core );
545
546    row = 0;
547    t = hig_workarea_create( );
548    hig_workarea_add_section_title( t, &row, _( "Add torrent from URL" ) );
549    e = gtk_entry_new( );
550    gtk_widget_set_size_request( e, 400, -1 );
551    paste_clipboard_url_into_entry( e );
552    g_object_set_data( G_OBJECT( w ), "url-entry", e );
553    hig_workarea_add_row( t, &row, _( "_URL" ), e, NULL );
554
555    gtk_box_pack_start( GTK_BOX( GTK_DIALOG( w )->vbox ), t, TRUE, TRUE, 0 );
556    gtk_widget_show_all( t );
557    gtk_widget_show( w );
558    return w;
559}
Note: See TracBrowser for help on using the repository browser.