source: trunk/gtk/tr_core.c @ 3206

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

preferences code refresh in the gtk+ client

  • Property svn:keywords set to Date Rev Author Id
File size: 20.1 KB
Line 
1/******************************************************************************
2 * $Id: tr_core.c 3206 2007-09-27 20:57:58Z charles $
3 *
4 * Copyright (c) 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 <inttypes.h>
26#include <stdio.h>
27#include <string.h>
28
29#include <gtk/gtk.h>
30#include <glib/gi18n.h>
31
32#include <libtransmission/bencode.h>
33#include <libtransmission/transmission.h>
34#include <libtransmission/utils.h>
35
36#include "conf.h"
37#include "tr_core.h"
38#include "tr_prefs.h"
39#include "tr_torrent.h"
40#include "util.h"
41
42static void
43tr_core_init( GTypeInstance * instance, gpointer g_class );
44static void
45tr_core_class_init( gpointer g_class, gpointer g_class_data );
46static void
47tr_core_marshal_err( GClosure * closure, GValue * ret, guint count,
48                     const GValue * vals, gpointer hint, gpointer marshal );
49void
50tr_core_marshal_prompt( GClosure * closure, GValue * ret, guint count,
51                        const GValue * vals, gpointer hint, gpointer marshal );
52void
53tr_core_marshal_data( GClosure * closure, GValue * ret, guint count,
54                      const GValue * vals, gpointer hint, gpointer marshal );
55static void
56tr_core_dispose( GObject * obj );
57static void
58tr_core_insert( TrCore * self, TrTorrent * tor );
59static void
60tr_core_errsig( TrCore * self, enum tr_core_err type, const char * msg );
61
62GType
63tr_core_get_type( void )
64{
65    static GType type = 0;
66
67    if( 0 == type )
68    {
69        static const GTypeInfo info =
70        {
71            sizeof( TrCoreClass ),
72            NULL,                       /* base_init */
73            NULL,                       /* base_finalize */
74            tr_core_class_init,         /* class_init */
75            NULL,                       /* class_finalize */
76            NULL,                       /* class_data */
77            sizeof( TrCore ),
78            0,                          /* n_preallocs */
79            tr_core_init,               /* instance_init */
80            NULL,
81        };
82        type = g_type_register_static( G_TYPE_OBJECT, "TrCore", &info, 0 );
83    }
84
85    return type;
86}
87
88void
89tr_core_class_init( gpointer g_class, gpointer g_class_data SHUTUP )
90{
91    GObjectClass * gobject_class;
92    TrCoreClass  * core_class;
93
94    gobject_class = G_OBJECT_CLASS( g_class );
95    gobject_class->dispose = tr_core_dispose;
96
97    core_class = TR_CORE_CLASS( g_class );
98    core_class->errsig = g_signal_new( "error", G_TYPE_FROM_CLASS( g_class ),
99                                       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
100                                       tr_core_marshal_err, G_TYPE_NONE,
101                                       2, G_TYPE_INT, G_TYPE_STRING );
102    core_class->promptsig = g_signal_new( "directory-prompt",
103                                          G_TYPE_FROM_CLASS( g_class ),
104                                          G_SIGNAL_RUN_LAST, 0, NULL, NULL,
105                                          tr_core_marshal_prompt, G_TYPE_NONE,
106                                          3, G_TYPE_POINTER, G_TYPE_INT,
107                                          G_TYPE_BOOLEAN );
108    core_class->promptdatasig = g_signal_new( "directory-prompt-data",
109                                              G_TYPE_FROM_CLASS( g_class ),
110                                              G_SIGNAL_RUN_LAST, 0, NULL, NULL,
111                                              tr_core_marshal_data,
112                                              G_TYPE_NONE, 3, G_TYPE_STRING,
113                                              G_TYPE_UINT, G_TYPE_BOOLEAN );
114    core_class->quitsig = g_signal_new( "quit", G_TYPE_FROM_CLASS( g_class ),
115                                        G_SIGNAL_RUN_LAST, 0, NULL, NULL,
116                                        g_cclosure_marshal_VOID__VOID,
117                                        G_TYPE_NONE, 0 );
118    core_class->prefsig = g_signal_new( "prefs-changed",
119                                        G_TYPE_FROM_CLASS( g_class ),
120                                        G_SIGNAL_RUN_LAST, 0, NULL, NULL,
121                                        g_cclosure_marshal_VOID__STRING,
122                                        G_TYPE_NONE, 1, G_TYPE_STRING );
123}
124
125void
126tr_core_marshal_err( GClosure * closure, GValue * ret SHUTUP, guint count,
127                     const GValue * vals, gpointer hint SHUTUP,
128                     gpointer marshal )
129{
130    typedef void (*TRMarshalErr)
131        ( gpointer, enum tr_core_err, const char *, gpointer );
132    TRMarshalErr     callback;
133    GCClosure      * cclosure = (GCClosure*) closure;
134    enum tr_core_err errcode;
135    const char     * errstr;
136    gpointer         inst, gdata;
137
138    g_return_if_fail( 3 == count );
139
140    inst    = g_value_peek_pointer( vals );
141    errcode = g_value_get_int( vals + 1 );
142    errstr  = g_value_get_string( vals + 2 );
143    gdata   = closure->data;
144
145    callback = (TRMarshalErr) ( NULL == marshal ?
146                                cclosure->callback : marshal );
147    callback( inst, errcode, errstr, gdata );
148}
149
150void
151tr_core_marshal_prompt( GClosure * closure, GValue * ret SHUTUP, guint count,
152                        const GValue * vals, gpointer hint SHUTUP,
153                        gpointer marshal )
154{
155    typedef void (*TRMarshalPrompt)
156        ( gpointer, GList *, enum tr_torrent_action, gboolean, gpointer );
157    TRMarshalPrompt        callback;
158    GCClosure            * cclosure = (GCClosure*) closure;
159    GList                * paths;
160    enum tr_torrent_action action;
161    gboolean               paused;
162    gpointer               inst, gdata;
163
164    g_return_if_fail( 4 == count );
165
166    inst    = g_value_peek_pointer( vals );
167    paths   = g_value_get_pointer( vals + 1 );
168    action  = g_value_get_int( vals + 2 );
169    paused  = g_value_get_boolean( vals + 3 );
170    gdata   = closure->data;
171
172    callback = (TRMarshalPrompt) ( NULL == marshal ?
173                                   cclosure->callback : marshal );
174    callback( inst, paths, action, paused, gdata );
175}
176
177void
178tr_core_marshal_data( GClosure * closure, GValue * ret SHUTUP, guint count,
179                      const GValue * vals, gpointer hint SHUTUP,
180                      gpointer marshal )
181{
182    typedef void (*TRMarshalPrompt)
183        ( gpointer, uint8_t *, size_t, gboolean, gpointer );
184    TRMarshalPrompt        callback;
185    GCClosure            * cclosure = (GCClosure*) closure;
186    uint8_t              * data;
187    size_t                 size;
188    gboolean               paused;
189    gpointer               inst, gdata;
190
191    g_return_if_fail( 4 == count );
192
193    inst    = g_value_peek_pointer( vals );
194    data    = (uint8_t *) g_value_get_string( vals + 1 );
195    size    = g_value_get_uint( vals + 2 );
196    paused  = g_value_get_boolean( vals + 3 );
197    gdata   = closure->data;
198
199    callback = (TRMarshalPrompt) ( NULL == marshal ?
200                                   cclosure->callback : marshal );
201    callback( inst, data, size, paused, gdata );
202}
203
204void
205tr_core_init( GTypeInstance * instance, gpointer g_class SHUTUP )
206{
207    TrCore * self = (TrCore *) instance;
208    GtkListStore * store;
209
210    /* column types for the model used to store torrent information */
211    /* keep this in sync with the enum near the bottom of tr_core.h */
212    GType types[] =
213    {
214        /* info->name, info->totalSize, info->hashString, status, */
215        G_TYPE_STRING, G_TYPE_UINT64,   G_TYPE_STRING,    G_TYPE_INT,
216        /* error,   errorString,   percentComplete, percentDone,  rateDownload, rateUpload, */
217        G_TYPE_INT, G_TYPE_STRING, G_TYPE_FLOAT,    G_TYPE_FLOAT, G_TYPE_FLOAT, G_TYPE_FLOAT,
218        /* eta,     peersConnected, peersUploading, peersDownloading, seeders, */
219        G_TYPE_INT, G_TYPE_INT,     G_TYPE_INT,     G_TYPE_INT,       G_TYPE_INT,
220        /* leechers, completedFromTracker, downloaded,    uploaded */
221        G_TYPE_INT,  G_TYPE_INT,           G_TYPE_UINT64, G_TYPE_UINT64,
222        /* left,       tracker,               TrTorrent object, ID for IPC */
223        G_TYPE_UINT64, TR_TRACKER_BOXED_TYPE, TR_TORRENT_TYPE,  G_TYPE_INT,
224    };
225
226#ifdef REFDBG
227    fprintf( stderr, "core    %p init\n", self );
228#endif
229
230    /* create the model used to store torrent data */
231    g_assert( ALEN( types ) == MC_ROW_COUNT );
232    store = gtk_list_store_newv( MC_ROW_COUNT, types );
233
234    self->model    = GTK_TREE_MODEL( store );
235    self->handle   = tr_init( "gtk" );
236    self->nextid   = 1;
237    self->quitting = FALSE;
238    self->disposed = FALSE;
239}
240
241void
242tr_core_dispose( GObject * obj )
243{
244    TrCore       * self = (TrCore *) obj;
245    GObjectClass * parent;
246    GtkTreeIter    iter;
247    TrTorrent    * tor;
248
249    if( self->disposed )
250    {
251        return;
252    }
253    self->disposed = TRUE;
254
255    pref_save( NULL );
256
257#ifdef REFDBG
258    fprintf( stderr, "core    %p dispose\n", self );
259#endif
260
261    /* sever all remaining torrents in the model */
262    if( gtk_tree_model_get_iter_first( self->model, &iter ) ) do
263    {
264        gtk_tree_model_get( self->model, &iter, MC_TORRENT, &tor, -1 );
265        tr_torrent_sever( tor );
266        g_object_unref( tor );
267    }
268    while( gtk_tree_model_iter_next( self->model, &iter ) );
269    g_object_unref( self->model );
270
271#ifdef REFDBG
272    fprintf( stderr, "core    %p dead\n", self );
273#endif
274
275    /* close the libtransmission instance */
276    tr_close( self->handle );
277
278    /* Chain up to the parent class */
279    parent = g_type_class_peek( g_type_parent( TR_CORE_TYPE ) );
280    parent->dispose( obj );
281}
282
283TrCore *
284tr_core_new( void )
285{
286    return g_object_new( TR_CORE_TYPE, NULL );
287}
288
289GtkTreeModel *
290tr_core_model( TrCore * self )
291{
292    g_return_val_if_fail (TR_IS_CORE(self), NULL);
293
294    return self->disposed ? NULL : self->model;
295}
296
297tr_handle *
298tr_core_handle( TrCore * self )
299{
300    g_return_val_if_fail (TR_IS_CORE(self), NULL);
301
302    return self->disposed ? NULL : self->handle;
303}
304
305void
306tr_core_shutdown( TrCore * self )
307{
308    GtkTreeIter iter;
309
310    TR_IS_CORE( self );
311
312    if( self->disposed )
313        return;
314
315    g_assert( !self->quitting );
316    self->quitting = TRUE;
317
318    /* try to stop all the torrents nicely */
319    if ( gtk_tree_model_get_iter_first( self->model, &iter) ) do {
320        TrTorrent * tor;
321        gtk_tree_model_get( self->model, &iter, MC_TORRENT, &tor, -1 );
322        tr_torrent_sever( tor );
323        g_object_unref( tor );
324    } while( gtk_list_store_remove( GTK_LIST_STORE(self->model), &iter ) );
325
326    /* shut down nat traversal */
327    tr_natTraversalEnable( self->handle, 0 );
328}
329
330gboolean
331tr_core_quiescent( TrCore * self )
332{
333    const tr_handle_status * hstat;
334
335    TR_IS_CORE( self );
336    g_assert( self->quitting );
337
338    if( self->disposed )
339        return TRUE;
340
341    if ( tr_torrentCount( self->handle ) != 0 )
342        return FALSE;
343
344    hstat = tr_handleStatus( self->handle );
345    return TR_NAT_TRAVERSAL_DISABLED == hstat->natTraversalStatus;
346}
347
348void
349tr_core_save( TrCore * self )
350{
351    benc_val_t  state;
352    int         count;
353    GtkTreeIter iter;
354    TrTorrent * tor;
355    char      * errstr;
356    GList     * saved, * ii;
357
358    TR_IS_CORE( self );
359
360    count = gtk_tree_model_iter_n_children( self->model, NULL );
361
362    tr_bencInit( &state, TYPE_LIST );
363    if( tr_bencListReserve( &state, count ) )
364    {
365        tr_core_errsig( self, TR_CORE_ERR_SAVE_STATE, "malloc failure" );
366        return;
367    }
368
369    saved = NULL;
370    if( gtk_tree_model_get_iter_first( self->model, &iter) ) do
371    {
372        benc_val_t * item = tr_bencListAdd( &state );
373        gtk_tree_model_get( self->model, &iter, MC_TORRENT, &tor, -1 );
374        if( tr_torrent_get_state( tor, item ) )
375        {
376            saved = g_list_append( saved, tor );
377        }
378        else
379        {
380            tr_bencFree( item );
381            tr_bencInitStr( item, NULL, 0, 1 );
382        }
383        g_object_unref( tor );
384    }
385    while( gtk_tree_model_iter_next( self->model, &iter ) );
386
387    errstr = NULL;
388    cf_savestate( &state, &errstr );
389    tr_bencFree( &state );
390    if( NULL != errstr )
391    {
392        tr_core_errsig( self, TR_CORE_ERR_SAVE_STATE, errstr );
393        g_free( errstr );
394    }
395    else
396    {
397        for( ii = saved; NULL != ii; ii = ii->next )
398        {
399            tr_torrent_state_saved( ii->data );
400        }
401    }
402
403    g_list_free( saved );
404}
405
406int
407tr_core_load( TrCore * self, gboolean forcepaused )
408{
409    int i;
410    int flags;
411    int count = 0;
412    tr_torrent ** torrents;
413    char * path;
414
415    TR_IS_CORE( self );
416
417    path = getdownloaddir( );
418
419    flags = 0;
420    if( forcepaused )
421         flags |= TR_FLAG_PAUSED;
422
423    torrents = tr_loadTorrents ( self->handle, path, flags, &count );
424    for( i=0; i<count; ++i )
425        tr_core_insert( self, tr_torrent_new_preexisting( torrents[i] ) );
426    tr_free( torrents );
427
428    g_free( path );
429    return count;
430}
431
432gboolean
433tr_core_add( TrCore * self, const char * path, enum tr_torrent_action act,
434             gboolean paused )
435{
436    GList * list;
437    int     ret;
438
439    TR_IS_CORE( self );
440
441    list = g_list_append( NULL, (void *) path );
442    ret  = tr_core_add_list( self, list, act, paused );
443    g_list_free( list );
444
445    return 1 == ret;
446}
447
448gboolean
449tr_core_add_dir( TrCore * self, const char * path, const char * dir,
450                 enum tr_torrent_action act, gboolean paused )
451{
452    TrTorrent * tor;
453    char      * errstr;
454
455    TR_IS_CORE( self );
456
457    errstr = NULL;
458    tor = tr_torrent_new( self->handle, path, dir, act, paused, &errstr );
459    if( NULL == tor )
460    {
461        tr_core_errsig( self, TR_CORE_ERR_ADD_TORRENT, errstr );
462        g_free( errstr );
463        return FALSE;
464    }
465    g_assert( NULL == errstr );
466
467    tr_core_insert( self, tor );
468
469    return TRUE;
470}
471
472int
473tr_core_add_list( TrCore * self, GList * paths, enum tr_torrent_action act,
474                  gboolean paused )
475{
476    char * dir;
477    int count;
478
479    TR_IS_CORE( self );
480
481    if( pref_flag_get( PREF_KEY_DIR_ASK ) )
482    {
483        TrCoreClass * class = g_type_class_peek( TR_CORE_TYPE );
484        g_signal_emit( self, class->promptsig, 0, paths, act, paused );
485        return 0;
486    }
487
488    dir = getdownloaddir();
489    count = 0;
490    for( ; paths; paths=paths->next )
491        if( tr_core_add_dir( self, paths->data, dir, act, paused ) )
492            count++;
493
494    g_free( dir );
495    return count;
496}
497
498gboolean
499tr_core_add_data( TrCore * self, uint8_t * data, size_t size, gboolean paused )
500{
501    gboolean ret;
502    char * path;
503    TR_IS_CORE( self );
504
505    if( pref_flag_get( PREF_KEY_DIR_ASK ) )
506    {
507        TrCoreClass * class = g_type_class_peek( TR_CORE_TYPE );
508        g_signal_emit( self, class->promptdatasig, 0, data, size, paused );
509        return FALSE;
510    }
511
512    path = getdownloaddir( );
513    ret = tr_core_add_data_dir( self, data, size, path, paused );
514    g_free( path );
515    return ret;
516}
517
518gboolean
519tr_core_add_data_dir( TrCore * self, uint8_t * data, size_t size,
520                      const char * dir, gboolean paused )
521{
522    TrTorrent * tor;
523    char      * errstr = NULL;
524
525    TR_IS_CORE( self );
526
527    tor = tr_torrent_new_with_data( self->handle, data, size, dir,
528                                    paused, &errstr );
529    if( NULL == tor )
530    {
531        tr_core_errsig( self, TR_CORE_ERR_ADD_TORRENT, errstr );
532        g_free( errstr );
533        return FALSE;
534    }
535    g_assert( NULL == errstr );
536
537    tr_core_insert( self, tor );
538
539    return TRUE;
540}
541
542void
543tr_core_torrents_added( TrCore * self )
544{
545    TR_IS_CORE( self );
546
547    tr_core_update( self );
548    tr_core_save( self );
549    tr_core_errsig( self, TR_CORE_ERR_NO_MORE_TORRENTS, NULL );
550}
551
552void
553tr_core_delete_torrent( TrCore * self, GtkTreeIter * iter )
554{
555    TrTorrent * tor;
556
557    TR_IS_CORE( self );
558
559    gtk_tree_model_get( self->model, iter, MC_TORRENT, &tor, -1 );
560    gtk_list_store_remove( GTK_LIST_STORE( self->model ), iter );
561    tr_torrentRemoveSaved( tr_torrent_handle( tor ) );
562
563    tr_torrent_sever( tor );
564}
565
566void
567tr_core_insert( TrCore * self, TrTorrent * tor )
568{
569    GtkTreeIter iter;
570    const tr_info * inf;
571
572    gtk_list_store_append( GTK_LIST_STORE( self->model ), &iter );
573    inf = tr_torrent_info( tor );
574    gtk_list_store_set( GTK_LIST_STORE( self->model ), &iter,
575                        MC_NAME,    inf->name,
576                        MC_SIZE,    inf->totalSize,
577                        MC_HASH,    inf->hashString,
578                        MC_TORRENT, tor,
579                        MC_ID,      self->nextid,
580                        -1);
581    g_object_unref( tor );
582    self->nextid++;
583}
584
585void
586tr_core_update( TrCore * self )
587{
588    GtkTreeIter iter;
589    TrTorrent * tor;
590    const tr_stat * st;
591
592    TR_IS_CORE( self );
593
594    if( gtk_tree_model_get_iter_first( self->model, &iter ) ) do
595    {
596        gtk_tree_model_get( self->model, &iter, MC_TORRENT, &tor, -1 );
597        st = tr_torrent_stat( tor );
598        g_object_unref( tor );
599        tr_torrent_check_seeding_cap ( tor );
600
601        /* XXX find out if setting the same data emits changed signal */
602        gtk_list_store_set( GTK_LIST_STORE( self->model ), &iter,
603                            MC_STAT,        st->status,
604                            MC_ERR,         st->error,
605                            MC_TERR,        st->errorString,
606                            MC_PROG_C,      st->percentComplete,
607                            MC_PROG_D,      st->percentDone,
608                            MC_DRATE,       st->rateDownload,
609                            MC_URATE,       st->rateUpload,
610                            MC_ETA,         st->eta,
611                            MC_PEERS,       st->peersConnected,
612                            MC_UPEERS,      st->peersGettingFromUs,
613                            MC_DPEERS,      st->peersSendingToUs,
614                            MC_SEED,        st->seeders,
615                            MC_LEECH,       st->leechers,
616                            MC_DONE,        st->completedFromTracker,
617                            MC_TRACKER,     st->tracker,
618                            MC_DOWN,        st->downloadedEver,
619                            MC_UP,          st->uploadedEver,
620                            MC_LEFT,        st->leftUntilDone,
621                            -1 );
622    }
623    while( gtk_tree_model_iter_next( self->model, &iter ) );
624}
625
626void
627tr_core_errsig( TrCore * self, enum tr_core_err type, const char * msg )
628{
629    TrCoreClass * class;
630
631    class = g_type_class_peek( TR_CORE_TYPE );
632    g_signal_emit( self, class->errsig, 0, type, msg );
633}
634
635void
636tr_core_quit( TrCore * self )
637{
638    TrCoreClass * class;
639
640    TR_IS_CORE( self );
641
642    class = g_type_class_peek( TR_CORE_TYPE );
643    g_signal_emit( self, class->quitsig, 0 );
644}
645
646/**
647***  Prefs
648**/
649
650static void
651commitPrefsChange( TrCore * self, const char * key )
652{
653    TrCoreClass * class = g_type_class_peek( TR_CORE_TYPE );
654    pref_save( NULL );
655    g_signal_emit( self, class->prefsig, 0, key );
656}
657
658void
659tr_core_set_pref( TrCore * self, const char * key, const char * newval )
660{
661    char * oldval = pref_string_get( key );
662    if( tr_strcmp( oldval, newval ) )
663    {
664        pref_string_set( key, newval );
665        commitPrefsChange( self, key );
666    }
667    g_free( oldval );
668}
669
670void
671tr_core_set_pref_bool( TrCore * self, const char * key, gboolean newval )
672{
673    const gboolean oldval = pref_flag_get( key );
674    if( oldval != newval )
675    {
676        pref_flag_set( key, newval );
677        commitPrefsChange( self, key );
678    }
679}
680
681void
682tr_core_set_pref_int( TrCore * self, const char * key, int newval )
683{
684    const int oldval = pref_int_get( key );
685    if( oldval != newval )
686    {
687        pref_int_set( key, newval );
688        commitPrefsChange( self, key );
689    }
690}
Note: See TracBrowser for help on using the repository browser.