source: trunk/gtk/tr_core.c @ 4215

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

(gtk) add sort-by-ratio

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