source: trunk/gtk/makemeta-ui.c @ 10066

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

(trunk gtk) make it easier to change the periodic refresh intervals at the code level

  • Property svn:keywords set to Date Rev Author Id
File size: 16.1 KB
Line 
1/*
2 * This file Copyright (C) 2007-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: makemeta-ui.c 10066 2010-02-01 04:54:10Z charles $
11 */
12
13#include <string.h>
14
15#include <glib/gi18n.h>
16#include <gtk/gtk.h>
17
18#include <libtransmission/transmission.h>
19#include <libtransmission/makemeta.h>
20
21#include "conf.h"
22#include "hig.h"
23#include "makemeta-ui.h"
24#include "tr-core.h"
25#include "tr-prefs.h"
26#include "util.h"
27
28#define FILE_CHOSEN_KEY "file-is-chosen"
29
30typedef struct
31{
32    char * target;
33    guint progress_tag;
34    GtkWidget * file_chooser;
35    GtkWidget * folder_chooser;
36    GtkWidget * pieces_lb;
37    GtkWidget * destination_chooser;
38    GtkWidget * comment_check;
39    GtkWidget * comment_entry;
40    GtkWidget * private_check;
41    GtkWidget * progress_label;
42    GtkWidget * progress_bar;
43    GtkWidget * progress_dialog;
44    GtkWidget * dialog;
45    GtkTextBuffer * announce_text_buffer;
46    TrCore * core;
47    tr_metainfo_builder *  builder;
48}
49MakeMetaUI;
50
51static void
52freeMetaUI( gpointer p )
53{
54    MakeMetaUI * ui = p;
55
56    tr_metaInfoBuilderFree( ui->builder );
57    g_free( ui->target );
58    memset( ui, ~0, sizeof( MakeMetaUI ) );
59    g_free( ui );
60}
61
62static gboolean
63onProgressDialogRefresh( gpointer data )
64{
65    char * str = NULL;
66    MakeMetaUI * ui = data;
67    const tr_metainfo_builder * b = ui->builder;
68    GtkDialog * d = GTK_DIALOG( ui->progress_dialog );
69    GtkProgressBar * p = GTK_PROGRESS_BAR( ui->progress_bar );
70    const double fraction = (double)b->pieceIndex / b->pieceCount;
71        char * base = g_path_get_basename( b->top );
72
73    /* progress label */
74    if( !b->isDone )
75        str = g_strdup_printf( _( "Creating \"%s\"" ), base );
76    else if( b->result == TR_MAKEMETA_OK )
77        str = g_strdup_printf( _( "Created \"%s\"!" ), base );
78    else if( b->result == TR_MAKEMETA_URL )
79        str = g_strdup_printf( _( "Error: invalid announce URL \"%s\"" ), b->errfile );
80    else if( b->result == TR_MAKEMETA_CANCELLED )
81        str = g_strdup_printf( _( "Cancelled" ) );
82    else if( b->result == TR_MAKEMETA_IO_READ )
83        str = g_strdup_printf( _( "Error reading \"%s\": %s" ), b->errfile, g_strerror( b->my_errno ) );
84    else if( b->result == TR_MAKEMETA_IO_WRITE )
85        str = g_strdup_printf( _( "Error writing \"%s\": %s" ), b->errfile, g_strerror( b->my_errno ) );
86    else
87        g_assert_not_reached( );
88
89    if( str != NULL ) {
90        gtk_label_set_text( GTK_LABEL( ui->progress_label ), str );
91        g_free( str );
92    }
93
94    /* progress bar */
95    if( !b->pieceIndex )
96        str = g_strdup( "" );
97    else {
98        char sizebuf[128];
99        tr_strlsize( sizebuf, (uint64_t)b->pieceIndex *
100                              (uint64_t)b->pieceSize, sizeof( sizebuf ) );
101        /* how much data we've scanned through to generate checksums */
102        str = g_strdup_printf( _( "Scanned %s" ), sizebuf );
103    }
104    gtk_progress_bar_set_fraction( p, fraction );
105    gtk_progress_bar_set_text( p, str );
106    g_free( str );
107
108    /* buttons */
109    gtk_dialog_set_response_sensitive( d, GTK_RESPONSE_CANCEL, !b->isDone );
110    gtk_dialog_set_response_sensitive( d, GTK_RESPONSE_CLOSE, b->isDone );
111    gtk_dialog_set_response_sensitive( d, GTK_RESPONSE_ACCEPT, b->isDone && !b->result );
112
113    g_free( base );
114    return TRUE;
115}
116
117static void
118onProgressDialogDestroyed( gpointer data, GObject * dead UNUSED )
119{
120    MakeMetaUI * ui = data;
121    g_source_remove( ui->progress_tag );
122}
123
124static void
125addTorrent( MakeMetaUI * ui )
126{
127    char * path;
128    const tr_metainfo_builder * b = ui->builder;
129    tr_ctor * ctor = tr_ctorNew( tr_core_session( ui->core ) );
130
131    tr_ctorSetMetainfoFromFile( ctor, ui->target );
132
133    path = g_path_get_dirname( b->top );
134    tr_ctorSetDownloadDir( ctor, TR_FORCE, path );
135    g_free( path );
136
137    tr_core_add_ctor( ui->core, ctor );
138}
139
140static void
141onProgressDialogResponse( GtkDialog * d, int response, gpointer data )
142{
143    MakeMetaUI * ui = data;
144
145    switch( response )
146    {
147        case GTK_RESPONSE_CANCEL:
148            ui->builder->abortFlag = TRUE;
149            gtk_widget_destroy( GTK_WIDGET( d ) );
150            break;
151        case GTK_RESPONSE_ACCEPT:
152            addTorrent( ui );
153            /* fall-through */
154        case GTK_RESPONSE_CLOSE:
155            gtk_widget_destroy( ui->builder->result ? GTK_WIDGET( d ) : ui->dialog );
156            break;
157        default:
158            g_assert( 0 && "unhandled response" );
159    }
160}
161
162static void
163makeProgressDialog( GtkWidget * parent, MakeMetaUI * ui )
164{
165    GtkWidget *d, *l, *w, *v, *fr;
166
167    d = gtk_dialog_new_with_buttons( _( "New Torrent" ),
168            GTK_WINDOW( parent ),
169            GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
170            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
171            GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
172            GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT,
173            NULL );
174    ui->progress_dialog = d;
175    g_signal_connect( d, "response", G_CALLBACK( onProgressDialogResponse ), ui );
176
177    fr = gtk_frame_new( NULL );
178    gtk_container_set_border_width( GTK_CONTAINER( fr ), GUI_PAD_BIG );
179    gtk_frame_set_shadow_type( GTK_FRAME( fr ), GTK_SHADOW_NONE );
180    v = gtk_vbox_new( TRUE, GUI_PAD );
181    gtk_container_add( GTK_CONTAINER( fr ), v );
182
183    l = gtk_label_new( _( "Creating torrent..." ) );
184    gtk_misc_set_alignment( GTK_MISC( l ), 0.0, 0.5 );
185    gtk_label_set_justify( GTK_LABEL( l ), GTK_JUSTIFY_LEFT );
186    ui->progress_label = l;
187    gtk_box_pack_start( GTK_BOX( v ), l, FALSE, FALSE, 0 );
188
189    w = gtk_progress_bar_new( );
190    ui->progress_bar = w;
191    gtk_box_pack_start( GTK_BOX( v ), w, FALSE, FALSE, 0 );
192
193    ui->progress_tag = gtr_timeout_add_seconds( SECONDARY_WINDOW_REFRESH_INTERVAL_SECONDS, onProgressDialogRefresh, ui );
194    g_object_weak_ref( G_OBJECT( d ), onProgressDialogDestroyed, ui );
195    onProgressDialogRefresh( ui );
196
197    gtk_box_pack_start( GTK_BOX( GTK_DIALOG( d )->vbox ), fr, TRUE, TRUE, 0 );
198    gtk_widget_show_all( d );
199}
200
201static void
202onResponse( GtkDialog* d, int response, gpointer user_data )
203{
204    MakeMetaUI * ui = user_data;
205
206    if( response == GTK_RESPONSE_ACCEPT )
207    {
208        if( ui->builder != NULL )
209        {
210            int i;
211            int n;
212            int tier;
213            GtkTextIter start, end;
214            char * dir;
215            char * base;
216            char * tracker_text;
217            char ** tracker_strings;
218            GtkEntry * c_entry = GTK_ENTRY( ui->comment_entry );
219            GtkToggleButton * p_check = GTK_TOGGLE_BUTTON( ui->private_check );
220            GtkToggleButton * c_check = GTK_TOGGLE_BUTTON( ui->comment_check );
221            const char * comment = gtk_entry_get_text( c_entry );
222            const gboolean isPrivate = gtk_toggle_button_get_active( p_check );
223            const gboolean useComment = gtk_toggle_button_get_active( c_check );
224            tr_tracker_info * trackers;
225
226            /* destination file */
227            dir = gtk_file_chooser_get_filename(
228                      GTK_FILE_CHOOSER( ui->destination_chooser ) );
229            base = g_path_get_basename( ui->builder->top );
230            g_free( ui->target );
231            ui->target = g_strdup_printf( "%s/%s.torrent", dir, base );
232
233            /* build the array of trackers */
234            gtk_text_buffer_get_bounds( ui->announce_text_buffer, &start, &end );
235            tracker_text = gtk_text_buffer_get_text( ui->announce_text_buffer,
236                                                     &start, &end, FALSE );
237            tracker_strings = g_strsplit( tracker_text, "\n", 0 );
238            for( i=0; tracker_strings[i]; )
239                ++i;
240            trackers = g_new0( tr_tracker_info, i );
241            for( i=n=tier=0; tracker_strings[i]; ++i ) {
242                const char * str = tracker_strings[i];
243                if( !*str )
244                    ++tier;
245                else {
246                    trackers[n].tier = tier;
247                    trackers[n].announce = tracker_strings[i];
248                    ++n;
249                }
250            }
251
252            /* build the .torrent */
253            makeProgressDialog( GTK_WIDGET( d ), ui );
254            tr_makeMetaInfo( ui->builder, ui->target, trackers, n,
255                             useComment ? comment : NULL, isPrivate );
256
257            /* cleanup */
258            g_free( trackers );
259            g_strfreev( tracker_strings );
260            g_free( tracker_text );
261            g_free( base );
262            g_free( dir );
263        }
264    }
265    else if( response == GTK_RESPONSE_CLOSE )
266    {
267        gtk_widget_destroy( GTK_WIDGET( d ) );
268    }
269}
270
271/***
272****
273***/
274
275static void
276onSourceToggled( GtkToggleButton * tb, gpointer user_data )
277{
278    gtk_widget_set_sensitive( GTK_WIDGET( user_data ),
279                              gtk_toggle_button_get_active( tb ) );
280}
281
282static void
283updatePiecesLabel( MakeMetaUI * ui )
284{
285    const tr_metainfo_builder * builder = ui->builder;
286    const char * filename = builder ? builder->top : NULL;
287    GString * gstr = g_string_new( NULL );
288
289    g_string_append( gstr, "<i>" );
290    if( !filename )
291    {
292        g_string_append( gstr, _( "No source selected" ) );
293    }
294    else
295    {
296        char buf[128];
297        tr_strlsize( buf, builder->totalSize, sizeof( buf ) );
298        g_string_append_printf( gstr, ngettext( "%1$s; %2$'d File",
299                                                "%1$s; %2$'d Files",
300                                                builder->fileCount ),
301                                buf, builder->fileCount );
302        g_string_append( gstr, "; " );
303        tr_strlsize( buf, builder->pieceSize, sizeof( buf ) );
304        g_string_append_printf( gstr, ngettext( "%1$'d Piece @ %2$s",
305                                                "%1$'d Pieces @ %2$s",
306                                                builder->pieceCount ),
307                                      builder->pieceCount, buf );
308    }
309    g_string_append( gstr, "</i>" );
310    gtk_label_set_markup ( GTK_LABEL( ui->pieces_lb ), gstr->str );
311    g_string_free( gstr, TRUE );
312}
313
314static void
315setFilename( MakeMetaUI * ui, const char * filename )
316{
317    if( ui->builder ) {
318        tr_metaInfoBuilderFree( ui->builder );
319        ui->builder = NULL;
320    }
321
322    if( filename )
323        ui->builder = tr_metaInfoBuilderCreate( filename );
324
325    updatePiecesLabel( ui );
326}
327
328static void
329onChooserChosen( GtkFileChooser * chooser, gpointer user_data )
330{
331    char * filename;
332    MakeMetaUI * ui = user_data;
333
334    g_object_set_data( G_OBJECT( chooser ), FILE_CHOSEN_KEY,
335                       GINT_TO_POINTER( TRUE ) );
336
337    filename = gtk_file_chooser_get_filename( chooser );
338    setFilename( ui, filename );
339    g_free( filename );
340}
341
342static void
343onSourceToggled2( GtkToggleButton * tb, GtkWidget * chooser, MakeMetaUI * ui )
344{
345    if( gtk_toggle_button_get_active( tb ) )
346    {
347        if( g_object_get_data( G_OBJECT( chooser ), FILE_CHOSEN_KEY ) != NULL )
348            onChooserChosen( GTK_FILE_CHOOSER( chooser ), ui );
349        else
350            setFilename( ui, NULL );
351    }
352}
353static void
354onFolderToggled( GtkToggleButton * tb, gpointer data )
355{
356    MakeMetaUI * ui = data;
357    onSourceToggled2( tb, ui->folder_chooser, ui );
358}
359static void
360onFileToggled( GtkToggleButton * tb, gpointer data )
361{
362    MakeMetaUI * ui = data;
363    onSourceToggled2( tb, ui->file_chooser, ui );
364}
365
366static const char *
367getDefaultSavePath( void )
368{
369    const char * path;
370#if GLIB_CHECK_VERSION( 2,14,0 )
371    path = g_get_user_special_dir( G_USER_DIRECTORY_DESKTOP );
372#else
373    path = g_get_home_dir( );
374#endif
375    return path;
376}
377
378GtkWidget*
379make_meta_ui( GtkWindow  * parent, TrCore * core )
380{
381    int row = 0;
382    const char * str;
383    GtkWidget * d, *t, *w, *l, *fr, *sw, *v;
384    GSList * slist;
385    MakeMetaUI * ui = g_new0 ( MakeMetaUI, 1 );
386
387    ui->core = core;
388
389    d = gtk_dialog_new_with_buttons( _( "New Torrent" ),
390                                     parent,
391                                     GTK_DIALOG_DESTROY_WITH_PARENT |
392                                     GTK_DIALOG_NO_SEPARATOR,
393                                     GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
394                                     GTK_STOCK_NEW, GTK_RESPONSE_ACCEPT,
395                                     NULL );
396    ui->dialog = d;
397    g_signal_connect( d, "response", G_CALLBACK( onResponse ), ui );
398    g_object_set_data_full( G_OBJECT( d ), "ui", ui, freeMetaUI );
399
400    t = hig_workarea_create ( );
401
402    hig_workarea_add_section_title ( t, &row, _( "Files" ) );
403
404        str = _( "Sa_ve to:" );
405        w = gtk_file_chooser_button_new( NULL, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER );
406        gtk_file_chooser_set_filename( GTK_FILE_CHOOSER( w ), getDefaultSavePath( ) );
407        ui->destination_chooser = w;
408        hig_workarea_add_row( t, &row, str, w, NULL );
409
410        l = gtk_radio_button_new_with_mnemonic( NULL, _( "Source F_older:" ) );
411        gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( l ), FALSE );
412        w = gtk_file_chooser_button_new( NULL, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER );
413        g_signal_connect( l, "toggled", G_CALLBACK( onFolderToggled ), ui );
414        g_signal_connect( l, "toggled", G_CALLBACK( onSourceToggled ), w );
415        g_signal_connect( w, "selection-changed", G_CALLBACK( onChooserChosen ), ui );
416        ui->folder_chooser = w;
417        gtk_widget_set_sensitive( GTK_WIDGET( w ), FALSE );
418        hig_workarea_add_row_w( t, &row, l, w, NULL );
419
420        slist = gtk_radio_button_get_group( GTK_RADIO_BUTTON( l ) ),
421        l = gtk_radio_button_new_with_mnemonic( slist, _( "Source _File:" ) );
422        gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( l ), TRUE );
423        w = gtk_file_chooser_button_new( NULL, GTK_FILE_CHOOSER_ACTION_OPEN );
424        g_signal_connect( l, "toggled", G_CALLBACK( onFileToggled ), ui );
425        g_signal_connect( l, "toggled", G_CALLBACK( onSourceToggled ), w );
426        g_signal_connect( w, "selection-changed", G_CALLBACK( onChooserChosen ), ui );
427        ui->file_chooser = w;
428        hig_workarea_add_row_w( t, &row, l, w, NULL );
429
430        w = gtk_label_new( NULL );
431        ui->pieces_lb = w;
432        gtk_label_set_markup( GTK_LABEL( w ), _( "<i>No source selected</i>" ) );
433        hig_workarea_add_row( t, &row, NULL, w, NULL );
434
435    hig_workarea_add_section_divider( t, &row );
436    hig_workarea_add_section_title ( t, &row, _( "Properties" ) );
437
438        str = _( "_Trackers:" );
439        v = gtk_vbox_new( FALSE, GUI_PAD_SMALL );
440        ui->announce_text_buffer = gtk_text_buffer_new( NULL );
441        w = gtk_text_view_new_with_buffer( ui->announce_text_buffer );
442        gtk_widget_set_size_request( w, -1, 80 );
443        sw = gtk_scrolled_window_new( NULL, NULL );
444        gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( sw ),
445                                        GTK_POLICY_AUTOMATIC,
446                                        GTK_POLICY_AUTOMATIC );
447        gtk_container_add( GTK_CONTAINER( sw ), w );
448        fr = gtk_frame_new( NULL );
449        gtk_frame_set_shadow_type( GTK_FRAME( fr ), GTK_SHADOW_IN );
450        gtk_container_add( GTK_CONTAINER( fr ), sw );
451        gtk_box_pack_start( GTK_BOX( v ), fr, TRUE, TRUE, 0 );
452        l = gtk_label_new( NULL );
453        gtk_label_set_markup( GTK_LABEL( l ), _( "To add a backup URL, add it on the line after the primary URL.\n"
454                                                 "To add another primary URL, add it after a blank line." ) );
455        gtk_label_set_justify( GTK_LABEL( l ), GTK_JUSTIFY_LEFT );
456        gtk_misc_set_alignment( GTK_MISC( l ), 0.0, 0.5 );
457        gtk_box_pack_start( GTK_BOX( v ), l, FALSE, FALSE, 0 );
458        hig_workarea_add_tall_row( t, &row, str, v, NULL );
459
460        l = gtk_check_button_new_with_mnemonic( _( "Co_mment:" ) );
461        ui->comment_check = l;
462        gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( l ), FALSE );
463        w = gtk_entry_new( );
464        ui->comment_entry = w;
465        gtk_widget_set_sensitive( GTK_WIDGET( w ), FALSE );
466        g_signal_connect( l, "toggled", G_CALLBACK( onSourceToggled ), w );
467        hig_workarea_add_row_w( t, &row, l, w, NULL );
468
469        w = hig_workarea_add_wide_checkbutton( t, &row, _( "_Private torrent" ), FALSE );
470        ui->private_check = w;
471
472    hig_workarea_finish( t, &row );
473    gtk_box_pack_start( GTK_BOX( GTK_DIALOG( d )->vbox ), t, TRUE, TRUE, 0 );
474
475    return d;
476}
Note: See TracBrowser for help on using the repository browser.