source: trunk/gtk/add-dialog.c @ 10644

Last change on this file since 10644 was 10644, checked in by charles, 12 years ago

(trunk gtk) #2612 "Display new transfer window when adding magnet transfer" -- implemented in GTK+ for 2.00

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