source: trunk/gtk/dialogs.c @ 5221

Last change on this file since 5221 was 5221, checked in by charles, 14 years ago

lots more i18n string work -- making strings more consistent, folding redundant strings together, etc.

  • Property svn:keywords set to Date Rev Author Id
File size: 12.6 KB
Line 
1/******************************************************************************
2 * $Id: dialogs.c 5221 2008-03-07 20:48:36Z charles $
3 *
4 * Copyright (c) 2005-2008 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include <gtk/gtk.h>
26#include <glib/gi18n.h>
27
28#include <libtransmission/transmission.h>
29
30#include "conf.h"
31#include "dialogs.h"
32#include "hig.h"
33#include "tr-core.h"
34#include "tr-prefs.h"
35
36struct dirdata
37{
38    GtkWidget  * widget;
39    TrCore     * core;
40    GList      * files;
41    tr_ctor    * ctor;
42};
43
44static void
45promptdirnocore( gpointer gdata, GObject * core UNUSED )
46{
47    struct dirdata * stuff = gdata;
48
49    /* prevent the response callback from trying to remove the weak
50       reference which no longer exists */
51    stuff->core = NULL;
52
53    gtk_dialog_response( GTK_DIALOG( stuff->widget ), GTK_RESPONSE_NONE );
54}
55
56static void
57promptresp( GtkWidget * widget, gint resp, gpointer data )
58{
59    struct dirdata * stuff;
60
61    stuff = data;
62
63    if( GTK_RESPONSE_ACCEPT == resp )
64    {
65        char * dir;
66        GList * l;
67
68        /* update the destination */
69        dir = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER( widget ) );
70        tr_ctorSetDestination( stuff->ctor, TR_FORCE, dir );
71        g_free( dir );
72
73        /* if there's metainfo in the ctor already, use it */
74        if( !tr_ctorGetMetainfo( stuff->ctor, NULL ) )
75            tr_core_add_ctor( stuff->core, stuff->ctor );
76
77        /* if there's a list of files, use them too */
78        for( l=stuff->files; l!=NULL; l=l->next )
79            if( !tr_ctorSetMetainfoFromFile( stuff->ctor, l->data ) )
80                tr_core_add_ctor( stuff->core, stuff->ctor );
81    }
82
83    if( stuff->core )
84        g_object_weak_unref( G_OBJECT( stuff->core ), promptdirnocore, stuff );
85
86    gtk_widget_destroy( widget );
87    freestrlist( stuff->files );
88    tr_ctorFree( stuff->ctor );
89    g_free( stuff );
90}
91
92void
93fmtpeercount( GtkLabel * label, int count )
94{
95    char str[16];
96
97    if( 0 > count )
98    {
99        gtk_label_set_text( label, "?" );
100    }
101    else
102    {
103        g_snprintf( str, sizeof str, "%i", count );
104        gtk_label_set_text( label, str );
105    }
106}
107
108static void
109deleteToggled( GtkToggleButton * tb, gpointer ctor )
110{
111    tr_ctorSetDeleteSource( ctor, gtk_toggle_button_get_active( tb ) );
112}
113
114static void
115startToggled( GtkToggleButton * tb, gpointer ctor )
116{
117    tr_ctorSetPaused( ctor, TR_FORCE, !gtk_toggle_button_get_active( tb ) );
118}
119
120GtkWidget*
121promptfordir( GtkWindow * parent, TrCore * core, GList * files, tr_ctor * ctor )
122{
123    uint8_t          flag = 0;
124    const char     * str;
125    struct dirdata * stuff;
126    GtkWidget      * wind;
127    GtkWidget      * v;
128    GtkWidget      * w;
129
130    stuff = g_new0( struct dirdata, 1 );
131    stuff->core   = core;
132    stuff->ctor   = ctor;
133    stuff->files  = dupstrlist( files );
134
135    g_object_weak_ref( G_OBJECT( core ), promptdirnocore, stuff );
136
137    wind =  gtk_file_chooser_dialog_new( _("Destination directory"), parent,
138                                         GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
139                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
140                                         GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
141                                         NULL );
142    gtk_dialog_set_alternative_button_order( GTK_DIALOG( wind ),
143                                             GTK_RESPONSE_ACCEPT,
144                                             GTK_RESPONSE_CANCEL,
145                                             -1 );
146    gtk_file_chooser_set_local_only( GTK_FILE_CHOOSER( wind ), TRUE );
147    gtk_file_chooser_set_select_multiple( GTK_FILE_CHOOSER( wind ), FALSE );
148    if( tr_ctorGetDestination( ctor, TR_FORCE, &str ) )
149        g_assert_not_reached( );
150    if( !gtk_file_chooser_set_filename( GTK_FILE_CHOOSER( wind ), str ) )
151        g_warning( "couldn't set destination '%s'", str );
152
153    v = gtk_vbox_new( FALSE, GUI_PAD );
154
155        flag = 0;
156        w = gtk_check_button_new_with_mnemonic( _( "_Delete original torrent file" ) );
157        g_signal_connect( w, "toggled", G_CALLBACK( deleteToggled ), ctor );
158        gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( w ), flag );
159        gtk_box_pack_start( GTK_BOX( v ), w, FALSE, FALSE, 0 );
160
161        flag = 1;
162        w = gtk_check_button_new_with_mnemonic( _( "_Start when added" ) );
163        g_signal_connect( w, "toggled", G_CALLBACK( startToggled ), ctor );
164        if( tr_ctorGetPaused( ctor, TR_FORCE, &flag ) )
165            g_assert_not_reached( );
166        gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( w ), !flag );
167        gtk_box_pack_start( GTK_BOX( v ), w, FALSE, FALSE, 0 );
168
169    gtk_file_chooser_set_extra_widget( GTK_FILE_CHOOSER( wind ), v );
170
171    stuff->widget = wind;
172
173    g_signal_connect( G_OBJECT( wind ), "response",
174                      G_CALLBACK( promptresp ), stuff );
175
176    gtk_widget_show_all( wind );
177    return wind;
178}
179
180/***
181****
182***/
183
184struct quitdata
185{
186    TrCore          * core;
187    callbackfunc_t    func;
188    void            * cbdata;
189    GtkWidget       * dontask;
190};
191
192static void
193quitresp( GtkWidget * widget, int response, gpointer data )
194{
195    struct quitdata * stuff = data;
196    GtkToggleButton * tb = GTK_TOGGLE_BUTTON( stuff->dontask );
197
198    tr_core_set_pref_bool( stuff->core,
199                           PREF_KEY_ASKQUIT,
200                           !gtk_toggle_button_get_active( tb ) );
201
202    if( response == GTK_RESPONSE_ACCEPT )
203        stuff->func( stuff->cbdata );
204
205    g_free( stuff );
206    gtk_widget_destroy( widget );
207}
208
209static gboolean
210countActiveTorrents( GtkTreeModel  * model,
211                     GtkTreePath   * path UNUSED,
212                     GtkTreeIter   * iter,
213                     gpointer        activeTorrentCount )
214{
215    int status = -1;
216    gtk_tree_model_get( model, iter, MC_STATUS, &status, -1 );
217    if( status != TR_STATUS_STOPPED )
218        *(int*)activeTorrentCount += 1;
219    return FALSE; /* keep iterating */
220}
221
222void
223askquit( TrCore          * core,
224         GtkWindow       * parent,
225         callbackfunc_t    func,
226         void            * cbdata )
227{
228    struct quitdata * stuff;
229    GtkWidget * wind;
230    GtkWidget * dontask;
231    GtkTreeModel * model;
232    int activeTorrentCount;
233
234    /* if the user doesn't want to be asked, don't ask */
235    if( !pref_flag_get( PREF_KEY_ASKQUIT ) ) {
236        func( cbdata );
237        return;
238    }
239
240    /* if there aren't any active torrents, don't ask */
241    model = tr_core_model( core );
242    activeTorrentCount = 0;
243    gtk_tree_model_foreach( model, countActiveTorrents, &activeTorrentCount );
244    if( !activeTorrentCount ) {
245        func( cbdata );
246        return;
247    }
248
249    stuff          = g_new( struct quitdata, 1 );
250    stuff->func    = func;
251    stuff->cbdata  = cbdata;
252    stuff->core    = core;
253
254    wind = gtk_message_dialog_new_with_markup( parent,
255                                               GTK_DIALOG_DESTROY_WITH_PARENT,
256                                               GTK_MESSAGE_QUESTION,
257                                               GTK_BUTTONS_NONE,
258                                               _("<b>Really Quit %s?</b>"),
259                                               g_get_application_name() );
260
261    gtk_message_dialog_format_secondary_text( GTK_MESSAGE_DIALOG(wind),
262                                              _("This will close all active torrents."));
263    gtk_dialog_add_buttons( GTK_DIALOG(wind),
264                            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
265                            GTK_STOCK_QUIT, GTK_RESPONSE_ACCEPT,
266                            NULL );
267    gtk_dialog_set_default_response( GTK_DIALOG( wind ),
268                                     GTK_RESPONSE_ACCEPT );
269    gtk_dialog_set_alternative_button_order( GTK_DIALOG( wind ),
270                                     GTK_RESPONSE_ACCEPT,
271                                     GTK_RESPONSE_CANCEL );
272
273    dontask = gtk_check_button_new_with_mnemonic( _("_Don't ask me this again") );
274    stuff->dontask = dontask;
275
276    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(wind)->vbox), dontask, FALSE, FALSE, GUI_PAD );
277
278    g_signal_connect( G_OBJECT( wind ), "response",
279                      G_CALLBACK( quitresp ), stuff );
280
281    gtk_widget_show_all( wind );
282}
283
284/***
285****
286***/
287
288struct DeleteData
289{
290    gboolean delete_files;
291    GList * torrents;
292    TrCore * core;
293};
294
295static void
296removeTorrents( struct DeleteData * data )
297{
298    GList * l;
299    for( l=data->torrents; l!=NULL; l=l->next )
300        tr_core_remove_torrent( data->core, l->data, data->delete_files );
301    g_list_free( data->torrents );
302}
303
304
305static void
306removeResponse( GtkDialog * dialog, gint response, gpointer gdata )
307{
308    struct DeleteData * data = gdata;
309
310    if( response == GTK_RESPONSE_ACCEPT )
311        removeTorrents( data );
312    else
313        g_list_foreach( data->torrents, (GFunc)g_object_unref, NULL );
314
315    gtk_widget_destroy( GTK_WIDGET( dialog ) );
316    g_free( data );
317}
318
319static void
320countBusyTorrents( gpointer gtor, gpointer busyCount )
321{
322    const tr_stat * stat = tr_torrent_stat( gtor );
323
324    if( stat->leftUntilDone || stat->peersConnected )
325        ++(*(int*)busyCount);
326}
327
328void
329confirmRemove( GtkWindow * parent,
330               TrCore    * core,
331               GList     * torrents,
332               gboolean    delete_files )
333{
334    GtkWidget * d;
335    struct DeleteData * dd;
336    int busyCount;
337    const int count = g_list_length( torrents );
338    const char * primary_text;
339    const char * secondary_text;
340
341    if( !count )
342        return;
343
344    dd = g_new0( struct DeleteData, 1 );
345    dd->core = core;
346    dd->torrents = torrents;
347    dd->delete_files = delete_files;
348
349    busyCount = 0;
350    g_list_foreach( torrents, countBusyTorrents, &busyCount );
351
352    if( !busyCount && !delete_files ) /* don't prompt boring torrents */
353    {
354        removeTorrents( dd );
355        g_free( dd );
356        return;
357    }
358
359    if( !delete_files )
360        primary_text = ngettext( "Remove torrent?", "Remove torrents?", count );
361    else
362        primary_text = ngettext( "Delete this torrent's downloaded files?",
363                                 "Delete these torrents' downloaded files?",
364                                 count );
365
366    if( busyCount > 1 )
367        secondary_text = _( "Some of these torrents are incomplete or connected to peers." );
368    else if( busyCount == 0 )
369        secondary_text = NULL;
370    else
371        secondary_text = ngettext( "This torrent is incomplete or connected to peers.",
372                                   "One of these torrents is incomplete or connected to peers.",
373                                   count );
374
375    d = gtk_message_dialog_new_with_markup( parent,
376                                            GTK_DIALOG_DESTROY_WITH_PARENT,
377                                            ( delete_files ? GTK_MESSAGE_WARNING : GTK_MESSAGE_QUESTION ),
378                                            GTK_BUTTONS_NONE,
379                                            "<b>%s</b>", primary_text );
380    if( secondary_text )
381        gtk_message_dialog_format_secondary_markup( GTK_MESSAGE_DIALOG( d ), secondary_text );
382    gtk_dialog_add_buttons( GTK_DIALOG( d ),
383                            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
384                            (delete_files ? GTK_STOCK_DELETE : GTK_STOCK_REMOVE), GTK_RESPONSE_ACCEPT,
385                            NULL );
386    gtk_dialog_set_default_response( GTK_DIALOG ( d ),
387                                     GTK_RESPONSE_CANCEL );
388    gtk_dialog_set_alternative_button_order( GTK_DIALOG( d ),
389                                             GTK_RESPONSE_ACCEPT,
390                                             GTK_RESPONSE_CANCEL,
391                                             -1 );
392    g_signal_connect( d, "response", G_CALLBACK( removeResponse ), dd );
393    gtk_widget_show_all( d );
394}
Note: See TracBrowser for help on using the repository browser.