source: trunk/gtk/tr_core.c @ 5015

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

(gtk) new "add torrent" popup to let users choose which files to download, file priority, add paused, delete source .torrent, etc

  • Property svn:keywords set to Date Rev Author Id
File size: 21.0 KB
Line 
1/******************************************************************************
2 * $Id: tr_core.c 5015 2008-02-13 03:00:21Z 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)( gpointer, GList *, gpointer, gpointer );
81    TRMarshalPrompt        callback;
82    GCClosure            * cclosure = (GCClosure*) closure;
83    GList                * paths;
84    gpointer               ctor;
85    gpointer               inst, gdata;
86
87    g_return_if_fail( 3 == count );
88
89    inst      = g_value_peek_pointer( vals );
90    paths     = g_value_get_pointer( vals + 1 );
91    ctor      = g_value_get_pointer( vals + 2 );
92    gdata     = closure->data;
93
94    callback = (TRMarshalPrompt) ( NULL == marshal ?
95                                   cclosure->callback : marshal );
96    callback( inst, paths, ctor, gdata );
97}
98
99static int
100isDisposed( const TrCore * core )
101{
102    return !core || !core->priv;
103}
104
105static void
106tr_core_dispose( GObject * obj )
107{
108    TrCore * core = TR_CORE( obj );
109    GObjectClass * parent;
110
111    if( !isDisposed( core ) )
112    {
113        pref_save( NULL );
114        core->priv = NULL;
115    }
116
117    parent = g_type_class_peek( g_type_parent( TR_CORE_TYPE ) );
118    parent->dispose( obj );
119}
120
121
122static void
123tr_core_class_init( gpointer g_class, gpointer g_class_data UNUSED )
124{
125    GObjectClass * gobject_class;
126    TrCoreClass  * core_class;
127
128    gobject_class = G_OBJECT_CLASS( g_class );
129    gobject_class->dispose = tr_core_dispose;
130
131    g_type_class_add_private( g_class,
132                              sizeof(struct TrCorePrivate) );
133
134
135    core_class = TR_CORE_CLASS( g_class );
136    core_class->errsig = g_signal_new( "error", G_TYPE_FROM_CLASS( g_class ),
137                                       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
138                                       tr_core_marshal_err, G_TYPE_NONE,
139                                       2, G_TYPE_INT, G_TYPE_STRING );
140    core_class->promptsig = g_signal_new( "directory-prompt",
141                                          G_TYPE_FROM_CLASS( g_class ),
142                                          G_SIGNAL_RUN_LAST, 0, NULL, NULL,
143                                          tr_core_marshal_prompt, G_TYPE_NONE,
144                                          2, G_TYPE_POINTER, G_TYPE_POINTER );
145    core_class->quitsig = g_signal_new( "quit", G_TYPE_FROM_CLASS( g_class ),
146                                        G_SIGNAL_RUN_LAST, 0, NULL, NULL,
147                                        g_cclosure_marshal_VOID__VOID,
148                                        G_TYPE_NONE, 0 );
149    core_class->prefsig = g_signal_new( "prefs-changed",
150                                        G_TYPE_FROM_CLASS( g_class ),
151                                        G_SIGNAL_RUN_LAST, 0, NULL, NULL,
152                                        g_cclosure_marshal_VOID__STRING,
153                                        G_TYPE_NONE, 1, G_TYPE_STRING );
154}
155
156static int
157compareDouble( double a, double b )
158{
159    if( a < b ) return -1;
160    if( b < a ) return 1;
161    return 0;
162}
163
164static int
165compareByActivity( GtkTreeModel * model,
166                   GtkTreeIter  * a,
167                   GtkTreeIter  * b,
168                   gpointer       user_data UNUSED )
169{
170    int i;
171    tr_torrent *ta, *tb;
172    const tr_stat *sa, *sb;
173
174    gtk_tree_model_get( model, a, MC_TORRENT_RAW, &ta, -1 );
175    gtk_tree_model_get( model, b, MC_TORRENT_RAW, &tb, -1 );
176
177    sa = tr_torrentStatCached( ta );
178    sb = tr_torrentStatCached( tb );
179
180    if(( i = compareDouble( sa->rateUpload + sa->rateDownload,
181                            sb->rateUpload + sb->rateDownload ) ))
182        return i;
183
184    if( sa->uploadedEver != sb->uploadedEver )
185        return sa->uploadedEver < sa->uploadedEver ? -1 : 1;
186
187    return 0;
188}
189
190static int
191compareByDateAdded( GtkTreeModel   * model UNUSED,
192                    GtkTreeIter    * a UNUSED,
193                    GtkTreeIter    * b UNUSED,
194                    gpointer         user_data UNUSED )
195{
196    return 0; /* FIXME */
197}
198
199static int
200compareByName( GtkTreeModel   * model,
201               GtkTreeIter    * a,
202               GtkTreeIter    * b,
203               gpointer         user_data UNUSED )
204{
205    int ret;
206    char *ca, *cb;
207    gtk_tree_model_get( model, a, MC_NAME_COLLATED, &ca, -1 );
208    gtk_tree_model_get( model, b, MC_NAME_COLLATED, &cb, -1 );
209    ret = strcmp( ca, cb );
210    g_free( cb );
211    g_free( ca );
212    return ret;
213}
214
215static int
216compareByProgress( GtkTreeModel   * model,
217                   GtkTreeIter    * a,
218                   GtkTreeIter    * b,
219                   gpointer         user_data UNUSED )
220{
221    int ret;
222    tr_torrent *ta, *tb;
223    const tr_stat *sa, *sb;
224    gtk_tree_model_get( model, a, MC_TORRENT_RAW, &ta, -1 );
225    gtk_tree_model_get( model, b, MC_TORRENT_RAW, &tb, -1 );
226    sa = tr_torrentStatCached( ta );
227    sb = tr_torrentStatCached( tb );
228    ret = compareDouble( sa->percentDone, sb->percentDone );
229    if( !ret )
230        ret = compareDouble( sa->ratio, sb->ratio );
231    return ret;
232}
233
234static int
235compareByState( GtkTreeModel   * model,
236                GtkTreeIter    * a,
237                GtkTreeIter    * b,
238                gpointer         user_data )
239{
240    int sa, sb, ret;
241
242    /* first by state */
243    gtk_tree_model_get( model, a, MC_STATUS, &sa, -1 );
244    gtk_tree_model_get( model, b, MC_STATUS, &sb, -1 );
245    ret = sa - sb;
246
247    /* second by progress */
248    if( !ret )
249        ret = compareByProgress( model, a, b, user_data );
250
251    return ret;
252}
253
254static int
255compareByTracker( GtkTreeModel   * model,
256                  GtkTreeIter    * a,
257                  GtkTreeIter    * b,
258                  gpointer         user_data UNUSED )
259{
260    const tr_torrent *ta, *tb;
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    return strcmp( tr_torrentInfo(ta)->primaryAddress,
264                   tr_torrentInfo(tb)->primaryAddress );
265}
266
267/***
268****
269***/
270
271
272static void
273setSort( TrCore * core, const char * mode, gboolean isReversed  )
274{
275    int col = MC_TORRENT_RAW;
276    GtkSortType type = isReversed ? GTK_SORT_ASCENDING : GTK_SORT_DESCENDING;
277    GtkTreeModel * model = tr_core_model( core );
278    GtkTreeSortable * sortable = GTK_TREE_SORTABLE( model );
279
280    if( !strcmp( mode, "sort-by-activity" ) )
281        gtk_tree_sortable_set_sort_func( sortable, col, compareByActivity, NULL, NULL );
282    else if( !strcmp( mode, "sort-by-date-added" ) )
283        gtk_tree_sortable_set_sort_func( sortable, col, compareByDateAdded, NULL, NULL );
284    else if( !strcmp( mode, "sort-by-progress" ) )
285        gtk_tree_sortable_set_sort_func( sortable, col, compareByProgress, NULL, NULL );
286    else if( !strcmp( mode, "sort-by-state" ) )
287        gtk_tree_sortable_set_sort_func( sortable, col, compareByState, NULL, NULL );
288    else if( !strcmp( mode, "sort-by-tracker" ) )
289        gtk_tree_sortable_set_sort_func( sortable, col, compareByTracker, NULL, NULL );
290    else {
291        type = isReversed ? GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;
292        gtk_tree_sortable_set_sort_func( sortable, col, compareByName, NULL, NULL );
293    }
294
295    gtk_tree_sortable_set_sort_column_id( sortable, col, type );
296}
297
298static void
299prefsChanged( TrCore * core, const char * key, gpointer data UNUSED )
300{
301    if( !strcmp( key, PREF_KEY_SORT_MODE ) || !strcmp( key, PREF_KEY_SORT_REVERSED ) )
302    {
303        char * mode = pref_string_get( PREF_KEY_SORT_MODE );
304        gboolean isReversed = pref_flag_get( PREF_KEY_SORT_REVERSED );
305        setSort( core, mode, isReversed );
306        g_free( mode );
307    }
308    else if( !strcmp( key, PREF_KEY_MAX_PEERS_GLOBAL ) )
309    {
310        const uint16_t val = pref_int_get( key );
311        tr_setGlobalPeerLimit( tr_core_handle( core ), val );
312    }
313}
314
315static void
316tr_core_init( GTypeInstance * instance, gpointer g_class UNUSED )
317{
318    tr_handle * h;
319    TrCore * self = (TrCore *) instance;
320    GtkListStore * store;
321    struct TrCorePrivate * p;
322
323    /* column types for the model used to store torrent information */
324    /* keep this in sync with the enum near the bottom of tr_core.h */
325    GType types[] = {
326        G_TYPE_STRING,    /* name */
327        G_TYPE_STRING,    /* collated name */
328        G_TYPE_STRING,    /* hash string */
329        TR_TORRENT_TYPE,  /* TrTorrent object */
330        G_TYPE_POINTER,   /* tr_torrent* */
331        G_TYPE_INT,       /* tr_stat()->status */
332        G_TYPE_INT        /* ID for IPC */
333    };
334
335    p = self->priv = G_TYPE_INSTANCE_GET_PRIVATE( self,
336                                                  TR_CORE_TYPE,
337                                                  struct TrCorePrivate );
338
339
340    h = tr_initFull( "gtk",
341                     pref_flag_get( PREF_KEY_PEX ),
342                     pref_flag_get( PREF_KEY_NAT ),
343                     pref_int_get( PREF_KEY_PORT ),
344                     pref_flag_get( PREF_KEY_ENCRYPTED_ONLY )
345                         ? TR_ENCRYPTION_REQUIRED
346                         : TR_ENCRYPTION_PREFERRED,
347                     pref_flag_get( PREF_KEY_UL_LIMIT_ENABLED ),
348                     pref_int_get( PREF_KEY_UL_LIMIT ),
349                     pref_flag_get( PREF_KEY_DL_LIMIT_ENABLED ),
350                     pref_int_get( PREF_KEY_DL_LIMIT ),
351                     pref_int_get( PREF_KEY_MAX_PEERS_GLOBAL ),
352                     pref_int_get( PREF_KEY_MSGLEVEL ),
353                     TRUE );
354
355    /* create the model used to store torrent data */
356    g_assert( ALEN( types ) == MC_ROW_COUNT );
357    store = gtk_list_store_newv( MC_ROW_COUNT, types );
358
359    p->model    = GTK_TREE_MODEL( store );
360    p->handle   = h;
361    p->nextid   = 1;
362    p->quitting = FALSE;
363}
364
365GType
366tr_core_get_type( void )
367{
368    static GType type = 0;
369
370    if( !type )
371    {
372        static const GTypeInfo info =
373        {
374            sizeof( TrCoreClass ),
375            NULL,                       /* base_init */
376            NULL,                       /* base_finalize */
377            tr_core_class_init,         /* class_init */
378            NULL,                       /* class_finalize */
379            NULL,                       /* class_data */
380            sizeof( TrCore ),
381            0,                          /* n_preallocs */
382            tr_core_init,               /* instance_init */
383            NULL,
384        };
385        type = g_type_register_static( G_TYPE_OBJECT, "TrCore", &info, 0 );
386    }
387
388    return type;
389}
390
391/**
392***
393**/
394
395TrCore *
396tr_core_new( void )
397{
398    TrCore * core = TR_CORE( g_object_new( TR_CORE_TYPE, NULL ) );
399
400    /* init from prefs & listen to pref changes */
401    prefsChanged( core, PREF_KEY_SORT_MODE, NULL );
402    prefsChanged( core, PREF_KEY_SORT_REVERSED, NULL );
403    prefsChanged( core, PREF_KEY_MAX_PEERS_GLOBAL, NULL );
404    g_signal_connect( core, "prefs-changed", G_CALLBACK(prefsChanged), NULL );
405
406    return core;
407}
408
409GtkTreeModel *
410tr_core_model( TrCore * core )
411{
412    return isDisposed( core ) ? NULL : core->priv->model;
413}
414
415tr_handle *
416tr_core_handle( TrCore * core )
417{
418    return isDisposed( core ) ? NULL : core->priv->handle;
419}
420
421
422const struct core_stats*
423tr_core_get_stats( const TrCore * core )
424{
425    return isDisposed( core ) ? NULL : &core->priv->stats;
426}
427
428static char*
429doCollate( const char * in )
430{
431    const char * end = in + strlen( in );
432    char * casefold;
433    char * ret;
434
435    while( in < end ) {
436        const gunichar ch = g_utf8_get_char( in );
437        if (!g_unichar_isalnum (ch)) // eat everything before the first alnum
438            in += g_unichar_to_utf8( ch, NULL );
439        else
440            break;
441    }
442
443    if ( in == end )
444        return g_strdup ("");
445
446    casefold = g_utf8_casefold( in, end-in );
447    ret = g_utf8_collate_key( casefold, -1 );
448    g_free( casefold );
449    return ret;
450}
451
452void
453tr_core_add_torrent( TrCore * self, TrTorrent * tor )
454{
455    const tr_info * inf = tr_torrent_info( tor );
456    const tr_stat * torStat = tr_torrent_stat( tor );
457    char * collated = doCollate( inf->name );
458    GtkListStore * store = GTK_LIST_STORE( tr_core_model( self ) );
459    GtkTreeIter unused;
460
461    gtk_list_store_insert_with_values( store, &unused, 0, 
462                                       MC_NAME,          inf->name,
463                                       MC_NAME_COLLATED, collated,
464                                       MC_HASH,          inf->hashString,
465                                       MC_TORRENT,       tor,
466                                       MC_TORRENT_RAW,   tr_torrent_handle( tor ),
467                                       MC_STATUS,        torStat->status,
468                                       MC_ID,            self->priv->nextid,
469                                       -1);
470    ++self->priv->nextid;
471
472    /* cleanup */
473    g_object_unref( G_OBJECT( tor ) );
474    g_free( collated );
475}
476
477int
478tr_core_load( TrCore * self, gboolean forcePaused )
479{
480    int i;
481    int count = 0;
482    tr_torrent ** torrents;
483    char * path;
484    tr_ctor * ctor;
485
486    TR_IS_CORE( self );
487
488    path = getdownloaddir( );
489
490    ctor = tr_ctorNew( tr_core_handle( self ) );
491    if( forcePaused )
492        tr_ctorSetPaused( ctor, TR_FORCE, TRUE );
493    tr_ctorSetDestination( ctor, TR_FALLBACK, path );
494    tr_ctorSetMaxConnectedPeers( ctor, TR_FALLBACK, pref_int_get( PREF_KEY_MAX_PEERS_PER_TORRENT ) );
495
496    torrents = tr_loadTorrents ( tr_core_handle( self ), ctor, &count );
497    for( i=0; i<count; ++i )
498        tr_core_add_torrent( self, tr_torrent_new_preexisting( torrents[i] ) );
499
500    tr_free( torrents );
501    tr_ctorFree( ctor );
502    g_free( path );
503
504    return count;
505}
506
507static void
508tr_core_errsig( TrCore * self, enum tr_core_err type, const char * msg )
509{
510    TrCoreClass * class;
511
512    class = g_type_class_peek( TR_CORE_TYPE );
513    g_signal_emit( self, class->errsig, 0, type, msg );
514}
515
516static void
517tr_core_apply_defaults( tr_ctor * ctor )
518{
519    if( tr_ctorGetPaused( ctor, TR_FORCE, NULL ) )
520        tr_ctorSetPaused( ctor, TR_FORCE, !pref_flag_get( PREF_KEY_START ) );
521
522    if( tr_ctorGetDeleteSource( ctor, NULL ) )
523        tr_ctorSetDeleteSource( ctor, pref_flag_get( PREF_KEY_DELETE_ORIGINAL ) );
524
525    if( tr_ctorGetMaxConnectedPeers( ctor, TR_FORCE, NULL ) )
526        tr_ctorSetMaxConnectedPeers( ctor, TR_FORCE, pref_int_get( PREF_KEY_MAX_PEERS_PER_TORRENT ) );
527
528    if( tr_ctorGetDestination( ctor, TR_FORCE, NULL ) ) {
529        char * path = pref_string_get( PREF_KEY_DIR_DEFAULT );
530        tr_ctorSetDestination( ctor, TR_FORCE, path );
531        g_free( path );
532    }
533}
534
535void
536tr_core_add_ctor( TrCore * self, tr_ctor * ctor )
537{
538    TrTorrent * tor;
539    char      * errstr;
540
541    errstr = NULL;
542
543    tr_core_apply_defaults( ctor );
544    tor = tr_torrent_new_ctor( tr_core_handle( self ), ctor, &errstr );
545    if( !tor )
546    {
547        tr_core_errsig( self, TR_CORE_ERR_ADD_TORRENT, errstr );
548        g_free( errstr );
549        errstr = NULL;
550    }
551    else
552    {
553        g_assert( !errstr );
554        tr_core_add_torrent( self, tor );
555    }
556}
557
558void
559tr_core_add_list( TrCore   * self,
560                  GList    * paths,
561                  tr_ctor  * ctor )
562{
563    tr_core_apply_defaults( ctor );
564
565    if( pref_flag_get( PREF_KEY_OPTIONS_PROMPT ) )
566    {
567        TrCoreClass * class = g_type_class_peek( TR_CORE_TYPE );
568        g_signal_emit( self, class->promptsig, 0, paths, ctor );
569    }
570    else
571    {
572        for( ; paths; paths=paths->next )
573            if( !tr_ctorSetMetainfoFromFile( ctor, paths->data ) )
574                tr_core_add_ctor( self, ctor );
575        tr_ctorFree( ctor );
576    }
577}
578
579void
580tr_core_torrents_added( TrCore * self )
581{
582    tr_core_update( self );
583    tr_core_errsig( self, TR_CORE_ERR_NO_MORE_TORRENTS, NULL );
584}
585
586void
587tr_core_delete_torrent( TrCore * self, GtkTreeIter * iter )
588{
589    TrTorrent * tor;
590    GtkTreeModel * model = tr_core_model( self );
591
592    gtk_tree_model_get( model, iter, MC_TORRENT, &tor, -1 );
593    gtk_list_store_remove( GTK_LIST_STORE( model ), iter );
594    tr_torrentRemoveSaved( tr_torrent_handle( tor ) );
595
596    g_object_unref( G_OBJECT( tor ) );
597}
598
599/***
600****
601***/
602
603static gboolean
604update_foreach( GtkTreeModel * model,
605                GtkTreePath  * path UNUSED,
606                GtkTreeIter  * iter,
607                gpointer       data )
608{
609    TrTorrent * gtor;
610    int oldStatus;
611    const tr_stat * torStat;
612    struct core_stats * stats = data;
613
614    gtk_tree_model_get( model, iter, MC_TORRENT, &gtor,
615                                     MC_STATUS, &oldStatus,
616                                     -1 );
617
618    torStat = tr_torrent_stat( gtor );
619
620    /* sum the torrents' cumulative stats... */
621    if( torStat->status == TR_STATUS_DOWNLOAD )
622        ++stats->downloadCount;
623    else if( torStat->status == TR_STATUS_SEED )
624        ++stats->seedingCount;
625    stats->clientDownloadSpeed += torStat->rateDownload;
626    stats->clientUploadSpeed += torStat->rateUpload;
627
628    /* update the model's status if necessary */
629    if( oldStatus != (int) torStat->status )
630        gtk_list_store_set( GTK_LIST_STORE( model ), iter,
631                            MC_STATUS, torStat->status,
632                            -1 );
633
634    tr_torrent_check_seeding_cap ( gtor );
635
636    g_object_unref( gtor );
637    return FALSE;
638}
639
640void
641tr_core_update( TrCore * self )
642{
643    int column;
644    GtkSortType order;
645    GtkTreeSortable * sortable;
646    GtkTreeModel * model = tr_core_model( self );
647
648    /* pause sorting */
649    sortable = GTK_TREE_SORTABLE( model );
650    gtk_tree_sortable_get_sort_column_id( sortable, &column, &order );
651    gtk_tree_sortable_set_sort_column_id( sortable, GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, order );
652
653    /* refresh the model */
654    memset( &self->priv->stats, 0, sizeof( struct core_stats ) );
655    gtk_tree_model_foreach( model, update_foreach, &self->priv->stats );
656
657    /* resume sorting */
658    gtk_tree_sortable_set_sort_column_id( sortable, column, order );
659}
660
661void
662tr_core_quit( TrCore * self )
663{
664    TrCoreClass * class = g_type_class_peek( TR_CORE_TYPE );
665    g_signal_emit( self, class->quitsig, 0 );
666}
667
668/**
669***  Prefs
670**/
671
672static void
673commitPrefsChange( TrCore * self, const char * key )
674{
675    TrCoreClass * class = g_type_class_peek( TR_CORE_TYPE );
676    pref_save( NULL );
677    g_signal_emit( self, class->prefsig, 0, key );
678}
679
680void
681tr_core_set_pref( TrCore * self, const char * key, const char * newval )
682{
683    char * oldval = pref_string_get( key );
684    if( tr_strcmp( oldval, newval ) )
685    {
686        pref_string_set( key, newval );
687        commitPrefsChange( self, key );
688    }
689    g_free( oldval );
690}
691
692void
693tr_core_set_pref_bool( TrCore * self, const char * key, gboolean newval )
694{
695    const gboolean oldval = pref_flag_get( key );
696    if( oldval != newval )
697    {
698        pref_flag_set( key, newval );
699        commitPrefsChange( self, key );
700    }
701}
702
703void
704tr_core_set_pref_int( TrCore * self, const char * key, int newval )
705{
706    const int oldval = pref_int_get( key );
707    if( oldval != newval )
708    {
709        pref_int_set( key, newval );
710        commitPrefsChange( self, key );
711    }
712}
Note: See TracBrowser for help on using the repository browser.