source: trunk/gtk/main.c @ 3587

Last change on this file since 3587 was 3587, checked in by charles, 15 years ago

(gtk) if two torrents are selected -- one running, one paused -- both "pause" and "resume" buttons should be enabled. reported by glyphobet.

  • Property svn:keywords set to Date Rev Author Id
File size: 31.5 KB
Line 
1/******************************************************************************
2 * $Id: main.c 3587 2007-10-26 23:29:19Z charles $
3 *
4 * Copyright (c) 2005-2007 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 <sys/param.h>
26#include <errno.h>
27#include <signal.h>
28#include <string.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <time.h>
32#include <unistd.h>
33
34#include <gtk/gtk.h>
35#include <glib/gi18n.h>
36#include <glib/gstdio.h>
37
38#include "actions.h"
39#include "conf.h"
40#include "dialogs.h"
41#include "ipc.h"
42#include "makemeta-ui.h"
43#include "msgwin.h"
44#include "torrent-inspector.h"
45#include "tr_cell_renderer_progress.h"
46#include "tr_core.h"
47#include "tr_icon.h"
48#include "tr_prefs.h"
49#include "tr_torrent.h"
50#include "tr_window.h"
51#include "util.h"
52#include "ui.h"
53
54#include <libtransmission/transmission.h>
55#include <libtransmission/version.h>
56
57/* time in seconds to wait for torrents to stop when exiting */
58#define TRACKER_EXIT_TIMEOUT    10
59
60/* interval in milliseconds to update the torrent list display */
61#define UPDATE_INTERVAL         1000
62
63/* interval in milliseconds to check for stopped torrents and update display */
64#define EXIT_CHECK_INTERVAL     500
65
66/* number of fatal signals required to cause an immediate exit */
67#define SIGCOUNT_MAX            3
68
69#if GTK_CHECK_VERSION(2,8,0)
70#define SHOW_LICENSE
71static const char * LICENSE = 
72"The Transmission binaries and most of its source code is distributed "
73"license. "
74"\n\n"
75"Some files are copyrighted by Charles Kerr and are covered by "
76"the GPL version 2.  Works owned by the Transmission project "
77"are granted a special exemption to clause 2(b) so that the bulk "
78"of its code can remain under the MIT license.  This exemption does "
79"not extend to original or derived works not owned by the "
80"Transmission project. "
81"\n\n"
82"Permission is hereby granted, free of charge, to any person obtaining "
83"a copy of this software and associated documentation files (the "
84"'Software'), to deal in the Software without restriction, including "
85"without limitation the rights to use, copy, modify, merge, publish, "
86"distribute, sublicense, and/or sell copies of the Software, and to "
87"permit persons to whom the Software is furnished to do so, subject to "
88"the following conditions: "
89"\n\n"
90"The above copyright notice and this permission notice shall be included "
91"in all copies or substantial portions of the Software. "
92"\n\n"
93"THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, "
94"EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF "
95"MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. "
96"IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY "
97"CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, "
98"TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE "
99"SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.";
100#endif
101
102struct cbdata {
103    GtkWindow    * wind;
104    TrCore       * core;
105    GtkWidget    * icon;
106    GtkWidget    * msgwin;
107    GtkWidget    * prefs;
108    guint          timer;
109    gboolean       closing;
110    GList        * errqueue;
111};
112
113struct exitdata {
114    struct cbdata * cbdata;
115    time_t          started;
116    guint           timer;
117};
118
119#define CBDATA_PTR              "callback-data-pointer"
120
121static GtkUIManager * myUIManager = NULL;
122
123static sig_atomic_t global_sigcount = 0;
124
125static gboolean
126sendremote( GList * files, gboolean sendquit );
127static void
128appsetup( TrWindow * wind, GList * args,
129          struct cbdata * , gboolean paused );
130static void
131winsetup( struct cbdata * cbdata, TrWindow * wind );
132static void
133makeicon( struct cbdata * cbdata );
134static void
135wannaquit( void * vdata );
136static gboolean
137exitcheck(gpointer gdata);
138static void
139setupdrag(GtkWidget *widget, struct cbdata *data);
140static void
141gotdrag(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
142        GtkSelectionData *sel, guint info, guint time, gpointer gdata);
143
144static void
145coreerr( TrCore * core, enum tr_core_err code, const char * msg,
146         gpointer gdata );
147static void
148coreprompt( TrCore *, GList *, enum tr_torrent_action, gboolean, gpointer );
149static void
150corepromptdata( TrCore *, uint8_t *, size_t, gboolean, gpointer );
151static void
152initializeFromPrefs( struct cbdata * cbdata );
153static void
154prefschanged( TrCore * core, const char * key, gpointer data );
155static void
156setpex( tr_torrent * tor, void * arg );
157static gboolean
158updatemodel(gpointer gdata);
159static GList *
160getselection( struct cbdata * cbdata );
161
162static void
163setupsighandlers(void);
164static void
165fatalsig(int sig);
166
167struct counts_data
168{
169    int totalCount;
170    int activeCount;
171    int inactiveCount;
172};
173
174static void
175accumulateStatusForeach (GtkTreeModel * model,
176                         GtkTreePath  * path UNUSED,
177                         GtkTreeIter  * iter,
178                         gpointer       user_data )
179{
180    int status = 0;
181    struct counts_data * counts = user_data;
182
183    ++counts->totalCount;
184
185    gtk_tree_model_get( model, iter, MC_STAT, &status, -1 );
186    if( TR_STATUS_IS_ACTIVE( status ) )
187        ++counts->activeCount;
188    else
189        ++counts->inactiveCount;
190}
191
192static void
193accumulateCanUpdateForeach (GtkTreeModel * model,
194                            GtkTreePath  * path UNUSED,
195                            GtkTreeIter  * iter,
196                            gpointer       accumulated_status)
197{
198    TrTorrent * gtor = NULL;
199    gtk_tree_model_get( model, iter, MC_TORRENT, &gtor, -1 );
200    *(int*)accumulated_status |=
201        tr_torrentCanManualUpdate( tr_torrent_handle( gtor ) );
202    g_object_unref( G_OBJECT( gtor ) );
203}
204
205static void
206refreshTorrentActions( GtkTreeSelection * s )
207{
208    int canUpdate;
209    struct counts_data counts;
210
211    counts.activeCount = 0;
212    counts.inactiveCount = 0;
213    counts.totalCount = 0;
214    gtk_tree_selection_selected_foreach( s, accumulateStatusForeach, &counts );
215    action_sensitize( "pause-torrent", counts.activeCount!=0 );
216    action_sensitize( "start-torrent", counts.inactiveCount!=0 );
217    action_sensitize( "remove-torrent", counts.totalCount!=0 );
218    action_sensitize( "verify-torrent", counts.totalCount!=0 );
219    action_sensitize( "show-torrent-details", counts.totalCount!=0 );
220
221    canUpdate = 0;
222    gtk_tree_selection_selected_foreach( s, accumulateCanUpdateForeach, &canUpdate );
223    action_sensitize( "update-tracker", canUpdate!=0 );
224}
225
226static void
227selectionChangedCB( GtkTreeSelection * s, gpointer unused UNUSED )
228{
229    refreshTorrentActions( s );
230}
231
232int
233main( int argc, char ** argv )
234{
235    char * err;
236    struct cbdata * cbdata = g_new (struct cbdata, 1);
237    GList * argfiles;
238    gboolean didinit = FALSE;
239    gboolean didlock = FALSE;
240    gboolean sendquit = FALSE;
241    gboolean startpaused = FALSE;
242    char * domain = "transmission";
243    GOptionEntry entries[] = {
244        { "paused", 'p', 0, G_OPTION_ARG_NONE, &startpaused,
245          _("Start with all torrents paused"), NULL },
246        { "quit", 'q', 0, G_OPTION_ARG_NONE, &sendquit,
247          _( "Request that the running instance quit"), NULL },
248        { NULL, 0, 0, 0, NULL, NULL, NULL }
249    };
250
251    /* bind the gettext domain */
252    bindtextdomain( domain, TRANSMISSIONLOCALEDIR );
253    bind_textdomain_codeset( domain, "UTF-8" );
254    textdomain( domain );
255    g_set_application_name( _( "Transmission" ) );
256
257    /* initialize gtk */
258    gtk_init_with_args( &argc, &argv, _("[torrent files]"), entries, domain, NULL );
259    myUIManager = gtk_ui_manager_new ();
260    actions_init ( myUIManager, cbdata );
261    gtk_ui_manager_add_ui_from_string (myUIManager, fallback_ui_file, -1, NULL);
262    gtk_ui_manager_ensure_update (myUIManager);
263    gtk_window_set_default_icon_name ( "transmission-logo" );
264
265    argfiles = checkfilenames( argc-1, argv+1 );
266    didinit = cf_init( tr_getPrefsDirectory(), NULL );
267    didlock = didinit && sendremote( argfiles, sendquit );
268    setupsighandlers( ); /* set up handlers for fatal signals */
269
270    if( ( didinit || cf_init( tr_getPrefsDirectory(), &err ) ) &&
271        ( didlock || cf_lock( &err ) ) )
272    {
273        /* create main window now to be a parent to any error dialogs */
274        GtkWindow * mainwind = GTK_WINDOW( tr_window_new( myUIManager ) );
275
276        tr_prefs_init_global( );
277
278        /* set message level here before tr_init() */
279        msgwin_loadpref( );
280        appsetup( mainwind, argfiles, cbdata, startpaused );
281    }
282    else
283    {
284        gtk_widget_show( errmsg_full( NULL, (callbackfunc_t)gtk_main_quit,
285                                      NULL, "%s", err ) );
286        g_free( err );
287    }
288
289    freestrlist(argfiles);
290
291    gtk_main();
292
293    return 0;
294}
295
296static gboolean
297sendremote( GList * files, gboolean sendquit )
298{
299    const gboolean didlock = cf_lock( NULL );
300
301    /* send files if there's another instance, otherwise start normally */
302    if( !didlock && files )
303        exit( ipc_sendfiles_blocking( files ) ? 0 : 1 );
304
305    /* either send a quit message or exit if no other instance */
306    if( sendquit )
307        exit( didlock ? 0 : !ipc_sendquit_blocking() );
308
309    return didlock;
310}
311
312static void
313appsetup( TrWindow * wind, GList * args,
314          struct cbdata * cbdata, gboolean paused )
315{
316    enum tr_torrent_action action;
317
318    /* fill out cbdata */
319    cbdata->wind       = NULL;
320    cbdata->core       = tr_core_new();
321    cbdata->icon       = NULL;
322    cbdata->msgwin     = NULL;
323    cbdata->prefs      = NULL;
324    cbdata->timer      = 0;
325    cbdata->closing    = FALSE;
326    cbdata->errqueue   = NULL;
327
328    /* set up core handlers */
329    g_signal_connect( cbdata->core, "error", G_CALLBACK( coreerr ), cbdata );
330    g_signal_connect( cbdata->core, "directory-prompt",
331                      G_CALLBACK( coreprompt ), cbdata );
332    g_signal_connect( cbdata->core, "directory-prompt-data",
333                      G_CALLBACK( corepromptdata ), cbdata );
334    g_signal_connect_swapped( cbdata->core, "quit",
335                              G_CALLBACK( wannaquit ), cbdata );
336    g_signal_connect( cbdata->core, "prefs-changed",
337                      G_CALLBACK( prefschanged ), cbdata );
338
339    /* apply a few prefs */
340    initializeFromPrefs( cbdata );
341
342    /* add torrents from command-line and saved state */
343    tr_core_load( cbdata->core, paused );
344
345    if( NULL != args )
346    {
347        action = tr_prefs_get_action( PREF_KEY_ADDIPC );
348        tr_core_add_list( cbdata->core, args, action, paused );
349    }
350    tr_core_torrents_added( cbdata->core );
351
352    /* set up the ipc socket */
353    ipc_socket_setup( GTK_WINDOW( wind ), cbdata->core );
354
355    /* set up main window */
356    winsetup( cbdata, wind );
357
358    /* start model update timer */
359    cbdata->timer = g_timeout_add( UPDATE_INTERVAL, updatemodel, cbdata );
360    updatemodel( cbdata );
361
362    /* show the window */
363    gtk_widget_show( GTK_WIDGET(wind) );
364}
365
366static gboolean
367winclose( GtkWidget * w UNUSED, GdkEvent * event UNUSED, gpointer gdata )
368{
369    struct cbdata * cbdata = gdata;
370
371    if( cbdata->icon != NULL )
372        gtk_widget_hide( GTK_WIDGET( cbdata->wind ) );
373    else
374        askquit( cbdata->core, cbdata->wind, wannaquit, cbdata );
375
376    return TRUE; /* don't propagate event further */
377}
378
379static void
380rowChangedCB( GtkTreeModel  * model UNUSED,
381              GtkTreePath   * path UNUSED,
382              GtkTreeIter   * iter UNUSED,
383              gpointer        sel)
384{
385    refreshTorrentActions( GTK_TREE_SELECTION(sel) );
386}
387
388static void
389winsetup( struct cbdata * cbdata, TrWindow * wind )
390{
391    GtkTreeModel * model;
392    GtkTreeSelection * sel;
393
394    g_assert( NULL == cbdata->wind );
395    cbdata->wind = GTK_WINDOW( wind );
396
397    sel = tr_window_get_selection( cbdata->wind );
398    g_signal_connect( sel, "changed", G_CALLBACK(selectionChangedCB), NULL );
399    selectionChangedCB( sel, NULL );
400    model = tr_core_model( cbdata->core );
401    gtk_tree_view_set_model ( gtk_tree_selection_get_tree_view(sel), model );
402    g_signal_connect( model, "row-changed", G_CALLBACK(rowChangedCB), sel );
403    g_signal_connect( wind, "delete-event", G_CALLBACK( winclose ), cbdata );
404   
405    setupdrag( GTK_WIDGET(wind), cbdata );
406}
407
408static void
409makeicon( struct cbdata * cbdata )
410{
411    if( cbdata->icon == NULL )
412        cbdata->icon = tr_icon_new( );
413}
414
415static void
416wannaquit( void * vdata )
417{
418  struct cbdata * data;
419  struct exitdata *edata;
420
421  data = vdata;
422  if( data->closing )
423  {
424      return;
425  }
426  data->closing = TRUE;
427
428  /* stop the update timer */
429  if(0 < data->timer)
430    g_source_remove(data->timer);
431  data->timer = 0;
432
433  /* pause torrents and stop nat traversal */
434  tr_core_shutdown( data->core );
435
436  /* set things up to wait for torrents to stop */
437  edata = g_new0(struct exitdata, 1);
438  edata->cbdata = data;
439  edata->started = time(NULL);
440  /* check if torrents are still running */
441  if(exitcheck(edata)) {
442    /* yes, start the exit timer and disable widgets */
443    edata->timer = g_timeout_add(EXIT_CHECK_INTERVAL, exitcheck, edata);
444    if( NULL != data->wind )
445    {
446        gtk_widget_set_sensitive( GTK_WIDGET( data->wind ), FALSE );
447    }
448  }
449}
450
451static gboolean
452exitcheck( gpointer gdata )
453{
454    struct exitdata    * edata;
455    struct cbdata      * cbdata;
456
457    edata  = gdata;
458    cbdata = edata->cbdata;
459
460    /* keep waiting until we're ready to quit or we hit the exit timeout */
461    if( time( NULL ) - edata->started < TRACKER_EXIT_TIMEOUT )
462    {
463        if( !tr_core_quiescent( cbdata->core ) )
464        {
465            updatemodel( cbdata );
466            return TRUE;
467        }
468    }
469
470    /* exit otherwise */
471    if( 0 < edata->timer )
472    {
473        g_source_remove( edata->timer );
474    }
475    g_free( edata );
476    /* note that cbdata->prefs holds a reference to cbdata->core, and
477       it's destruction may trigger callbacks that use cbdata->core */
478    if( NULL != cbdata->prefs )
479    {
480        gtk_widget_destroy( GTK_WIDGET( cbdata->prefs ) );
481    }
482    if( NULL != cbdata->wind )
483    {
484        gtk_widget_destroy( GTK_WIDGET( cbdata->wind ) );
485    }
486    g_object_unref( cbdata->core );
487    if( NULL != cbdata->icon )
488    {
489        g_object_unref( cbdata->icon );
490    }
491    g_assert( 0 == cbdata->timer );
492    if( NULL != cbdata->errqueue )
493    {
494        g_list_foreach( cbdata->errqueue, (GFunc) g_free, NULL );
495        g_list_free( cbdata->errqueue );
496    }
497    g_free( cbdata );
498    gtk_main_quit();
499
500    return FALSE;
501}
502
503static void
504gotdrag( GtkWidget         * widget UNUSED,
505         GdkDragContext    * dc,
506         gint                x UNUSED,
507         gint                y UNUSED,
508         GtkSelectionData  * sel,
509         guint               info UNUSED,
510         guint               time,
511         gpointer            gdata )
512{
513    struct cbdata * data = gdata;
514    GList * paths = NULL;
515    GList * freeme = NULL;
516
517#ifdef DND_DEBUG
518    char *sele = gdk_atom_name(sel->selection);
519    char *targ = gdk_atom_name(sel->target);
520    char *type = gdk_atom_name(sel->type);
521
522    g_message( "dropped file: sel=%s targ=%s type=%s fmt=%i len=%i",
523               sele, targ, type, sel->format, sel->length );
524    g_free(sele);
525    g_free(targ);
526    g_free(type);
527    if( sel->format == 8 ) {
528        for( i=0; i<sel->length; ++i )
529            fprintf(stderr, "%02X ", sel->data[i]);
530        fprintf(stderr, "\n");
531    }
532#endif
533
534    if( ( sel->format == 8 ) &&
535        ( sel->selection == gdk_atom_intern( "XdndSelection", FALSE ) ) )
536    {
537        int i;
538        char * str = g_strndup( (char*)sel->data, sel->length );
539        gchar ** files = g_strsplit_set( str, "\r\n", -1 );
540        for( i=0; files && files[i]; ++i )
541        {
542            char * filename;
543            if( !*files[i] ) /* empty filename... */
544                continue;
545
546            /* decode the filename */
547            filename = urldecode( files[i], -1 );
548            freeme = g_list_prepend( freeme, filename );
549            if( !g_utf8_validate( filename, -1, NULL ) )
550                continue;
551
552            /* walk past "file://", if present */
553            if( g_str_has_prefix( filename, "file:" ) ) {
554                filename += 5;
555                while( g_str_has_prefix( filename, "//" ) )
556                    ++filename;
557            }
558
559            /* if the file doesn't exist, the first part
560               might be a hostname ... walk past it. */
561            if( !g_file_test( filename, G_FILE_TEST_EXISTS ) ) {
562                char * pch = strchr( filename + 1, '/' );
563                if( pch != NULL )
564                    filename = pch;
565            }
566
567            /* finally, add it to the list of torrents to try adding */
568            if( g_file_test( filename, G_FILE_TEST_EXISTS ) )
569                paths = g_list_prepend( paths, filename );
570        }
571
572        /* try to add any torrents we found */
573        if( paths != NULL )
574        {
575            enum tr_torrent_action action = tr_prefs_get_action( PREF_KEY_ADDSTD );
576            paths = g_list_reverse( paths );
577            tr_core_add_list( data->core, paths, action, FALSE );
578            tr_core_torrents_added( data->core );
579            g_list_free( paths );
580        }
581
582        freestrlist( freeme );
583        g_strfreev( files );
584        g_free( str );
585    }
586
587    gtk_drag_finish(dc, (NULL != paths), FALSE, time);
588}
589
590static void
591setupdrag(GtkWidget *widget, struct cbdata *data) {
592  GtkTargetEntry targets[] = {
593    { "STRING",     0, 0 },
594    { "text/plain", 0, 0 },
595    { "text/uri-list", 0, 0 },
596  };
597
598  g_signal_connect(widget, "drag_data_received", G_CALLBACK(gotdrag), data);
599
600  gtk_drag_dest_set(widget, GTK_DEST_DEFAULT_ALL, targets,
601                    ALEN(targets), GDK_ACTION_COPY | GDK_ACTION_MOVE);
602}
603
604static void
605coreerr( TrCore * core UNUSED, enum tr_core_err code, const char * msg,
606         gpointer gdata )
607{
608    struct cbdata * cbdata = gdata;
609    char          * joined;
610
611    switch( code )
612    {
613        case TR_CORE_ERR_ADD_TORRENT:
614            cbdata->errqueue = g_list_append( cbdata->errqueue,
615                                              g_strdup( msg ) );
616            return;
617        case TR_CORE_ERR_NO_MORE_TORRENTS:
618            if( NULL != cbdata->errqueue )
619            {
620                joined = joinstrlist( cbdata->errqueue, "\n" );
621                errmsg( cbdata->wind,
622                        ngettext( "Failed to load torrent file:\n%s",
623                                  "Failed to load torrent files:\n%s",
624                                  g_list_length( cbdata->errqueue ) ),
625                        joined );
626                g_list_foreach( cbdata->errqueue, (GFunc) g_free, NULL );
627                g_list_free( cbdata->errqueue );
628                cbdata->errqueue = NULL;
629                g_free( joined );
630            }
631            return;
632        case TR_CORE_ERR_SAVE_STATE:
633            errmsg( cbdata->wind, "%s", msg );
634            return;
635    }
636
637    g_assert_not_reached();
638}
639
640void
641coreprompt( TrCore * core, GList * paths, enum tr_torrent_action act,
642            gboolean paused, gpointer gdata )
643{
644    struct cbdata * cbdata = gdata;
645
646    promptfordir( cbdata->wind, core, paths, NULL, 0, act, paused );
647}
648
649void
650corepromptdata( TrCore * core, uint8_t * data, size_t size,
651                gboolean paused, gpointer gdata )
652{
653    struct cbdata * cbdata = gdata;
654
655    promptfordir( cbdata->wind, core, NULL, data, size, TR_TOR_LEAVE, paused );
656}
657
658static void
659initializeFromPrefs( struct cbdata * cbdata )
660{
661    size_t i;
662    const char * keys[] =
663    {
664        PREF_KEY_PORT,
665        PREF_KEY_DL_LIMIT_ENABLED,
666        PREF_KEY_DL_LIMIT,
667        PREF_KEY_UL_LIMIT_ENABLED,
668        PREF_KEY_UL_LIMIT,
669        PREF_KEY_NAT,
670        PREF_KEY_PEX,
671        PREF_KEY_SYSTRAY,
672        PREF_KEY_SORT_COLUMN,
673        PREF_KEY_ENCRYPTED_ONLY
674    };
675
676    for( i=0; i<G_N_ELEMENTS(keys); ++i )
677        prefschanged( NULL, keys[i], cbdata );
678}
679
680static void
681prefschanged( TrCore * core UNUSED, const char * key, gpointer data )
682{
683    struct cbdata * cbdata = data;
684    tr_handle     * tr     = tr_core_handle( cbdata->core );
685
686    if( !strcmp( key, PREF_KEY_ENCRYPTED_ONLY ) )
687    {
688        const gboolean crypto_only = pref_flag_get( key );
689        tr_setEncryptionMode( tr, crypto_only ? TR_ENCRYPTION_REQUIRED
690                                              : TR_ENCRYPTION_PREFERRED );
691    }
692    else if( !strcmp( key, PREF_KEY_PORT ) )
693    {
694        const int port = pref_int_get( key );
695        tr_setBindPort( tr, port );
696    }
697    else if( !strcmp( key, PREF_KEY_DL_LIMIT_ENABLED ) )
698    {
699        const gboolean b = pref_flag_get( key );
700        tr_setUseGlobalSpeedLimit( tr, TR_DOWN, b );
701    }
702    else if( !strcmp( key, PREF_KEY_DL_LIMIT ) )
703    {
704        const int limit = pref_int_get( key );
705        tr_setGlobalSpeedLimit( tr, TR_DOWN, limit );
706    }
707    else if( !strcmp( key, PREF_KEY_UL_LIMIT_ENABLED ) )
708    {
709        const gboolean b = pref_flag_get( key );
710        tr_setUseGlobalSpeedLimit( tr, TR_UP, b );
711    }
712    else if( !strcmp( key, PREF_KEY_UL_LIMIT ) )
713    {
714        const int limit = pref_int_get( key );
715        tr_setGlobalSpeedLimit( tr, TR_UP, limit );
716    }
717    else if( !strcmp( key, PREF_KEY_NAT ) )
718    {
719        const gboolean enabled = pref_flag_get( key );
720        tr_natTraversalEnable( tr, enabled );
721    }
722    else if( !strcmp( key, PREF_KEY_SYSTRAY ) )
723    {
724        if( pref_flag_get( key ) )
725        {
726            makeicon( cbdata );
727        }
728        else if( cbdata->icon )
729        {
730            g_object_unref( cbdata->icon );
731            cbdata->icon = NULL;
732        }
733    }
734    else if( !strcmp( key, PREF_KEY_SORT_COLUMN ) )
735    {
736        tr_core_set_sort_column_from_prefs( cbdata->core );
737    }
738    else if( !strcmp( key, PREF_KEY_PEX ) )
739    {
740        gboolean enabled = pref_flag_get( key );
741        tr_torrentIterate( tr, setpex, &enabled );
742    }
743}
744
745void
746setpex( tr_torrent * tor, void * arg )
747{
748    gboolean * val;
749
750    val = arg;
751    tr_torrentDisablePex( tor, !(*val) );
752}
753
754gboolean
755updatemodel(gpointer gdata) {
756  struct cbdata *data = gdata;
757  float up, down;
758
759  if( !data->closing && 0 < global_sigcount )
760  {
761      wannaquit( data );
762      return FALSE;
763  }
764
765  /* update the torrent data in the model */
766  tr_core_update( data->core );
767
768  /* update the main window's statusbar and toolbar buttons */
769  if( NULL != data->wind )
770  {
771      tr_torrentRates( tr_core_handle( data->core ), &down, &up );
772      tr_window_update( data->wind, down, up );
773  }
774
775  /* update the message window */
776  msgwin_update();
777
778  return TRUE;
779}
780
781/* returns a GList of GtkTreeRowReferences to each selected row */
782static GList *
783getselection( struct cbdata * cbdata )
784{
785    GList * rows = NULL;
786
787    if( NULL != cbdata->wind )
788    {
789        GList * ii;
790        GtkTreeSelection *s = tr_window_get_selection(cbdata->wind);
791        GtkTreeModel * model = tr_core_model( cbdata->core );
792        rows = gtk_tree_selection_get_selected_rows( s, NULL );
793        for( ii = rows; NULL != ii; ii = ii->next )
794        {
795            GtkTreeRowReference * ref = gtk_tree_row_reference_new(
796                model, ii->data );
797            gtk_tree_path_free( ii->data );
798            ii->data = ref;
799        }
800    }
801
802    return rows;
803}
804
805static void
806about ( void )
807{
808  GtkWidget * w = gtk_about_dialog_new ();
809  GtkAboutDialog * a = GTK_ABOUT_DIALOG (w);
810  const char *authors[] = { "Charles Kerr (Backend; GTK+)",
811                            "Mitchell Livingston (Backend; OS X)",
812                            "Eric Petit (Backend; OS X)",
813                            "Josh Elsasser (Daemon; Backend; GTK+)",
814                            "Bryan Varner (BeOS)", 
815                            NULL };
816  gtk_about_dialog_set_version (a, LONG_VERSION_STRING );
817#ifdef SHOW_LICENSE
818  gtk_about_dialog_set_license (a, LICENSE);
819  gtk_about_dialog_set_wrap_license (a, TRUE);
820#endif
821  gtk_about_dialog_set_logo_icon_name( a, "transmission-logo" );
822  gtk_about_dialog_set_comments( a, _("A fast and easy BitTorrent client") );
823  gtk_about_dialog_set_website( a, "http://transmission.m0k.org/" );
824  gtk_about_dialog_set_copyright( a, _("Copyright 2005-2007 The Transmission Project") );
825  gtk_about_dialog_set_authors( a, authors );
826  /* note to translators: put yourself here for credit in the "About" dialog */
827  gtk_about_dialog_set_translator_credits( a, _("translator-credits") );
828  g_signal_connect_swapped( w, "response", G_CALLBACK (gtk_widget_destroy), w );
829  gtk_widget_show_all( w );
830}
831
832static void
833startTorrentForeach (GtkTreeModel * model,
834                     GtkTreePath  * path UNUSED,
835                     GtkTreeIter  * iter,
836                     gpointer       data UNUSED)
837{
838    TrTorrent * tor = NULL;
839    gtk_tree_model_get( model, iter, MC_TORRENT, &tor, -1 );
840    tr_torrent_start( tor );
841    g_object_unref( G_OBJECT( tor ) );
842}
843
844static void
845stopTorrentForeach (GtkTreeModel * model,
846                    GtkTreePath  * path UNUSED,
847                    GtkTreeIter  * iter,
848                    gpointer       data UNUSED)
849{
850    TrTorrent * tor = NULL;
851    gtk_tree_model_get( model, iter, MC_TORRENT, &tor, -1 );
852    tr_torrent_stop( tor );
853    g_object_unref( G_OBJECT( tor ) );
854}
855
856static void
857updateTrackerForeach (GtkTreeModel * model,
858                      GtkTreePath  * path UNUSED,
859                      GtkTreeIter  * iter,
860                      gpointer       data UNUSED)
861{
862    TrTorrent * tor = NULL;
863    gtk_tree_model_get( model, iter, MC_TORRENT, &tor, -1 );
864    tr_manualUpdate( tr_torrent_handle( tor ) );
865    g_object_unref( G_OBJECT( tor ) );
866}
867
868static void
869showInfoForeach (GtkTreeModel * model,
870                 GtkTreePath  * path UNUSED,
871                 GtkTreeIter  * iter,
872                 gpointer       data UNUSED)
873{
874    TrTorrent * tor = NULL;
875    GtkWidget * w;
876    gtk_tree_model_get( model, iter, MC_TORRENT, &tor, -1 );
877    w = torrent_inspector_new( GTK_WINDOW(data), tor );
878    gtk_widget_show( w );
879    g_object_unref( G_OBJECT( tor ) );
880}
881
882static void
883recheckTorrentForeach (GtkTreeModel * model,
884                       GtkTreePath  * path UNUSED,
885                       GtkTreeIter  * iter,
886                       gpointer       data UNUSED)
887{
888    TrTorrent * gtor = NULL;
889    gtk_tree_model_get( model, iter, MC_TORRENT, &gtor, -1 );
890    tr_torrentRecheck( tr_torrent_handle( gtor ) );
891    g_object_unref( G_OBJECT( gtor ) );
892}
893
894static gboolean
895msgwinclosed()
896{
897  action_toggle( "toggle-debug-window", FALSE );
898  return FALSE;
899}
900
901void
902doAction ( const char * action_name, gpointer user_data )
903{
904    struct cbdata * data = (struct cbdata *) user_data;
905    gboolean changed = FALSE;
906
907    if (!strcmp (action_name, "add-torrent"))
908    {
909        makeaddwind( data->wind, data->core );
910    }
911    else if (!strcmp (action_name, "start-torrent"))
912    {
913        GtkTreeSelection * s = tr_window_get_selection(data->wind);
914        gtk_tree_selection_selected_foreach( s, startTorrentForeach, NULL );
915        changed |= gtk_tree_selection_count_selected_rows( s ) != 0;
916    }
917    else if (!strcmp (action_name, "pause-torrent"))
918    {
919        GtkTreeSelection * s = tr_window_get_selection(data->wind);
920        gtk_tree_selection_selected_foreach( s, stopTorrentForeach, NULL );
921        changed |= gtk_tree_selection_count_selected_rows( s ) != 0;
922    }
923    else if (!strcmp (action_name, "verify-torrent"))
924    {
925        GtkTreeSelection * s = tr_window_get_selection(data->wind);
926        gtk_tree_selection_selected_foreach( s, recheckTorrentForeach, NULL );
927        changed |= gtk_tree_selection_count_selected_rows( s ) != 0;
928    }
929    else if (!strcmp (action_name, "show-torrent-details"))
930    {
931        GtkTreeSelection * s = tr_window_get_selection(data->wind);
932        gtk_tree_selection_selected_foreach( s, showInfoForeach, data->wind );
933    }
934    else if (!strcmp( action_name, "update-tracker"))
935    {
936        GtkTreeSelection * s = tr_window_get_selection(data->wind);
937        gtk_tree_selection_selected_foreach( s, updateTrackerForeach, data->wind );
938    }
939    else if (!strcmp (action_name, "create-torrent"))
940    {
941        GtkWidget * w = make_meta_ui( GTK_WINDOW( data->wind ),
942                                      tr_core_handle( data->core ) );
943        gtk_widget_show_all( w );
944    }
945    else if (!strcmp (action_name, "remove-torrent"))
946    {
947        /* this modifies the model's contents, so can't use foreach */
948        GList *l, *sel = getselection( data );
949        GtkTreeModel *model = tr_core_model( data->core );
950        for( l=sel; l!=NULL; l=l->next )
951        {
952            GtkTreeIter iter;
953            GtkTreeRowReference * reference = (GtkTreeRowReference *) l->data;
954            GtkTreePath * path = gtk_tree_row_reference_get_path( reference );
955            gtk_tree_model_get_iter( model, &iter, path );
956            tr_core_delete_torrent( data->core, &iter );
957            gtk_tree_row_reference_free( reference );
958            changed = TRUE;
959        }
960        g_list_free( sel );
961    }
962    else if (!strcmp (action_name, "close"))
963    {
964        if( data->wind != NULL )
965            winclose( NULL, NULL, data );
966    }
967    else if (!strcmp (action_name, "quit"))
968    {
969        askquit( data->core, data->wind, wannaquit, data );
970    }
971    else if (!strcmp (action_name, "select-all"))
972    {
973        GtkTreeSelection * s = tr_window_get_selection(data->wind);
974        gtk_tree_selection_select_all( s );
975    }
976    else if (!strcmp (action_name, "unselect-all"))
977    {
978        GtkTreeSelection * s = tr_window_get_selection(data->wind);
979        gtk_tree_selection_unselect_all( s );
980    }
981    else if (!strcmp (action_name, "edit-preferences"))
982    {
983        if( NULL == data->prefs )
984        {
985            data->prefs = tr_prefs_dialog_new( G_OBJECT(data->core), data->wind );
986            g_signal_connect( data->prefs, "destroy",
987                             G_CALLBACK( gtk_widget_destroyed ), &data->prefs );
988            gtk_widget_show( GTK_WIDGET( data->prefs ) );
989        }
990    }
991    else if (!strcmp (action_name, "toggle-debug-window"))
992    {
993        if( !data->msgwin )
994        {
995            GtkWidget * win = msgwin_create( data->core );
996            g_signal_connect( win, "destroy", G_CALLBACK( msgwinclosed ), 
997                             NULL );
998            data->msgwin = win;
999        }
1000        else
1001        {
1002            action_toggle("toggle-debug-window", FALSE);
1003            gtk_widget_destroy( data->msgwin );
1004            data->msgwin = NULL;
1005        }
1006    }
1007    else if (!strcmp (action_name, "show-about-dialog"))
1008    {
1009        about();
1010    }
1011    else if (!strcmp (action_name, "toggle-main-window"))
1012    {
1013        GtkWidget * w = GTK_WIDGET (data->wind);
1014        if (GTK_WIDGET_VISIBLE(w))
1015            gtk_widget_hide (w);
1016        else
1017            gtk_window_present (GTK_WINDOW(w));
1018    }
1019    else g_error ("Unhandled action: %s", action_name );
1020
1021    if( changed )
1022        updatemodel( data );
1023}
1024
1025
1026static void
1027setupsighandlers(void) {
1028  int sigs[] = {SIGHUP, SIGINT, SIGQUIT, SIGTERM};
1029  struct sigaction sa;
1030  int ii;
1031
1032  memset(&sa, 0,  sizeof(sa));
1033  sa.sa_handler = fatalsig;
1034  for(ii = 0; ii < ALEN(sigs); ii++)
1035    sigaction(sigs[ii], &sa, NULL);
1036}
1037
1038static void
1039fatalsig(int sig) {
1040  struct sigaction sa;
1041
1042  if(SIGCOUNT_MAX <= ++global_sigcount) {
1043    memset(&sa, 0,  sizeof(sa));
1044    sa.sa_handler = SIG_DFL;
1045    sigaction(sig, &sa, NULL);
1046    raise(sig);
1047  }
1048}
Note: See TracBrowser for help on using the repository browser.