source: trunk/gtk/main.c @ 3486

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

remove dead code (TR_STATUS_ACTIVE, TR_STATUS_INACTIVE)

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