source: trunk/gtk/tr_core.c @ 4998

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

(gtk) make TrCore?'s fields private

  • Property svn:keywords set to Date Rev Author Id
File size: 23.4 KB
Line 
1/******************************************************************************
2 * $Id: tr_core.c 4998 2008-02-09 17:29:05Z charles $
3 *
4 * Copyright (c) 2007-2008 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include <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/transmission.h>
33#include <libtransmission/utils.h>
34
35#include "conf.h"
36#include "tr_core.h"
37#include "tr_prefs.h"
38#include "tr_torrent.h"
39#include "util.h"
40
41struct TrCorePrivate
42{
43    GtkTreeModel     * model;
44    tr_handle        * handle;
45    int                nextid;
46    gboolean           quitting;
47    struct core_stats  stats;
48};
49
50static void
51tr_core_marshal_err( GClosure * closure, GValue * ret UNUSED, guint count,
52                     const GValue * vals, gpointer hint UNUSED,
53                     gpointer marshal )
54{
55    typedef void (*TRMarshalErr)
56        ( gpointer, enum tr_core_err, const char *, gpointer );
57    TRMarshalErr     callback;
58    GCClosure      * cclosure = (GCClosure*) closure;
59    enum tr_core_err errcode;
60    const char     * errstr;
61    gpointer         inst, gdata;
62
63    g_return_if_fail( 3 == count );
64
65    inst    = g_value_peek_pointer( vals );
66    errcode = g_value_get_int( vals + 1 );
67    errstr  = g_value_get_string( vals + 2 );
68    gdata   = closure->data;
69
70    callback = (TRMarshalErr) ( NULL == marshal ?
71                                cclosure->callback : marshal );
72    callback( inst, errcode, errstr, gdata );
73}
74
75static void
76tr_core_marshal_prompt( GClosure * closure, GValue * ret UNUSED, guint count,
77                        const GValue * vals, gpointer hint UNUSED,
78                        gpointer marshal )
79{
80    typedef void (*TRMarshalPrompt)
81        ( gpointer, GList *, enum tr_torrent_action, gboolean, gpointer );
82    TRMarshalPrompt        callback;
83    GCClosure            * cclosure = (GCClosure*) closure;
84    GList                * paths;
85    enum tr_torrent_action action;
86    gboolean               paused;
87    gpointer               inst, gdata;
88
89    g_return_if_fail( 4 == count );
90
91    inst    = g_value_peek_pointer( vals );
92    paths   = g_value_get_pointer( vals + 1 );
93    action  = g_value_get_int( vals + 2 );
94    paused  = g_value_get_boolean( vals + 3 );
95    gdata   = closure->data;
96
97    callback = (TRMarshalPrompt) ( NULL == marshal ?
98                                   cclosure->callback : marshal );
99    callback( inst, paths, action, paused, gdata );
100}
101
102static void
103tr_core_marshal_data( GClosure * closure, GValue * ret UNUSED, guint count,
104                      const GValue * vals, gpointer hint UNUSED,
105                      gpointer marshal )
106{
107    typedef void (*TRMarshalPrompt)
108        ( gpointer, uint8_t *, size_t, gboolean, gpointer );
109    TRMarshalPrompt        callback;
110    GCClosure            * cclosure = (GCClosure*) closure;
111    uint8_t              * data;
112    size_t                 size;
113    gboolean               paused;
114    gpointer               inst, gdata;
115
116    g_return_if_fail( 4 == count );
117
118    inst    = g_value_peek_pointer( vals );
119    data    = (uint8_t *) g_value_get_string( vals + 1 );
120    size    = g_value_get_uint( vals + 2 );
121    paused  = g_value_get_boolean( vals + 3 );
122    gdata   = closure->data;
123
124    callback = (TRMarshalPrompt) ( NULL == marshal ?
125                                   cclosure->callback : marshal );
126    callback( inst, data, size, paused, gdata );
127}
128
129static int
130isDisposed( const TrCore * core )
131{
132    return !core || !core->priv;
133}
134
135static void
136tr_core_dispose( GObject * obj )
137{
138    TrCore * core = TR_CORE( obj );
139    GObjectClass * parent;
140
141    if( !isDisposed( core ) )
142    {
143        pref_save( NULL );
144        core->priv = NULL;
145    }
146
147    parent = g_type_class_peek( g_type_parent( TR_CORE_TYPE ) );
148    parent->dispose( obj );
149}
150
151
152static void
153tr_core_class_init( gpointer g_class, gpointer g_class_data UNUSED )
154{
155    GObjectClass * gobject_class;
156    TrCoreClass  * core_class;
157
158    gobject_class = G_OBJECT_CLASS( g_class );
159    gobject_class->dispose = tr_core_dispose;
160
161    g_type_class_add_private( g_class,
162                              sizeof(struct TrCorePrivate) );
163
164
165    core_class = TR_CORE_CLASS( g_class );
166    core_class->errsig = g_signal_new( "error", G_TYPE_FROM_CLASS( g_class ),
167                                       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
168                                       tr_core_marshal_err, G_TYPE_NONE,
169                                       2, G_TYPE_INT, G_TYPE_STRING );
170    core_class->promptsig = g_signal_new( "directory-prompt",
171                                          G_TYPE_FROM_CLASS( g_class ),
172                                          G_SIGNAL_RUN_LAST, 0, NULL, NULL,
173                                          tr_core_marshal_prompt, G_TYPE_NONE,
174                                          3, G_TYPE_POINTER, G_TYPE_INT,
175                                          G_TYPE_BOOLEAN );
176    core_class->promptdatasig = g_signal_new( "directory-prompt-data",
177                                              G_TYPE_FROM_CLASS( g_class ),
178                                              G_SIGNAL_RUN_LAST, 0, NULL, NULL,
179                                              tr_core_marshal_data,
180                                              G_TYPE_NONE, 3, G_TYPE_STRING,
181                                              G_TYPE_UINT, G_TYPE_BOOLEAN );
182    core_class->quitsig = g_signal_new( "quit", G_TYPE_FROM_CLASS( g_class ),
183                                        G_SIGNAL_RUN_LAST, 0, NULL, NULL,
184                                        g_cclosure_marshal_VOID__VOID,
185                                        G_TYPE_NONE, 0 );
186    core_class->prefsig = g_signal_new( "prefs-changed",
187                                        G_TYPE_FROM_CLASS( g_class ),
188                                        G_SIGNAL_RUN_LAST, 0, NULL, NULL,
189                                        g_cclosure_marshal_VOID__STRING,
190                                        G_TYPE_NONE, 1, G_TYPE_STRING );
191}
192
193static int
194compareDouble( double a, double b )
195{
196    if( a < b ) return -1;
197    if( b < a ) return 1;
198    return 0;
199}
200
201static int
202compareByActivity( GtkTreeModel * model,
203                   GtkTreeIter  * a,
204                   GtkTreeIter  * b,
205                   gpointer       user_data UNUSED )
206{
207    int i;
208    tr_torrent *ta, *tb;
209    const tr_stat *sa, *sb;
210
211    gtk_tree_model_get( model, a, MC_TORRENT_RAW, &ta, -1 );
212    gtk_tree_model_get( model, b, MC_TORRENT_RAW, &tb, -1 );
213
214    sa = tr_torrentStatCached( ta );
215    sb = tr_torrentStatCached( tb );
216
217    if(( i = compareDouble( sa->rateUpload + sa->rateDownload,
218                            sb->rateUpload + sb->rateDownload ) ))
219        return i;
220
221    if( sa->uploadedEver != sb->uploadedEver )
222        return sa->uploadedEver < sa->uploadedEver ? -1 : 1;
223
224    return 0;
225}
226
227static int
228compareByDateAdded( GtkTreeModel   * model UNUSED,
229                    GtkTreeIter    * a UNUSED,
230                    GtkTreeIter    * b UNUSED,
231                    gpointer         user_data UNUSED )
232{
233    return 0; /* FIXME */
234}
235
236static int
237compareByName( GtkTreeModel   * model,
238               GtkTreeIter    * a,
239               GtkTreeIter    * b,
240               gpointer         user_data UNUSED )
241{
242    int ret;
243    char *ca, *cb;
244    gtk_tree_model_get( model, a, MC_NAME_COLLATED, &ca, -1 );
245    gtk_tree_model_get( model, b, MC_NAME_COLLATED, &cb, -1 );
246    ret = strcmp( ca, cb );
247    g_free( cb );
248    g_free( ca );
249    return ret;
250}
251
252static int
253compareByProgress( GtkTreeModel   * model,
254                   GtkTreeIter    * a,
255                   GtkTreeIter    * b,
256                   gpointer         user_data UNUSED )
257{
258    int ret;
259    tr_torrent *ta, *tb;
260    const tr_stat *sa, *sb;
261    gtk_tree_model_get( model, a, MC_TORRENT_RAW, &ta, -1 );
262    gtk_tree_model_get( model, b, MC_TORRENT_RAW, &tb, -1 );
263    sa = tr_torrentStatCached( ta );
264    sb = tr_torrentStatCached( tb );
265    ret = compareDouble( sa->percentDone, sb->percentDone );
266    if( !ret )
267        ret = compareDouble( sa->ratio, sb->ratio );
268    return ret;
269}
270
271static int
272compareByState( GtkTreeModel   * model,
273                GtkTreeIter    * a,
274                GtkTreeIter    * b,
275                gpointer         user_data )
276{
277    int sa, sb, ret;
278
279    /* first by state */
280    gtk_tree_model_get( model, a, MC_STATUS, &sa, -1 );
281    gtk_tree_model_get( model, b, MC_STATUS, &sb, -1 );
282    ret = sa - sb;
283
284    /* second by progress */
285    if( !ret )
286        ret = compareByProgress( model, a, b, user_data );
287
288    return ret;
289}
290
291static int
292compareByTracker( GtkTreeModel   * model,
293                  GtkTreeIter    * a,
294                  GtkTreeIter    * b,
295                  gpointer         user_data UNUSED )
296{
297    const tr_torrent *ta, *tb;
298    gtk_tree_model_get( model, a, MC_TORRENT_RAW, &ta, -1 );
299    gtk_tree_model_get( model, b, MC_TORRENT_RAW, &tb, -1 );
300    return strcmp( tr_torrentInfo(ta)->primaryAddress,
301                   tr_torrentInfo(tb)->primaryAddress );
302}
303
304/***
305****
306***/
307
308
309static void
310setSort( TrCore * core, const char * mode, gboolean isReversed  )
311{
312    int col = MC_TORRENT_RAW;
313    GtkSortType type = isReversed ? GTK_SORT_ASCENDING : GTK_SORT_DESCENDING;
314    GtkTreeModel * model = tr_core_model( core );
315    GtkTreeSortable * sortable = GTK_TREE_SORTABLE( model );
316
317    if( !strcmp( mode, "sort-by-activity" ) )
318        gtk_tree_sortable_set_sort_func( sortable, col, compareByActivity, NULL, NULL );
319    else if( !strcmp( mode, "sort-by-date-added" ) )
320        gtk_tree_sortable_set_sort_func( sortable, col, compareByDateAdded, NULL, NULL );
321    else if( !strcmp( mode, "sort-by-progress" ) )
322        gtk_tree_sortable_set_sort_func( sortable, col, compareByProgress, NULL, NULL );
323    else if( !strcmp( mode, "sort-by-state" ) )
324        gtk_tree_sortable_set_sort_func( sortable, col, compareByState, NULL, NULL );
325    else if( !strcmp( mode, "sort-by-tracker" ) )
326        gtk_tree_sortable_set_sort_func( sortable, col, compareByTracker, NULL, NULL );
327    else {
328        type = isReversed ? GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;
329        gtk_tree_sortable_set_sort_func( sortable, col, compareByName, NULL, NULL );
330    }
331
332    gtk_tree_sortable_set_sort_column_id( sortable, col, type );
333}
334
335static void
336prefsChanged( TrCore * core, const char * key, gpointer data UNUSED )
337{
338    if( !strcmp( key, PREF_KEY_SORT_MODE ) || !strcmp( key, PREF_KEY_SORT_REVERSED ) )
339    {
340        char * mode = pref_string_get( PREF_KEY_SORT_MODE );
341        gboolean isReversed = pref_flag_get( PREF_KEY_SORT_REVERSED );
342        setSort( core, mode, isReversed );
343        g_free( mode );
344    }
345    else if( !strcmp( key, PREF_KEY_MAX_PEERS_GLOBAL ) )
346    {
347        const uint16_t val = pref_int_get( key );
348        tr_setGlobalPeerLimit( tr_core_handle( core ), val );
349    }
350}
351
352static void
353tr_core_init( GTypeInstance * instance, gpointer g_class UNUSED )
354{
355    tr_handle * h;
356    TrCore * self = (TrCore *) instance;
357    GtkListStore * store;
358    struct TrCorePrivate * p;
359
360    /* column types for the model used to store torrent information */
361    /* keep this in sync with the enum near the bottom of tr_core.h */
362    GType types[] = {
363        G_TYPE_STRING,    /* name */
364        G_TYPE_STRING,    /* collated name */
365        G_TYPE_STRING,    /* hash string */
366        TR_TORRENT_TYPE,  /* TrTorrent object */
367        G_TYPE_POINTER,   /* tr_torrent* */
368        G_TYPE_INT,       /* tr_stat()->status */
369        G_TYPE_INT        /* ID for IPC */
370    };
371
372    p = self->priv = G_TYPE_INSTANCE_GET_PRIVATE( self,
373                                                  TR_CORE_TYPE,
374                                                  struct TrCorePrivate );
375
376
377    h = tr_initFull( "gtk",
378                     pref_flag_get( PREF_KEY_PEX ),
379                     pref_flag_get( PREF_KEY_NAT ),
380                     pref_int_get( PREF_KEY_PORT ),
381                     pref_flag_get( PREF_KEY_ENCRYPTED_ONLY )
382                         ? TR_ENCRYPTION_REQUIRED
383                         : TR_ENCRYPTION_PREFERRED,
384                     pref_flag_get( PREF_KEY_UL_LIMIT_ENABLED ),
385                     pref_int_get( PREF_KEY_UL_LIMIT ),
386                     pref_flag_get( PREF_KEY_DL_LIMIT_ENABLED ),
387                     pref_int_get( PREF_KEY_DL_LIMIT ),
388                     pref_int_get( PREF_KEY_MAX_PEERS_GLOBAL ),
389                     pref_int_get( PREF_KEY_MSGLEVEL ),
390                     TRUE );
391
392    /* create the model used to store torrent data */
393    g_assert( ALEN( types ) == MC_ROW_COUNT );
394    store = gtk_list_store_newv( MC_ROW_COUNT, types );
395
396    p->model    = GTK_TREE_MODEL( store );
397    p->handle   = h;
398    p->nextid   = 1;
399    p->quitting = FALSE;
400}
401
402GType
403tr_core_get_type( void )
404{
405    static GType type = 0;
406
407    if( !type )
408    {
409        static const GTypeInfo info =
410        {
411            sizeof( TrCoreClass ),
412            NULL,                       /* base_init */
413            NULL,                       /* base_finalize */
414            tr_core_class_init,         /* class_init */
415            NULL,                       /* class_finalize */
416            NULL,                       /* class_data */
417            sizeof( TrCore ),
418            0,                          /* n_preallocs */
419            tr_core_init,               /* instance_init */
420            NULL,
421        };
422        type = g_type_register_static( G_TYPE_OBJECT, "TrCore", &info, 0 );
423    }
424
425    return type;
426}
427
428/**
429***
430**/
431
432TrCore *
433tr_core_new( void )
434{
435    TrCore * core = TR_CORE( g_object_new( TR_CORE_TYPE, NULL ) );
436
437    /* init from prefs & listen to pref changes */
438    prefsChanged( core, PREF_KEY_SORT_MODE, NULL );
439    prefsChanged( core, PREF_KEY_SORT_REVERSED, NULL );
440    prefsChanged( core, PREF_KEY_MAX_PEERS_GLOBAL, NULL );
441    g_signal_connect( core, "prefs-changed", G_CALLBACK(prefsChanged), NULL );
442
443    return core;
444}
445
446GtkTreeModel *
447tr_core_model( TrCore * core )
448{
449    return isDisposed( core ) ? NULL : core->priv->model;
450}
451
452tr_handle *
453tr_core_handle( TrCore * core )
454{
455    return isDisposed( core ) ? NULL : core->priv->handle;
456}
457
458
459const struct core_stats*
460tr_core_get_stats( const TrCore * core )
461{
462    return isDisposed( core ) ? NULL : &core->priv->stats;
463}
464
465static char*
466doCollate( const char * in )
467{
468    const char * end = in + strlen( in );
469    char * casefold;
470    char * ret;
471
472    while( in < end ) {
473        const gunichar ch = g_utf8_get_char( in );
474        if (!g_unichar_isalnum (ch)) // eat everything before the first alnum
475            in += g_unichar_to_utf8( ch, NULL );
476        else
477            break;
478    }
479
480    if ( in == end )
481        return g_strdup ("");
482
483    casefold = g_utf8_casefold( in, end-in );
484    ret = g_utf8_collate_key( casefold, -1 );
485    g_free( casefold );
486    return ret;
487}
488
489static void
490tr_core_insert( TrCore * self, TrTorrent * tor )
491{
492    const tr_info * inf = tr_torrent_info( tor );
493    const tr_stat * torStat = tr_torrent_stat( tor );
494    char * collated = doCollate( inf->name );
495    GtkListStore * store = GTK_LIST_STORE( tr_core_model( self ) );
496    GtkTreeIter unused;
497    gtk_list_store_insert_with_values( store, &unused, 0, 
498                                       MC_NAME,          inf->name,
499                                       MC_NAME_COLLATED, collated,
500                                       MC_HASH,          inf->hashString,
501                                       MC_TORRENT,       tor,
502                                       MC_TORRENT_RAW,   tor->handle,
503                                       MC_STATUS,        torStat->status,
504                                       MC_ID,            self->priv->nextid,
505                                       -1);
506    self->priv->nextid++;
507    g_object_unref( tor );
508    g_free( collated );
509}
510
511int
512tr_core_load( TrCore * self, gboolean forcePaused )
513{
514    int i;
515    int count = 0;
516    tr_torrent ** torrents;
517    char * path;
518    tr_ctor * ctor;
519
520    TR_IS_CORE( self );
521
522    path = getdownloaddir( );
523
524    ctor = tr_ctorNew( tr_core_handle( self ) );
525    if( forcePaused )
526        tr_ctorSetPaused( ctor, TR_FORCE, TRUE );
527    tr_ctorSetDestination( ctor, TR_FALLBACK, path );
528    tr_ctorSetMaxConnectedPeers( ctor, TR_FALLBACK, pref_int_get( PREF_KEY_MAX_PEERS_PER_TORRENT ) );
529
530    torrents = tr_loadTorrents ( tr_core_handle( self ), ctor, &count );
531    for( i=0; i<count; ++i )
532        tr_core_insert( self, tr_torrent_new_preexisting( torrents[i] ) );
533
534    tr_free( torrents );
535    tr_ctorFree( ctor );
536    g_free( path );
537
538    return count;
539}
540
541gboolean
542tr_core_add( TrCore * self, const char * path, enum tr_torrent_action act,
543             gboolean paused )
544{
545    GList * list;
546    int     ret;
547
548    TR_IS_CORE( self );
549
550    list = g_list_append( NULL, (void *) path );
551    ret  = tr_core_add_list( self, list, act, paused );
552    g_list_free( list );
553
554    return 1 == ret;
555}
556
557static void
558tr_core_errsig( TrCore * self, enum tr_core_err type, const char * msg )
559{
560    TrCoreClass * class;
561
562    class = g_type_class_peek( TR_CORE_TYPE );
563    g_signal_emit( self, class->errsig, 0, type, msg );
564}
565
566gboolean
567tr_core_add_dir( TrCore * self, const char * path, const char * dir,
568                 enum tr_torrent_action act, gboolean paused )
569{
570    TrTorrent * tor;
571    char      * errstr;
572
573    TR_IS_CORE( self );
574
575    errstr = NULL;
576    tor = tr_torrent_new( tr_core_handle( self ), path, dir, act, paused, &errstr );
577    if( NULL == tor )
578    {
579        tr_core_errsig( self, TR_CORE_ERR_ADD_TORRENT, errstr );
580        g_free( errstr );
581        return FALSE;
582    }
583    g_assert( NULL == errstr );
584
585    tr_core_insert( self, tor );
586
587    return TRUE;
588}
589
590int
591tr_core_add_list( TrCore * self, GList * paths, enum tr_torrent_action act,
592                  gboolean paused )
593{
594    char * dir;
595    int count;
596
597    TR_IS_CORE( self );
598
599    if( pref_flag_get( PREF_KEY_DIR_ASK ) )
600    {
601        TrCoreClass * class = g_type_class_peek( TR_CORE_TYPE );
602        g_signal_emit( self, class->promptsig, 0, paths, act, paused );
603        return 0;
604    }
605
606    dir = getdownloaddir();
607    count = 0;
608    for( ; paths; paths=paths->next )
609        if( tr_core_add_dir( self, paths->data, dir, act, paused ) )
610            count++;
611
612    g_free( dir );
613    return count;
614}
615
616gboolean
617tr_core_add_data( TrCore * self, uint8_t * data, size_t size, gboolean paused )
618{
619    gboolean ret;
620    char * path;
621    TR_IS_CORE( self );
622
623    if( pref_flag_get( PREF_KEY_DIR_ASK ) )
624    {
625        TrCoreClass * class = g_type_class_peek( TR_CORE_TYPE );
626        g_signal_emit( self, class->promptdatasig, 0, data, size, paused );
627        return FALSE;
628    }
629
630    path = getdownloaddir( );
631    ret = tr_core_add_data_dir( self, data, size, path, paused );
632    g_free( path );
633    return ret;
634}
635
636gboolean
637tr_core_add_data_dir( TrCore * self, uint8_t * data, size_t size,
638                      const char * dir, gboolean paused )
639{
640    TrTorrent * tor;
641    char      * errstr = NULL;
642
643    TR_IS_CORE( self );
644
645    tor = tr_torrent_new_with_data( tr_core_handle( self ), data, size, dir,
646                                    paused, &errstr );
647    if( NULL == tor )
648    {
649        tr_core_errsig( self, TR_CORE_ERR_ADD_TORRENT, errstr );
650        g_free( errstr );
651        return FALSE;
652    }
653    g_assert( NULL == errstr );
654
655    tr_core_insert( self, tor );
656
657    return TRUE;
658}
659
660void
661tr_core_torrents_added( TrCore * self )
662{
663    TR_IS_CORE( self );
664
665    tr_core_update( self );
666    tr_core_errsig( self, TR_CORE_ERR_NO_MORE_TORRENTS, NULL );
667}
668
669void
670tr_core_delete_torrent( TrCore * self, GtkTreeIter * iter )
671{
672    TrTorrent * tor;
673    GtkTreeModel * model = tr_core_model( self );
674
675    TR_IS_CORE( self );
676
677    gtk_tree_model_get( model, iter, MC_TORRENT, &tor, -1 );
678    gtk_list_store_remove( GTK_LIST_STORE( model ), iter );
679    tr_torrentRemoveSaved( tr_torrent_handle( tor ) );
680
681    g_object_unref( G_OBJECT( tor ) );
682}
683
684/***
685****
686***/
687
688static gboolean
689update_foreach( GtkTreeModel * model,
690                GtkTreePath  * path UNUSED,
691                GtkTreeIter  * iter,
692                gpointer       data )
693{
694    TrTorrent * gtor;
695    int oldStatus;
696    const tr_stat * torStat;
697    struct core_stats * stats = data;
698
699    gtk_tree_model_get( model, iter, MC_TORRENT, &gtor,
700                                     MC_STATUS, &oldStatus,
701                                     -1 );
702
703    torStat = tr_torrent_stat( gtor );
704
705    /* sum the torrents' cumulative stats... */
706    if( torStat->status == TR_STATUS_DOWNLOAD )
707        ++stats->downloadCount;
708    else if( torStat->status == TR_STATUS_SEED )
709        ++stats->seedingCount;
710    stats->clientDownloadSpeed += torStat->rateDownload;
711    stats->clientUploadSpeed += torStat->rateUpload;
712
713    /* update the model's status if necessary */
714    if( oldStatus != (int) torStat->status )
715        gtk_list_store_set( GTK_LIST_STORE( model ), iter,
716                            MC_STATUS, torStat->status,
717                            -1 );
718
719    tr_torrent_check_seeding_cap ( gtor );
720
721    g_object_unref( gtor );
722    return FALSE;
723}
724
725void
726tr_core_update( TrCore * self )
727{
728    int column;
729    GtkSortType order;
730    GtkTreeSortable * sortable;
731    GtkTreeModel * model = tr_core_model( self );
732
733    /* pause sorting */
734    sortable = GTK_TREE_SORTABLE( model );
735    gtk_tree_sortable_get_sort_column_id( sortable, &column, &order );
736    gtk_tree_sortable_set_sort_column_id( sortable, GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, order );
737
738    /* refresh the model */
739    memset( &self->priv->stats, 0, sizeof( struct core_stats ) );
740    gtk_tree_model_foreach( model, update_foreach, &self->priv->stats );
741
742    /* resume sorting */
743    gtk_tree_sortable_set_sort_column_id( sortable, column, order );
744}
745
746void
747tr_core_quit( TrCore * self )
748{
749    TrCoreClass * class;
750
751    TR_IS_CORE( self );
752
753    class = g_type_class_peek( TR_CORE_TYPE );
754    g_signal_emit( self, class->quitsig, 0 );
755}
756
757/**
758***  Prefs
759**/
760
761static void
762commitPrefsChange( TrCore * self, const char * key )
763{
764    TrCoreClass * class = g_type_class_peek( TR_CORE_TYPE );
765    pref_save( NULL );
766    g_signal_emit( self, class->prefsig, 0, key );
767}
768
769void
770tr_core_set_pref( TrCore * self, const char * key, const char * newval )
771{
772    char * oldval = pref_string_get( key );
773    if( tr_strcmp( oldval, newval ) )
774    {
775        pref_string_set( key, newval );
776        commitPrefsChange( self, key );
777    }
778    g_free( oldval );
779}
780
781void
782tr_core_set_pref_bool( TrCore * self, const char * key, gboolean newval )
783{
784    const gboolean oldval = pref_flag_get( key );
785    if( oldval != newval )
786    {
787        pref_flag_set( key, newval );
788        commitPrefsChange( self, key );
789    }
790}
791
792void
793tr_core_set_pref_int( TrCore * self, const char * key, int newval )
794{
795    const int oldval = pref_int_get( key );
796    if( oldval != newval )
797    {
798        pref_int_set( key, newval );
799        commitPrefsChange( self, key );
800    }
801}
Note: See TracBrowser for help on using the repository browser.