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

Last change on this file since 12656 was 12656, checked in by jordan, 10 years ago

(trunk gtk) first cut at using GApplication. This lets glib replace hundreds of lines of homegrown code. Whee!

  • Property svn:keywords set to Date Rev Author Id
File size: 16.3 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 12656 2011-08-09 02:30:31Z 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#include "util.h" /* gtr_priority_combo_get_value() */
27
28/****
29*****
30****/
31
32#define N_RECENT 4
33
34static GSList*
35get_recent_destinations( void )
36{
37    int i;
38    GSList * list = NULL;
39
40    for( i=0; i<N_RECENT; ++i )
41    {
42        char key[64];
43        const char * val;
44        g_snprintf( key, sizeof( key ), "recent-download-dir-%d", i+1 );
45        if(( val = gtr_pref_string_get( key )))
46            list = g_slist_append( list, (void*)val );
47    }
48    return list;
49}
50
51static void
52save_recent_destination( TrCore * core, const char * dir  )
53{
54    int i;
55    GSList * l;
56    GSList * list = get_recent_destinations( );
57
58    if( dir == NULL )
59        return;
60
61    /* if it was already in the list, remove it */
62    if(( l = g_slist_find_custom( list, dir, (GCompareFunc)strcmp )))
63        list = g_slist_delete_link( list, l );
64
65    /* add it to the front of the list */
66    list = g_slist_prepend( list, (void*)dir );
67
68    /* make local copies of the strings that aren't
69     * invalidated by gtr_pref_string_set() */
70    for( l=list; l; l=l->next )
71        l->data = g_strdup( l->data );
72
73    /* save the first N_RECENT directories */
74    for( l=list, i=0; l && ( i<N_RECENT ); ++i, l=l->next ) {
75        char key[64];
76        g_snprintf( key, sizeof( key ), "recent-download-dir-%d", i + 1 );
77        gtr_pref_string_set( key, l->data );
78    }
79    gtr_pref_save( gtr_core_session( core ) );
80
81    /* cleanup */
82    g_slist_foreach( list, (GFunc)g_free, NULL );
83    g_slist_free( list );
84}
85
86/****
87*****
88****/
89
90struct OpenData
91{
92    TrCore *     core;
93    GtkWidget *  file_list;
94    GtkWidget *  run_check;
95    GtkWidget *  trash_check;
96    GtkWidget *  priority_combo;
97    char *       filename;
98    char *       downloadDir;
99    tr_torrent * tor;
100    tr_ctor *    ctor;
101};
102
103static void
104removeOldTorrent( struct OpenData * o )
105{
106    if( o->tor )
107    {
108        gtr_file_list_clear( o->file_list );
109        tr_torrentRemove( o->tor, FALSE, NULL );
110        o->tor = NULL;
111    }
112}
113
114static void
115addResponseCB( GtkDialog * dialog,
116               gint        response,
117               gpointer    gdata )
118{
119    struct OpenData * o = gdata;
120
121    if( o->tor )
122    {
123        if( response != GTK_RESPONSE_ACCEPT )
124        {
125            removeOldTorrent( o );
126        }
127        else
128        {
129            tr_torrentSetPriority( o->tor, gtr_priority_combo_get_value( GTK_COMBO_BOX( o->priority_combo ) ) );
130
131            if( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( o->run_check ) ) )
132                tr_torrentStart( o->tor );
133
134            gtr_core_add_torrent( o->core, o->tor, FALSE );
135
136            if( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( o->trash_check ) ) )
137                gtr_file_trash_or_remove( o->filename );
138
139            save_recent_destination( o->core, o->downloadDir );
140        }
141    }
142
143    tr_ctorFree( o->ctor );
144    g_free( o->filename );
145    g_free( o->downloadDir );
146    g_free( o );
147    gtk_widget_destroy( GTK_WIDGET( dialog ) );
148}
149
150static void
151updateTorrent( struct OpenData * o )
152{
153    const gboolean isLocalFile = tr_ctorGetSourceFile( o->ctor ) != NULL;
154    gtk_widget_set_sensitive( o->trash_check, isLocalFile );
155
156    if( !o->tor )
157    {
158        gtr_file_list_clear( o->file_list );
159        gtk_widget_set_sensitive( o->file_list, FALSE );
160    }
161    else
162    {
163        tr_torrentSetDownloadDir( o->tor, o->downloadDir );
164        gtk_widget_set_sensitive( o->file_list, tr_torrentHasMetadata( o->tor ) );
165        gtr_file_list_set_torrent( o->file_list, tr_torrentId( o->tor ) );
166        tr_torrentVerify( o->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 OpenData * o = gdata;
181    char * filename = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER( b ) );
182
183    /* maybe instantiate a torrent */
184    if( filename || !o->tor )
185    {
186        int err = 0;
187        int new_file = 0;
188        tr_torrent * torrent;
189
190        if( filename && ( !o->filename || !tr_is_same_file( filename, o->filename ) ) )
191        {
192            g_free( o->filename );
193            o->filename = g_strdup( filename );
194            tr_ctorSetMetainfoFromFile( o->ctor, o->filename );
195            new_file = 1;
196        }
197
198        tr_ctorSetDownloadDir( o->ctor, TR_FORCE, o->downloadDir );
199        tr_ctorSetPaused( o->ctor, TR_FORCE, TRUE );
200        tr_ctorSetDeleteSource( o->ctor, FALSE );
201
202        if(( torrent = tr_torrentNew( o->ctor, &err ) ) )
203        {
204            removeOldTorrent( o );
205            o->tor = torrent;
206        }
207        else if( new_file )
208        {
209            gtr_add_torrent_error_dialog( GTK_WIDGET( b ), err, o->filename );
210        }
211
212        updateTorrent( o );
213    }
214
215    g_free( filename );
216}
217
218static void
219downloadDirChanged( GtkFileChooserButton * b, gpointer gdata )
220{
221    struct OpenData * data = gdata;
222    char * fname = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER( b ) );
223
224    if( fname && ( !data->downloadDir || !tr_is_same_file( 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*
255gtr_torrent_options_dialog_new( 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 OpenData * data;
267    bool             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                                     NULL );
275    gtk_dialog_add_button( GTK_DIALOG( d ), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL );
276    grab = gtk_dialog_add_button( GTK_DIALOG( d ), GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT );
277    gtk_dialog_set_default_response( GTK_DIALOG( d ),
278                                     GTK_RESPONSE_ACCEPT );
279    gtk_dialog_set_alternative_button_order( GTK_DIALOG( d ),
280                                             GTK_RESPONSE_ACCEPT,
281                                             GTK_RESPONSE_CANCEL,
282                                             -1 );
283
284    if( tr_ctorGetDownloadDir( ctor, TR_FORCE, &str ) )
285        g_assert_not_reached( );
286    g_assert( str );
287
288    data = g_new0( struct OpenData, 1 );
289    data->core = core;
290    data->ctor = ctor;
291    data->filename = g_strdup( tr_ctorGetSourceFile( ctor ) );
292    data->downloadDir = g_strdup( str );
293    data->file_list = gtr_file_list_new( core, 0 );
294    str = _( "Mo_ve .torrent file to the trash" );
295    data->trash_check = gtk_check_button_new_with_mnemonic( str );
296    str = _( "_Start when added" );
297    data->run_check = gtk_check_button_new_with_mnemonic( str );
298
299    w = data->priority_combo = gtr_priority_combo_new( );
300    gtr_priority_combo_set_value( GTK_COMBO_BOX( w ), TR_PRI_NORMAL );
301
302    g_signal_connect( G_OBJECT( d ), "response",
303                      G_CALLBACK( addResponseCB ), data );
304
305    t = gtk_table_new( 6, 2, FALSE );
306    gtk_container_set_border_width( GTK_CONTAINER( t ), GUI_PAD_BIG );
307    gtk_table_set_row_spacings( GTK_TABLE( t ), GUI_PAD );
308    gtk_table_set_col_spacings( GTK_TABLE( t ), GUI_PAD_BIG );
309
310    row = col = 0;
311    l = gtk_label_new_with_mnemonic( _( "_Torrent file:" ) );
312    gtk_misc_set_alignment( GTK_MISC( l ), 0.0f, 0.5f );
313    gtk_table_attach( GTK_TABLE( t ), l, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0 );
314    ++col;
315    w = gtk_file_chooser_button_new( _( "Select Source File" ),
316                                     GTK_FILE_CHOOSER_ACTION_OPEN );
317    source_chooser = w;
318    gtk_table_attach( GTK_TABLE( t ), w, col, col + 1, row, row + 1, ~0, 0, 0, 0 );
319    gtk_label_set_mnemonic_widget( GTK_LABEL( l ), w );
320    addTorrentFilters( GTK_FILE_CHOOSER( w ) );
321    g_signal_connect( w, "selection-changed",
322                      G_CALLBACK( sourceChanged ), data );
323
324    ++row;
325    col = 0;
326    l = gtk_label_new_with_mnemonic( _( "_Destination folder:" ) );
327    gtk_misc_set_alignment( GTK_MISC( l ), 0.0f, 0.5f );
328    gtk_table_attach( GTK_TABLE( t ), l, col, col + 1, row, row + 1, GTK_FILL, 0, 0, 0 );
329    ++col;
330    w = gtk_file_chooser_button_new( _( "Select Destination Folder" ),
331                                     GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER );
332    if( !gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER( w ),
333                                              data->downloadDir ) )
334        g_warning( "couldn't select '%s'", data->downloadDir );
335    list = get_recent_destinations( );
336    for( walk = list; walk; walk = walk->next )
337        gtk_file_chooser_add_shortcut_folder( GTK_FILE_CHOOSER( w ), walk->data, NULL );
338    g_slist_free( list );
339    gtk_table_attach( GTK_TABLE( t ), w, col, col + 1, row, row + 1, ~0, 0, 0, 0 );
340    gtk_label_set_mnemonic_widget( GTK_LABEL( l ), w );
341    g_signal_connect( w, "selection-changed",
342                      G_CALLBACK( downloadDirChanged ), data );
343
344    ++row;
345    col = 0;
346    w = data->file_list;
347    gtk_widget_set_size_request ( w, 466u, 300u );
348    gtk_table_attach_defaults( GTK_TABLE( t ), w, col, col + 2, row, row + 1 );
349
350    ++row;
351    col = 0;
352    w = gtk_label_new_with_mnemonic( _( "Torrent _priority:" ) );
353    gtk_misc_set_alignment( GTK_MISC( w ), 0.0f, 0.5f );
354    gtk_table_attach( GTK_TABLE( t ), w, col, col + 1, row, row + 1, ~0, 0, 0, 0 );
355    ++col;
356    gtk_table_attach( GTK_TABLE( t ), data->priority_combo, col, col + 1, row, row + 1, ~0, 0, 0, 0 );
357    gtk_label_set_mnemonic_widget( GTK_LABEL( w ), data->priority_combo );
358
359    ++row;
360    col = 0;
361    w = data->run_check;
362    if( tr_ctorGetPaused( ctor, TR_FORCE, &flag ) )
363        g_assert_not_reached( );
364    gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( w ), !flag );
365    gtk_table_attach( GTK_TABLE( t ), w, col, col + 2, row, row + 1, GTK_FILL, 0, 0, 0 );
366
367    ++row;
368    col = 0;
369    w = data->trash_check;
370    if( tr_ctorGetDeleteSource( ctor, &flag ) )
371        g_assert_not_reached( );
372    gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( w ), flag );
373    gtk_table_attach( GTK_TABLE( t ), w, col, col + 2, row, row + 1, GTK_FILL, 0, 0, 0 );
374
375    /* trigger sourceChanged, either directly or indirectly,
376     * so that it creates the tor/gtor objects */
377    w = source_chooser;
378    if( data->filename )
379        gtk_file_chooser_set_filename( GTK_FILE_CHOOSER( w ), data->filename );
380    else
381        sourceChanged( GTK_FILE_CHOOSER_BUTTON( w ), data );
382
383    gtr_dialog_set_content( GTK_DIALOG( d ), t );
384    gtk_widget_grab_focus( grab );
385    return d;
386}
387
388/****
389*****
390****/
391
392static void
393onOpenDialogResponse( GtkDialog * dialog, int response, gpointer core )
394{
395    char * folder;
396
397    /* remember this folder the next time we use this dialog */
398    folder = gtk_file_chooser_get_current_folder( GTK_FILE_CHOOSER( dialog ) );
399    gtr_pref_string_set( PREF_KEY_OPEN_DIALOG_FOLDER, folder );
400    g_free( folder );
401
402    if( response == GTK_RESPONSE_ACCEPT )
403    {
404        GtkFileChooser  * chooser = GTK_FILE_CHOOSER( dialog );
405        GtkWidget       * w = gtk_file_chooser_get_extra_widget( chooser );
406        GtkToggleButton * tb = GTK_TOGGLE_BUTTON( w );
407        const gboolean    do_start = gtr_pref_flag_get( TR_PREFS_KEY_START );
408        const gboolean    do_prompt = gtk_toggle_button_get_active( tb );
409        const gboolean    do_notify = FALSE;
410        GSList * files = gtk_file_chooser_get_files( chooser );
411
412        gtr_core_add_files( core, files, do_start, do_prompt, do_notify );
413        g_slist_foreach( files, (GFunc)g_object_unref, NULL );
414        g_slist_free( files );
415    }
416
417    gtk_widget_destroy( GTK_WIDGET( dialog ) );
418}
419
420GtkWidget*
421gtr_torrent_open_from_file_dialog_new( GtkWindow * parent, TrCore * core )
422{
423    GtkWidget *  w;
424    GtkWidget *  c;
425    const char * folder;
426
427    w = gtk_file_chooser_dialog_new( _( "Open a Torrent" ), parent,
428                                     GTK_FILE_CHOOSER_ACTION_OPEN,
429                                     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
430                                     GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
431                                     NULL );
432    gtk_dialog_set_alternative_button_order( GTK_DIALOG( w ),
433                                             GTK_RESPONSE_ACCEPT,
434                                             GTK_RESPONSE_CANCEL,
435                                             -1 );
436    gtk_file_chooser_set_select_multiple( GTK_FILE_CHOOSER( w ), TRUE );
437    addTorrentFilters( GTK_FILE_CHOOSER( w ) );
438    g_signal_connect( w, "response", G_CALLBACK( onOpenDialogResponse ), core );
439
440    if( ( folder = gtr_pref_string_get( PREF_KEY_OPEN_DIALOG_FOLDER ) ) )
441        gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER( w ), folder );
442
443    c = gtk_check_button_new_with_mnemonic( _( "Show _options dialog" ) );
444    gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( c ),
445                                 gtr_pref_flag_get( PREF_KEY_OPTIONS_PROMPT ) );
446    gtk_file_chooser_set_extra_widget( GTK_FILE_CHOOSER( w ), c );
447    gtk_widget_show( c );
448
449    return w;
450}
451
452/***
453****
454***/
455
456static void
457onOpenURLResponse( GtkDialog * dialog, int response, gpointer user_data )
458{
459    bool handled = false;
460
461    if( response == GTK_RESPONSE_ACCEPT )
462    {
463        GtkWidget * e = GTK_WIDGET( g_object_get_data( G_OBJECT( dialog ), "url-entry" ) );
464        char * url = g_strdup( gtk_entry_get_text( GTK_ENTRY( e ) ) );
465        g_strstrip( url );
466
467        if( url ) {
468            handled = gtr_core_add_from_url( user_data, url );
469            if( !handled )
470                gtr_unrecognized_url_dialog( GTK_WIDGET( dialog ), url );
471            g_free( url );
472        }
473    }
474
475    if( handled )
476        gtk_widget_destroy( GTK_WIDGET( dialog ) );
477}
478
479GtkWidget*
480gtr_torrent_open_from_url_dialog_new( GtkWindow * parent, TrCore * core )
481{
482    int row;
483    GtkWidget * e;
484    GtkWidget * t;
485    GtkWidget * w;
486
487    w = gtk_dialog_new_with_buttons( _( "Open URL" ), parent,
488                                     GTK_DIALOG_DESTROY_WITH_PARENT,
489                                     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
490                                     GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
491                                     NULL );
492    gtk_dialog_set_alternative_button_order( GTK_DIALOG( w ),
493                                             GTK_RESPONSE_ACCEPT,
494                                             GTK_RESPONSE_CANCEL,
495                                             -1 );
496    g_signal_connect( w, "response", G_CALLBACK( onOpenURLResponse ), core );
497
498    row = 0;
499    t = hig_workarea_create( );
500    hig_workarea_add_section_title( t, &row, _( "Open torrent from URL" ) );
501    e = gtk_entry_new( );
502    gtk_widget_set_size_request( e, 400, -1 );
503    gtr_paste_clipboard_url_into_entry( e );
504    g_object_set_data( G_OBJECT( w ), "url-entry", e );
505    hig_workarea_add_row( t, &row, _( "_URL" ), e, NULL );
506
507    gtr_dialog_set_content( GTK_DIALOG( w ), t );
508
509    if( gtk_entry_get_text_length( GTK_ENTRY( e ) ) == 0 )
510        gtk_widget_grab_focus( e );
511    else
512        gtk_widget_grab_focus( gtk_dialog_get_widget_for_response( GTK_DIALOG( w ), GTK_RESPONSE_ACCEPT ) );
513
514    return w;
515}
Note: See TracBrowser for help on using the repository browser.