source: trunk/gtk/open-dialog.c @ 12409

Last change on this file since 12409 was 12409, checked in by jordan, 11 years ago

(trunk gtk) #4218 "open url should set focus on the open button by default" -- done.

  • Property svn:keywords set to Date Rev Author Id
File size: 16.5 KB
Line 
1/*
2 * This file Copyright (C) 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: open-dialog.c 12409 2011-05-01 04:14:50Z jordan $
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 "conf.h"
22#include "file-list.h"
23#include "hig.h"
24#include "open-dialog.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 = gtr_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 gtr_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        gtr_pref_string_set( key, l->data );
77    }
78    gtr_pref_save( gtr_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 OpenData
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    tr_torrent * tor;
99    tr_ctor *    ctor;
100};
101
102static void
103removeOldTorrent( struct OpenData * o )
104{
105    if( o->tor )
106    {
107        gtr_file_list_clear( o->file_list );
108        tr_torrentRemove( o->tor, FALSE, NULL );
109        o->tor = NULL;
110    }
111}
112
113static void
114addResponseCB( GtkDialog * dialog,
115               gint        response,
116               gpointer    gdata )
117{
118    struct OpenData * o = gdata;
119
120    if( o->tor )
121    {
122        if( response != GTK_RESPONSE_ACCEPT )
123        {
124            removeOldTorrent( o );
125        }
126        else
127        {
128            tr_torrentSetPriority( o->tor, gtr_priority_combo_get_value( GTK_COMBO_BOX( o->priority_combo ) ) );
129
130            if( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( o->run_check ) ) )
131                tr_torrentStart( o->tor );
132
133            gtr_core_add_torrent( o->core, o->tor, FALSE );
134
135            if( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( o->trash_check ) ) )
136                gtr_file_trash_or_remove( o->filename );
137
138            save_recent_destination( o->core, o->downloadDir );
139        }
140    }
141
142    tr_ctorFree( o->ctor );
143    g_free( o->filename );
144    g_free( o->downloadDir );
145    g_free( o );
146    gtk_widget_destroy( GTK_WIDGET( dialog ) );
147}
148
149static void
150updateTorrent( struct OpenData * o )
151{
152    const gboolean isLocalFile = tr_ctorGetSourceFile( o->ctor ) != NULL;
153    gtk_widget_set_sensitive( o->trash_check, isLocalFile );
154
155    if( !o->tor )
156    {
157        gtr_file_list_clear( o->file_list );
158        gtk_widget_set_sensitive( o->file_list, FALSE );
159    }
160    else
161    {
162        tr_torrentSetDownloadDir( o->tor, o->downloadDir );
163        gtk_widget_set_sensitive( o->file_list, tr_torrentHasMetadata( o->tor ) );
164        gtr_file_list_set_torrent( o->file_list, tr_torrentId( o->tor ) );
165        tr_torrentVerify( o->tor );
166    }
167}
168
169/**
170 * When the source .torrent file is deleted
171 * (such as, if it was a temp file that a web browser passed to us),
172 * gtk invokes this callback and `filename' will be NULL.
173 * The `filename' tests here are to prevent us from losing the current
174 * metadata when that happens.
175 */
176static void
177sourceChanged( GtkFileChooserButton * b, gpointer gdata )
178{
179    struct OpenData * o = gdata;
180    char * filename = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER( b ) );
181
182    /* maybe instantiate a torrent */
183    if( filename || !o->tor )
184    {
185        int err = 0;
186        int new_file = 0;
187        tr_torrent * torrent;
188
189        if( filename && ( !o->filename || !tr_is_same_file( filename, o->filename ) ) )
190        {
191            g_free( o->filename );
192            o->filename = g_strdup( filename );
193            tr_ctorSetMetainfoFromFile( o->ctor, o->filename );
194            new_file = 1;
195        }
196
197        tr_ctorSetDownloadDir( o->ctor, TR_FORCE, o->downloadDir );
198        tr_ctorSetPaused( o->ctor, TR_FORCE, TRUE );
199        tr_ctorSetDeleteSource( o->ctor, FALSE );
200
201        if(( torrent = tr_torrentNew( o->ctor, &err ) ) )
202        {
203            removeOldTorrent( o );
204            o->tor = torrent;
205        }
206        else if( new_file )
207        {
208            gtr_add_torrent_error_dialog( GTK_WIDGET( b ), err, o->filename );
209        }
210
211        updateTorrent( o );
212    }
213
214    g_free( filename );
215}
216
217static void
218downloadDirChanged( GtkFileChooserButton * b, gpointer gdata )
219{
220    struct OpenData * data = gdata;
221    char * fname = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER( b ) );
222
223    if( fname && ( !data->downloadDir || !tr_is_same_file( fname, data->downloadDir ) ) )
224    {
225        g_free( data->downloadDir );
226        data->downloadDir = g_strdup( fname );
227        updateTorrent( data );
228    }
229
230    g_free( fname );
231}
232
233static void
234addTorrentFilters( GtkFileChooser * chooser )
235{
236    GtkFileFilter * filter;
237
238    filter = gtk_file_filter_new( );
239    gtk_file_filter_set_name( filter, _( "Torrent files" ) );
240    gtk_file_filter_add_pattern( filter, "*.torrent" );
241    gtk_file_chooser_add_filter( chooser, filter );
242
243    filter = gtk_file_filter_new( );
244    gtk_file_filter_set_name( filter, _( "All files" ) );
245    gtk_file_filter_add_pattern( filter, "*" );
246    gtk_file_chooser_add_filter( chooser, filter );
247}
248
249/****
250*****
251****/
252
253GtkWidget*
254gtr_torrent_options_dialog_new( GtkWindow * parent, TrCore * core, tr_ctor * ctor )
255{
256    int              row;
257    int              col;
258    const char *     str;
259    GtkWidget *      w;
260    GtkWidget *      d;
261    GtkWidget *      t;
262    GtkWidget *      l;
263    GtkWidget *      grab;
264    GtkWidget *      source_chooser;
265    struct OpenData * data;
266    bool             flag;
267    GSList *         list;
268    GSList *         walk;
269
270    /* make the dialog */
271    d = gtk_dialog_new_with_buttons( _( "Torrent Options" ), parent,
272                                     GTK_DIALOG_DESTROY_WITH_PARENT,
273                                     NULL );
274    gtk_dialog_add_button( GTK_DIALOG( d ), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL );
275    grab = gtk_dialog_add_button( GTK_DIALOG( d ), GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT );
276    gtk_dialog_set_default_response( GTK_DIALOG( d ),
277                                     GTK_RESPONSE_ACCEPT );
278    gtk_dialog_set_alternative_button_order( GTK_DIALOG( d ),
279                                             GTK_RESPONSE_ACCEPT,
280                                             GTK_RESPONSE_CANCEL,
281                                             -1 );
282
283    if( tr_ctorGetDownloadDir( ctor, TR_FORCE, &str ) )
284        g_assert_not_reached( );
285    g_assert( str );
286
287    data = g_new0( struct OpenData, 1 );
288    data->core = core;
289    data->ctor = ctor;
290    data->filename = g_strdup( tr_ctorGetSourceFile( ctor ) );
291    data->downloadDir = g_strdup( str );
292    data->file_list = gtr_file_list_new( core, 0 );
293    str = _( "Mo_ve .torrent file to the trash" );
294    data->trash_check = gtk_check_button_new_with_mnemonic( str );
295    str = _( "_Start when added" );
296    data->run_check = gtk_check_button_new_with_mnemonic( str );
297
298    w = data->priority_combo = gtr_priority_combo_new( );
299    gtr_priority_combo_set_value( GTK_COMBO_BOX( w ), TR_PRI_NORMAL );
300
301    g_signal_connect( G_OBJECT( d ), "response",
302                      G_CALLBACK( addResponseCB ), data );
303
304    t = gtk_table_new( 6, 2, FALSE );
305    gtk_container_set_border_width( GTK_CONTAINER( t ), GUI_PAD_BIG );
306    gtk_table_set_row_spacings( GTK_TABLE( t ), GUI_PAD );
307    gtk_table_set_col_spacings( GTK_TABLE( t ), GUI_PAD_BIG );
308
309    row = col = 0;
310    l = gtk_label_new_with_mnemonic( _( "_Torrent file:" ) );
311    gtk_misc_set_alignment( GTK_MISC( l ), 0.0f, 0.5f );
312    gtk_table_attach( GTK_TABLE( t ), l, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0 );
313    ++col;
314    w = gtk_file_chooser_button_new( _( "Select Source File" ),
315                                     GTK_FILE_CHOOSER_ACTION_OPEN );
316    source_chooser = w;
317    gtk_table_attach( GTK_TABLE( t ), w, col, col + 1, row, row + 1, ~0, 0, 0, 0 );
318    gtk_label_set_mnemonic_widget( GTK_LABEL( l ), w );
319    addTorrentFilters( GTK_FILE_CHOOSER( w ) );
320    g_signal_connect( w, "selection-changed",
321                      G_CALLBACK( sourceChanged ), data );
322
323    ++row;
324    col = 0;
325    l = gtk_label_new_with_mnemonic( _( "_Destination folder:" ) );
326    gtk_misc_set_alignment( GTK_MISC( l ), 0.0f, 0.5f );
327    gtk_table_attach( GTK_TABLE( t ), l, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0 );
328    ++col;
329    w = gtk_file_chooser_button_new( _( "Select Destination Folder" ),
330                                     GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER );
331    if( !gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER( w ),
332                                              data->downloadDir ) )
333        g_warning( "couldn't select '%s'", data->downloadDir );
334    list = get_recent_destinations( );
335    for( walk = list; walk; walk = walk->next )
336        gtk_file_chooser_add_shortcut_folder( GTK_FILE_CHOOSER( w ), walk->data, NULL );
337    g_slist_free( list );
338    gtk_table_attach( GTK_TABLE( t ), w, col, col + 1, row, row + 1, ~0, 0, 0, 0 );
339    gtk_label_set_mnemonic_widget( GTK_LABEL( l ), w );
340    g_signal_connect( w, "selection-changed",
341                      G_CALLBACK( downloadDirChanged ), data );
342
343    ++row;
344    col = 0;
345    w = data->file_list;
346    gtk_widget_set_size_request ( w, 466u, 300u );
347    gtk_table_attach_defaults( GTK_TABLE( t ), w, col, col + 2, row, row + 1 );
348
349    ++row;
350    col = 0;
351    w = gtk_label_new_with_mnemonic( _( "Torrent _priority:" ) );
352    gtk_misc_set_alignment( GTK_MISC( w ), 0.0f, 0.5f );
353    gtk_table_attach( GTK_TABLE( t ), w, col, col + 1, row, row + 1, ~0, 0, 0, 0 );
354    ++col;
355    gtk_table_attach( GTK_TABLE( t ), data->priority_combo, col, col + 1, row, row + 1, ~0, 0, 0, 0 );
356    gtk_label_set_mnemonic_widget( GTK_LABEL( w ), data->priority_combo );
357
358    ++row;
359    col = 0;
360    w = data->run_check;
361    if( tr_ctorGetPaused( ctor, TR_FORCE, &flag ) )
362        g_assert_not_reached( );
363    gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( w ), !flag );
364    gtk_table_attach( GTK_TABLE( t ), w, col, col + 2, row, row + 1, GTK_FILL, 0, 0, 0 );
365
366    ++row;
367    col = 0;
368    w = data->trash_check;
369    if( tr_ctorGetDeleteSource( ctor, &flag ) )
370        g_assert_not_reached( );
371    gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( w ), flag );
372    gtk_table_attach( GTK_TABLE( t ), w, col, col + 2, row, row + 1, GTK_FILL, 0, 0, 0 );
373
374    /* trigger sourceChanged, either directly or indirectly,
375     * so that it creates the tor/gtor objects */
376    w = source_chooser;
377    if( data->filename )
378        gtk_file_chooser_set_filename( GTK_FILE_CHOOSER( w ), data->filename );
379    else
380        sourceChanged( GTK_FILE_CHOOSER_BUTTON( w ), data );
381
382    gtr_dialog_set_content( GTK_DIALOG( d ), t );
383    gtk_widget_grab_focus( grab );
384    return d;
385}
386
387/****
388*****
389****/
390
391static void
392onOpenDialogResponse( GtkDialog * dialog, int response, gpointer core )
393{
394    char * folder;
395
396    /* remember this folder the next time we use this dialog */
397    folder = gtk_file_chooser_get_current_folder( GTK_FILE_CHOOSER( dialog ) );
398    gtr_pref_string_set( PREF_KEY_OPEN_DIALOG_FOLDER, folder );
399    g_free( folder );
400
401    if( response == GTK_RESPONSE_ACCEPT )
402    {
403        GtkFileChooser  * chooser = GTK_FILE_CHOOSER( dialog );
404        GtkWidget       * w = gtk_file_chooser_get_extra_widget( chooser );
405        GtkToggleButton * tb = GTK_TOGGLE_BUTTON( w );
406        const gboolean    doStart = gtr_pref_flag_get( TR_PREFS_KEY_START );
407        const gboolean    doPrompt = gtk_toggle_button_get_active( tb );
408        const gboolean    doNotify = FALSE;
409        GSList * l = gtk_file_chooser_get_filenames( chooser );
410
411        gtr_core_add_list( core, l, doStart, doPrompt, doNotify );
412    }
413
414    gtk_widget_destroy( GTK_WIDGET( dialog ) );
415}
416
417GtkWidget*
418gtr_torrent_open_from_file_dialog_new( GtkWindow * parent, TrCore * core )
419{
420    GtkWidget *  w;
421    GtkWidget *  c;
422    const char * folder;
423
424    w = gtk_file_chooser_dialog_new( _( "Open a Torrent" ), parent,
425                                     GTK_FILE_CHOOSER_ACTION_OPEN,
426                                     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
427                                     GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
428                                     NULL );
429    gtk_dialog_set_alternative_button_order( GTK_DIALOG( w ),
430                                             GTK_RESPONSE_ACCEPT,
431                                             GTK_RESPONSE_CANCEL,
432                                             -1 );
433    gtk_file_chooser_set_select_multiple( GTK_FILE_CHOOSER( w ), TRUE );
434    addTorrentFilters( GTK_FILE_CHOOSER( w ) );
435    g_signal_connect( w, "response", G_CALLBACK( onOpenDialogResponse ), core );
436
437    if( ( folder = gtr_pref_string_get( PREF_KEY_OPEN_DIALOG_FOLDER ) ) )
438        gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER( w ), folder );
439
440    c = gtk_check_button_new_with_mnemonic( _( "Show _options dialog" ) );
441    gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( c ),
442                                 gtr_pref_flag_get( PREF_KEY_OPTIONS_PROMPT ) );
443    gtk_file_chooser_set_extra_widget( GTK_FILE_CHOOSER( w ), c );
444    gtk_widget_show( c );
445
446    return w;
447}
448
449/***
450****
451***/
452
453static void
454onOpenURLResponse( GtkDialog * dialog, int response, gpointer user_data )
455{
456    gboolean destroy = TRUE;
457
458    if( response == GTK_RESPONSE_ACCEPT )
459    {
460        GtkWidget * e = GTK_WIDGET( g_object_get_data( G_OBJECT( dialog ), "url-entry" ) );
461        char * url = g_strdup( gtk_entry_get_text( GTK_ENTRY( e ) ) );
462        g_strstrip( url );
463
464        if( url && *url )
465        {
466            TrCore * core = user_data;
467
468            if( gtr_is_supported_url( url ) || gtr_is_magnet_link( url )
469                                            || gtr_is_hex_hashcode( url ) )
470            {
471                gtr_core_add_from_url( core, url );
472            }
473            else
474            {
475                gtr_unrecognized_url_dialog( GTK_WIDGET( dialog ), url );
476                destroy = FALSE;
477            }
478        }
479
480        g_free( url );
481    }
482
483    if( destroy )
484        gtk_widget_destroy( GTK_WIDGET( dialog ) );
485}
486
487GtkWidget*
488gtr_torrent_open_from_url_dialog_new( GtkWindow * parent, TrCore * core )
489{
490    int row;
491    GtkWidget * e;
492    GtkWidget * t;
493    GtkWidget * w;
494
495    w = gtk_dialog_new_with_buttons( _( "Open URL" ), parent,
496                                     GTK_DIALOG_DESTROY_WITH_PARENT,
497                                     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
498                                     GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
499                                     NULL );
500    gtk_dialog_set_alternative_button_order( GTK_DIALOG( w ),
501                                             GTK_RESPONSE_ACCEPT,
502                                             GTK_RESPONSE_CANCEL,
503                                             -1 );
504    g_signal_connect( w, "response", G_CALLBACK( onOpenURLResponse ), core );
505
506    row = 0;
507    t = hig_workarea_create( );
508    hig_workarea_add_section_title( t, &row, _( "Open torrent from URL" ) );
509    e = gtk_entry_new( );
510    gtk_widget_set_size_request( e, 400, -1 );
511    gtr_paste_clipboard_url_into_entry( e );
512    g_object_set_data( G_OBJECT( w ), "url-entry", e );
513    hig_workarea_add_row( t, &row, _( "_URL" ), e, NULL );
514
515    gtr_dialog_set_content( GTK_DIALOG( w ), t );
516
517#ifdef GTK_CHECK_VERSION(2,20,0)
518    if( gtk_entry_get_text_length( GTK_ENTRY( e ) ) == 0 )
519        gtk_widget_grab_focus( e );
520    else
521        gtk_widget_grab_focus( gtk_dialog_get_widget_for_response( GTK_DIALOG( w ), GTK_RESPONSE_ACCEPT ) );
522#endif
523
524    return w;
525}
Note: See TracBrowser for help on using the repository browser.