source: trunk/gtk/tr-core.c @ 6162

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

unify the daemon and gtk client's config files so that you can easily swap back and forth between clients and keep the same torrents and preferences.

  • Property svn:keywords set to Date Rev Author Id
File size: 30.7 KB
Line 
1/******************************************************************************
2 * $Id: tr-core.c 6162 2008-06-12 16:25:36Z 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 <string.h> /* strcmp, strlen */
26
27#include <gtk/gtk.h>
28#include <glib/gi18n.h>
29#ifdef HAVE_GIO
30#include <gio/gio.h>
31#endif
32#ifdef HAVE_DBUS_GLIB
33#include <dbus/dbus-glib.h>
34#endif
35
36#include <libtransmission/transmission.h>
37#include <libtransmission/utils.h> /* tr_free */
38
39#include "conf.h"
40#include "tr-core.h"
41#ifdef HAVE_DBUS_GLIB
42#include "tr-core-dbus.h"
43#endif
44#include "tr-prefs.h"
45#include "tr-torrent.h"
46#include "util.h"
47
48static void tr_core_set_hibernation_allowed( TrCore * core, gboolean allowed );
49
50static gboolean our_instance_adds_remote_torrents = FALSE;
51
52struct TrCorePrivate
53{
54#ifdef HAVE_GIO
55    GFileMonitor     * monitor;
56    gulong             monitor_tag;
57    char             * monitor_path;
58    GSList           * monitor_files;
59    guint              monitor_idle_tag;
60#endif
61    gboolean           inhibit_allowed;
62    gboolean           have_inhibit_cookie;
63    guint              inhibit_cookie;
64    GtkTreeModel     * model;
65    tr_handle        * handle;
66};
67
68static void
69tr_core_marshal_err( GClosure * closure, GValue * ret UNUSED,
70                     guint count, const GValue * vals,
71                     gpointer hint UNUSED, gpointer marshal )
72{
73    typedef void (*TRMarshalErr)
74        ( gpointer, enum tr_core_err, const char *, gpointer );
75    TRMarshalErr     callback;
76    GCClosure      * cclosure = (GCClosure*) closure;
77    enum tr_core_err errcode;
78    const char     * errstr;
79    gpointer         inst, gdata;
80
81    g_return_if_fail( count == 3 );
82
83    inst    = g_value_peek_pointer( vals );
84    errcode = g_value_get_int( vals + 1 );
85    errstr  = g_value_get_string( vals + 2 );
86    gdata   = closure->data;
87
88    callback = (TRMarshalErr)( marshal ? marshal : cclosure->callback );
89    callback( inst, errcode, errstr, gdata );
90}
91
92static void
93tr_core_marshal_prompt( GClosure * closure, GValue * ret UNUSED,
94                        guint count, const GValue * vals,
95                        gpointer hint UNUSED, gpointer marshal )
96{
97    typedef void (*TRMarshalPrompt)( gpointer, tr_ctor *, gpointer );
98    TRMarshalPrompt        callback;
99    GCClosure            * cclosure = (GCClosure*) closure;
100    gpointer               ctor;
101    gpointer               inst, gdata;
102
103    g_return_if_fail( count == 2 );
104
105    inst      = g_value_peek_pointer( vals );
106    ctor      = g_value_peek_pointer( vals + 1 );
107    gdata     = closure->data;
108
109    callback = (TRMarshalPrompt)( marshal ? marshal : cclosure->callback );
110    callback( inst, ctor, gdata );
111}
112
113static int
114isDisposed( const TrCore * core )
115{
116    return !core || !core->priv;
117}
118
119static void
120tr_core_dispose( GObject * obj )
121{
122    TrCore * core = TR_CORE( obj );
123
124    if( !isDisposed( core ) )
125    {
126        GObjectClass * parent;
127
128        pref_save( );
129        core->priv = NULL;
130
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 UNUSED )
138{
139    GObjectClass * gobject_class;
140    TrCoreClass  * core_class;
141
142    g_type_class_add_private( g_class, sizeof(struct TrCorePrivate) );
143
144    gobject_class = G_OBJECT_CLASS( g_class );
145    gobject_class->dispose = tr_core_dispose;
146
147
148    core_class = TR_CORE_CLASS( g_class );
149    core_class->errsig = g_signal_new( "error", G_TYPE_FROM_CLASS( g_class ),
150                                       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
151                                       tr_core_marshal_err, G_TYPE_NONE,
152                                       2, G_TYPE_INT, G_TYPE_STRING );
153    core_class->promptsig = g_signal_new( "add-torrent-prompt",
154                                          G_TYPE_FROM_CLASS( g_class ),
155                                          G_SIGNAL_RUN_LAST, 0, NULL, NULL,
156                                          tr_core_marshal_prompt, G_TYPE_NONE,
157                                          1, G_TYPE_POINTER );
158    core_class->quitsig = g_signal_new( "quit", G_TYPE_FROM_CLASS( g_class ),
159                                        G_SIGNAL_RUN_LAST, 0, NULL, NULL,
160                                        g_cclosure_marshal_VOID__VOID,
161                                        G_TYPE_NONE, 0 );
162    core_class->prefsig = g_signal_new( "prefs-changed",
163                                        G_TYPE_FROM_CLASS( g_class ),
164                                        G_SIGNAL_RUN_LAST, 0, NULL, NULL,
165                                        g_cclosure_marshal_VOID__STRING,
166                                        G_TYPE_NONE, 1, G_TYPE_STRING );
167
168#ifdef HAVE_DBUS_GLIB
169    {
170        DBusGConnection * bus = dbus_g_bus_get( DBUS_BUS_SESSION, NULL );
171        DBusGProxy * bus_proxy = NULL;
172        if( bus )
173            bus_proxy = dbus_g_proxy_new_for_name( bus, "org.freedesktop.DBus",
174                                                        "/org/freedesktop/DBus",
175                                                        "org.freedesktop.DBus" );
176        if( bus_proxy ) {
177            int result = 0;
178            dbus_g_proxy_call( bus_proxy, "RequestName", NULL,
179                               G_TYPE_STRING, "com.transmissionbt.Transmission",
180                               G_TYPE_UINT, 0,
181                               G_TYPE_INVALID,
182                               G_TYPE_UINT, &result,
183                               G_TYPE_INVALID );
184            if(( our_instance_adds_remote_torrents = result == 1 ))
185                dbus_g_object_type_install_info( TR_CORE_TYPE,
186                                                 &dbus_glib_tr_core_object_info );
187        }
188    }
189#endif
190}
191
192/***
193****  SORTING
194***/
195
196static int
197compareDouble( double a, double b )
198{
199    if( a < b ) return -1;
200    if( a > b ) return 1;
201    return 0;
202}
203
204static int
205compareRatio( double a, double b )
206{
207    if( (int)a == TR_RATIO_INF && (int)b == TR_RATIO_INF ) return 0;
208    if( (int)a == TR_RATIO_INF ) return 1;
209    if( (int)b == TR_RATIO_INF ) return -1;
210    return compareDouble( a, b );
211}
212
213static int
214compareTime( time_t a, time_t b )
215{
216    if( a < b ) return -1;
217    if( a > b ) return 1;
218    return 0;
219}
220
221static int
222compareByRatio( GtkTreeModel * model,
223                GtkTreeIter  * a,
224                GtkTreeIter  * b,
225                gpointer       user_data UNUSED )
226{
227    tr_torrent *ta, *tb;
228    const tr_stat *sa, *sb;
229
230    gtk_tree_model_get( model, a, MC_TORRENT_RAW, &ta, -1 );
231    gtk_tree_model_get( model, b, MC_TORRENT_RAW, &tb, -1 );
232
233    sa = tr_torrentStatCached( ta );
234    sb = tr_torrentStatCached( tb );
235
236    return compareRatio( sa->ratio, sb->ratio );
237}
238
239static int
240compareByActivity( GtkTreeModel * model,
241                   GtkTreeIter  * a,
242                   GtkTreeIter  * b,
243                   gpointer       user_data UNUSED )
244{
245    int i;
246    tr_torrent *ta, *tb;
247    const tr_stat *sa, *sb;
248
249    gtk_tree_model_get( model, a, MC_TORRENT_RAW, &ta, -1 );
250    gtk_tree_model_get( model, b, MC_TORRENT_RAW, &tb, -1 );
251
252    sa = tr_torrentStatCached( ta );
253    sb = tr_torrentStatCached( tb );
254
255    if(( i = compareDouble( sa->rateUpload + sa->rateDownload,
256                            sb->rateUpload + sb->rateDownload ) ))
257        return i;
258
259    if( sa->uploadedEver != sb->uploadedEver )
260        return sa->uploadedEver < sa->uploadedEver ? -1 : 1;
261
262    return 0;
263}
264
265static int
266compareByName( GtkTreeModel   * model,
267               GtkTreeIter    * a,
268               GtkTreeIter    * b,
269               gpointer         user_data UNUSED )
270{
271    int ret;
272    char *ca, *cb;
273    gtk_tree_model_get( model, a, MC_NAME_COLLATED, &ca, -1 );
274    gtk_tree_model_get( model, b, MC_NAME_COLLATED, &cb, -1 );
275    ret = strcmp( ca, cb );
276    g_free( cb );
277    g_free( ca );
278    return ret;
279}
280
281static int
282compareByAge( GtkTreeModel   * model,
283              GtkTreeIter    * a,
284              GtkTreeIter    * b,
285              gpointer         user_data UNUSED )
286{
287    tr_torrent *ta, *tb;
288    gtk_tree_model_get( model, a, MC_TORRENT_RAW, &ta, -1 );
289    gtk_tree_model_get( model, b, MC_TORRENT_RAW, &tb, -1 );
290    return compareTime( tr_torrentStatCached(ta)->addedDate,
291                        tr_torrentStatCached(tb)->addedDate );
292}
293
294static int
295compareByProgress( GtkTreeModel   * model,
296                   GtkTreeIter    * a,
297                   GtkTreeIter    * b,
298                   gpointer         user_data UNUSED )
299{
300    int ret;
301    tr_torrent *ta, *tb;
302    const tr_stat *sa, *sb;
303    gtk_tree_model_get( model, a, MC_TORRENT_RAW, &ta, -1 );
304    gtk_tree_model_get( model, b, MC_TORRENT_RAW, &tb, -1 );
305    sa = tr_torrentStatCached( ta );
306    sb = tr_torrentStatCached( tb );
307    ret = compareDouble( sa->percentDone, sb->percentDone );
308    if( !ret )
309        ret = compareRatio( sa->ratio, sb->ratio );
310    return ret;
311}
312
313static int
314compareByState( GtkTreeModel   * model,
315                GtkTreeIter    * a,
316                GtkTreeIter    * b,
317                gpointer         user_data )
318{
319    int sa, sb, ret;
320
321    /* first by state */
322    gtk_tree_model_get( model, a, MC_STATUS, &sa, -1 );
323    gtk_tree_model_get( model, b, MC_STATUS, &sb, -1 );
324    ret = sa - sb;
325
326    /* second by progress */
327    if( !ret )
328        ret = compareByProgress( model, a, b, user_data );
329
330    return ret;
331}
332
333static int
334compareByTracker( GtkTreeModel   * model,
335                  GtkTreeIter    * a,
336                  GtkTreeIter    * b,
337                  gpointer         user_data UNUSED )
338{
339    const tr_torrent *ta, *tb;
340    gtk_tree_model_get( model, a, MC_TORRENT_RAW, &ta, -1 );
341    gtk_tree_model_get( model, b, MC_TORRENT_RAW, &tb, -1 );
342    return strcmp( tr_torrentInfo(ta)->trackers[0].announce,
343                   tr_torrentInfo(tb)->trackers[0].announce );
344}
345
346static void
347setSort( TrCore * core, const char * mode, gboolean isReversed  )
348{
349    const int col = MC_TORRENT_RAW;
350    GtkTreeIterCompareFunc sort_func;
351    GtkSortType type = isReversed ? GTK_SORT_ASCENDING : GTK_SORT_DESCENDING;
352    GtkTreeSortable * sortable = GTK_TREE_SORTABLE( tr_core_model( core )  );
353
354    if( !strcmp( mode, "sort-by-activity" ) )
355        sort_func = compareByActivity;
356    else if( !strcmp( mode, "sort-by-age" ) )
357        sort_func = compareByAge;
358    else if( !strcmp( mode, "sort-by-progress" ) )
359        sort_func = compareByProgress;
360    else if( !strcmp( mode, "sort-by-ratio" ) )
361        sort_func = compareByRatio;
362    else if( !strcmp( mode, "sort-by-state" ) )
363        sort_func = compareByState;
364    else if( !strcmp( mode, "sort-by-tracker" ) )
365        sort_func = compareByTracker;
366    else {
367        sort_func = compareByName;
368        type = isReversed ? GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;
369    }
370 
371    gtk_tree_sortable_set_sort_func( sortable, col, sort_func, NULL, NULL );
372    gtk_tree_sortable_set_sort_column_id( sortable, col, type );
373}
374
375static void
376tr_core_apply_defaults( tr_ctor * ctor )
377{
378    if( tr_ctorGetPaused( ctor, TR_FORCE, NULL ) )
379        tr_ctorSetPaused( ctor, TR_FORCE, !pref_flag_get( PREF_KEY_START ) );
380
381    if( tr_ctorGetDeleteSource( ctor, NULL ) ) 
382        tr_ctorSetDeleteSource( ctor, pref_flag_get( PREF_KEY_TRASH_ORIGINAL ) ); 
383
384    if( tr_ctorGetPeerLimit( ctor, TR_FORCE, NULL ) )
385        tr_ctorSetPeerLimit( ctor, TR_FORCE,
386                             pref_int_get( PREF_KEY_MAX_PEERS_PER_TORRENT ) );
387
388    if( tr_ctorGetDownloadDir( ctor, TR_FORCE, NULL ) ) {
389        const char * path = pref_string_get( PREF_KEY_DOWNLOAD_DIR );
390        tr_ctorSetDownloadDir( ctor, TR_FORCE, path );
391    }
392}
393
394#ifdef HAVE_GIO
395static gboolean
396watchFolderIdle( gpointer gcore )
397{
398    TrCore * core = TR_CORE( gcore );
399    tr_core_add_list_defaults( core, core->priv->monitor_files );
400
401    /* cleanup */
402    core->priv->monitor_files = NULL;
403    core->priv->monitor_idle_tag = 0;
404    return FALSE;
405}
406
407static void
408maybeAddTorrent( TrCore * core, const char * filename )
409{
410    const gboolean isTorrent = g_str_has_suffix( filename, ".torrent" );
411
412    if( isTorrent )
413    {
414        struct TrCorePrivate * p = core->priv;
415
416        if( !g_slist_find_custom( p->monitor_files, filename, (GCompareFunc)strcmp ) )
417            p->monitor_files = g_slist_append( p->monitor_files, g_strdup( filename ) );
418        if( !p->monitor_idle_tag )
419            p->monitor_idle_tag = g_timeout_add( 1000, watchFolderIdle, core );
420    }
421}
422
423static void
424watchFolderChanged( GFileMonitor       * monitor UNUSED,
425                    GFile              * file,
426                    GFile              * other_type UNUSED,
427                    GFileMonitorEvent    event_type,
428                    gpointer             core )
429{
430    if( event_type == G_FILE_MONITOR_EVENT_CREATED )
431    {
432        char * filename = g_file_get_path( file );
433        maybeAddTorrent( core, filename );
434        g_free( filename );
435    }
436}
437
438static void
439scanWatchDir( TrCore * core )
440{
441    const gboolean isEnabled = pref_flag_get( PREF_KEY_DIR_WATCH_ENABLED );
442    if( isEnabled )
443    {
444        char * dirname = pref_string_get( PREF_KEY_DIR_WATCH );
445        GDir * dir = g_dir_open( dirname, 0, NULL );
446        const char * basename;
447        while(( basename = g_dir_read_name( dir ))) {
448            char * filename = g_build_filename( dirname, basename, NULL );
449            maybeAddTorrent( core, filename );
450            g_free( filename );
451        }
452        g_free( dirname );
453    }
454}
455
456static void
457updateWatchDir( TrCore * core )
458{
459    char * filename = pref_string_get( PREF_KEY_DIR_WATCH );
460    const gboolean isEnabled = pref_flag_get( PREF_KEY_DIR_WATCH_ENABLED );
461    struct TrCorePrivate * p = TR_CORE( core )->priv;
462
463    if( p->monitor && ( !isEnabled || tr_strcmp( filename, p->monitor_path ) ) )
464    {
465        g_signal_handler_disconnect( p->monitor, p->monitor_tag );
466        g_free( p->monitor_path );
467        g_file_monitor_cancel( p->monitor );
468        g_object_unref( G_OBJECT( p->monitor ) );
469        p->monitor_path = NULL;
470        p->monitor = NULL;
471        p->monitor_tag = 0;
472    }
473
474    if( isEnabled && !p->monitor )
475    {
476        GFile * file = g_file_new_for_path( filename );
477        GFileMonitor * m = g_file_monitor_directory( file, 0, NULL, NULL );
478        scanWatchDir( core );
479        p->monitor = m;
480        p->monitor_path = g_strdup( filename );
481        p->monitor_tag = g_signal_connect( m, "changed",
482                                           G_CALLBACK( watchFolderChanged ), core );
483    }
484
485    g_free( filename );
486}
487#endif
488
489static void
490prefsChanged( TrCore * core, const char * key, gpointer data UNUSED )
491{
492    if( !strcmp( key, PREF_KEY_SORT_MODE ) ||
493        !strcmp( key, PREF_KEY_SORT_REVERSED ) )
494    {
495        const char * mode = pref_string_get( PREF_KEY_SORT_MODE );
496        gboolean isReversed = pref_flag_get( PREF_KEY_SORT_REVERSED );
497        setSort( core, mode, isReversed );
498    }
499    else if( !strcmp( key, PREF_KEY_MAX_PEERS_GLOBAL ) )
500    {
501        const uint16_t val = pref_int_get( key );
502        tr_sessionSetPeerLimit( tr_core_handle( core ), val );
503    }
504    else if( !strcmp( key, PREF_KEY_ALLOW_HIBERNATION ) )
505    {
506        tr_core_set_hibernation_allowed( core, pref_flag_get( key ) );
507    }
508#ifdef HAVE_GIO
509    else if( !strcmp( key, PREF_KEY_DIR_WATCH ) ||
510             !strcmp( key, PREF_KEY_DIR_WATCH_ENABLED ) )
511    {
512        updateWatchDir( core );
513    }
514#endif
515}
516
517static void
518tr_core_init( GTypeInstance * instance, gpointer g_class UNUSED )
519{
520    TrCore * self = (TrCore *) instance;
521    GtkListStore * store;
522    struct TrCorePrivate * p;
523
524    /* column types for the model used to store torrent information */
525    /* keep this in sync with the enum near the bottom of tr_core.h */
526    GType types[] = {
527        G_TYPE_STRING,    /* name */
528        G_TYPE_STRING,    /* collated name */
529        TR_TORRENT_TYPE,  /* TrTorrent object */
530        G_TYPE_POINTER,   /* tr_torrent* */
531        G_TYPE_INT        /* tr_stat()->status */
532    };
533
534    p = self->priv = G_TYPE_INSTANCE_GET_PRIVATE( self,
535                                                  TR_CORE_TYPE,
536                                                  struct TrCorePrivate );
537
538    /* create the model used to store torrent data */
539    g_assert( ALEN( types ) == MC_ROW_COUNT );
540    store = gtk_list_store_newv( MC_ROW_COUNT, types );
541
542    p->model    = GTK_TREE_MODEL( store );
543
544#ifdef HAVE_DBUS_GLIB
545    if( our_instance_adds_remote_torrents )
546    {
547        DBusGConnection * bus = dbus_g_bus_get( DBUS_BUS_SESSION, NULL );
548        if( bus )
549            dbus_g_connection_register_g_object( bus,
550                                                 "/com/transmissionbt/Transmission",
551                                                 G_OBJECT( self ));
552    }
553#endif
554
555}
556
557GType
558tr_core_get_type( void )
559{
560    static GType type = 0;
561
562    if( !type )
563    {
564        static const GTypeInfo info =
565        {
566            sizeof( TrCoreClass ),
567            NULL,                       /* base_init */
568            NULL,                       /* base_finalize */
569            tr_core_class_init,         /* class_init */
570            NULL,                       /* class_finalize */
571            NULL,                       /* class_data */
572            sizeof( TrCore ),
573            0,                          /* n_preallocs */
574            tr_core_init,               /* instance_init */
575            NULL,
576        };
577        type = g_type_register_static( G_TYPE_OBJECT, "TrCore", &info, 0 );
578    }
579
580    return type;
581}
582
583/**
584***
585**/
586
587TrCore *
588tr_core_new( tr_handle * h )
589{
590    TrCore * core = TR_CORE( g_object_new( TR_CORE_TYPE, NULL ) );
591    core->priv->handle   = h;
592
593    /* init from prefs & listen to pref changes */
594    prefsChanged( core, PREF_KEY_SORT_MODE, NULL );
595    prefsChanged( core, PREF_KEY_SORT_REVERSED, NULL );
596    prefsChanged( core, PREF_KEY_DIR_WATCH_ENABLED, NULL );
597    prefsChanged( core, PREF_KEY_MAX_PEERS_GLOBAL, NULL );
598    prefsChanged( core, PREF_KEY_ALLOW_HIBERNATION, NULL );
599    g_signal_connect( core, "prefs-changed", G_CALLBACK(prefsChanged), NULL );
600
601    return core;
602}
603
604void
605tr_core_close( TrCore * core )
606{
607    tr_handle * handle = tr_core_handle( core );
608    if( handle )
609    {
610        core->priv->handle = NULL;
611        tr_sessionClose( handle ); 
612    }
613}
614
615GtkTreeModel *
616tr_core_model( TrCore * core )
617{
618    return isDisposed( core ) ? NULL : core->priv->model;
619}
620
621tr_handle *
622tr_core_handle( TrCore * core )
623{
624    return isDisposed( core ) ? NULL : core->priv->handle;
625}
626
627static gboolean
628statsForeach( GtkTreeModel * model,
629              GtkTreePath  * path UNUSED,
630              GtkTreeIter  * iter,
631              gpointer       gstats )
632{
633    tr_torrent * tor;
634    struct core_stats * stats = gstats;
635    int status;
636
637    gtk_tree_model_get( model, iter, MC_TORRENT_RAW, &tor, -1 );
638    status = tr_torrentGetStatus( tor );
639
640    if( status == TR_STATUS_DOWNLOAD )
641        ++stats->downloadCount;
642    else if( status == TR_STATUS_SEED )
643        ++stats->seedingCount;
644
645    return FALSE;
646}
647
648void
649tr_core_get_stats( const TrCore       * core,
650                   struct core_stats  * setme )
651{
652    memset( setme, 0, sizeof( struct core_stats ) );
653
654    if( !isDisposed( core ) )
655    {
656        tr_sessionGetSpeed( core->priv->handle,
657                            &setme->clientDownloadSpeed,
658                            &setme->clientUploadSpeed );
659
660        gtk_tree_model_foreach( core->priv->model,
661                                statsForeach,
662                                setme );
663    }
664}
665
666static char*
667doCollate( const char * in )
668{
669    const char * end = in + strlen( in );
670    char * casefold;
671    char * ret;
672
673    while( in < end ) {
674        const gunichar ch = g_utf8_get_char( in );
675        if (!g_unichar_isalnum (ch)) /* eat everything before the first alnum */
676            in += g_unichar_to_utf8( ch, NULL );
677        else
678            break;
679    }
680
681    if ( in == end )
682        return g_strdup ("");
683
684    casefold = g_utf8_casefold( in, end-in );
685    ret = g_utf8_collate_key( casefold, -1 );
686    g_free( casefold );
687
688    return ret;
689}
690
691void
692tr_core_add_torrent( TrCore * self, TrTorrent * gtor )
693{
694    const tr_info * inf = tr_torrent_info( gtor );
695    const tr_stat * torStat = tr_torrent_stat( gtor );
696    tr_torrent * tor = tr_torrent_handle( gtor );
697    char * collated = doCollate( inf->name );
698    GtkListStore * store = GTK_LIST_STORE( tr_core_model( self ) );
699    GtkTreeIter unused;
700
701    gtk_list_store_insert_with_values( store, &unused, 0, 
702                                       MC_NAME,          inf->name,
703                                       MC_NAME_COLLATED, collated,
704                                       MC_TORRENT,       gtor,
705                                       MC_TORRENT_RAW,   tor,
706                                       MC_STATUS,        torStat->status,
707                                       -1);
708
709    /* cleanup */
710    g_object_unref( G_OBJECT( gtor ) );
711    g_free( collated );
712}
713
714int
715tr_core_load( TrCore * self, gboolean forcePaused )
716{
717    int i;
718    int count = 0;
719    tr_torrent ** torrents;
720    tr_ctor * ctor;
721
722    ctor = tr_ctorNew( tr_core_handle( self ) );
723    if( forcePaused )
724        tr_ctorSetPaused( ctor, TR_FORCE, TRUE );
725    tr_ctorSetPeerLimit( ctor, TR_FALLBACK,
726                         pref_int_get( PREF_KEY_MAX_PEERS_PER_TORRENT ) );
727
728    torrents = tr_sessionLoadTorrents ( tr_core_handle( self ), ctor, &count );
729    for( i=0; i<count; ++i )
730        tr_core_add_torrent( self, tr_torrent_new_preexisting( torrents[i] ) );
731
732    tr_free( torrents );
733    tr_ctorFree( ctor );
734
735    return count;
736}
737
738static void
739tr_core_errsig( TrCore * core, enum tr_core_err type, const char * msg )
740{
741    g_signal_emit( core, TR_CORE_GET_CLASS(core)->errsig, 0, type, msg );
742}
743
744void
745tr_core_add_ctor( TrCore * self, tr_ctor * ctor )
746{
747    TrTorrent * tor;
748    char      * errstr = NULL;
749
750    tr_core_apply_defaults( ctor );
751
752    if(( tor = tr_torrent_new_ctor( tr_core_handle( self ), ctor, &errstr )))
753        tr_core_add_torrent( self, tor );
754    else{ 
755        tr_core_errsig( self, TR_CORE_ERR_ADD_TORRENT, errstr );
756        g_free( errstr );
757    }
758
759    /* cleanup */
760    tr_ctorFree( ctor );
761}
762
763static void
764add_filename( TrCore       * core,
765              const char   * filename,
766              gboolean       doStart,
767              gboolean       doPrompt )
768{
769    tr_handle * handle = tr_core_handle( core );
770
771    if( filename && handle )
772    {
773        tr_ctor * ctor = tr_ctorNew( handle );
774        tr_core_apply_defaults( ctor );
775        tr_ctorSetPaused( ctor, TR_FORCE, !doStart );
776        if( tr_ctorSetMetainfoFromFile( ctor, filename ) )
777            tr_ctorFree( ctor );
778        else if( tr_torrentParse( handle, ctor, NULL ) )
779            tr_ctorFree( ctor );
780        else if( doPrompt )
781            g_signal_emit( core, TR_CORE_GET_CLASS(core)->promptsig, 0, ctor );
782        else
783            tr_core_add_ctor( core, ctor );
784    }
785}
786
787gboolean
788tr_core_add_file( TrCore      * core,
789                  const char  * filename,
790                  gboolean    * success,
791                  GError     ** err UNUSED )
792{
793    add_filename( core, filename,
794                  pref_flag_get( PREF_KEY_START ),
795                  pref_flag_get( PREF_KEY_OPTIONS_PROMPT ) );
796    *success = TRUE;
797    return TRUE;
798}
799
800void
801tr_core_add_list( TrCore      * core,
802                  GSList      * torrentFiles,
803                  pref_flag_t   start,
804                  pref_flag_t   prompt )
805{
806    const gboolean doStart = pref_flag_eval( start, PREF_KEY_START );
807    const gboolean doPrompt = pref_flag_eval( prompt,PREF_KEY_OPTIONS_PROMPT );
808    GSList * l;
809    for( l=torrentFiles; l!=NULL; l=l->next )
810        add_filename( core, l->data, doStart, doPrompt );
811    freestrlist( torrentFiles );
812}
813
814void
815tr_core_torrents_added( TrCore * self )
816{
817    tr_core_update( self );
818    tr_core_errsig( self, TR_CORE_ERR_NO_MORE_TORRENTS, NULL );
819}
820
821static gboolean
822findTorrentInModel( TrCore * core, const TrTorrent * gtor, GtkTreeIter * setme )
823{
824    int match = 0;
825    GtkTreeIter iter;
826    GtkTreeModel * model = tr_core_model( core );
827
828    if( gtk_tree_model_iter_children( model, &iter, NULL ) ) do
829    {
830        TrTorrent * tmp;
831        gtk_tree_model_get( model, &iter, MC_TORRENT, &tmp, -1 );
832        match = tmp == gtor;
833        g_object_unref( G_OBJECT( tmp ) );
834    }
835    while( !match && gtk_tree_model_iter_next( model, &iter ) );
836
837    if( match )
838        *setme = iter;
839
840    return match;
841}
842
843void
844tr_core_remove_torrent( TrCore * self, TrTorrent * gtor, int deleteFiles )
845{
846    GtkTreeIter iter;
847    GtkTreeModel * model = tr_core_model( self );
848
849    /* remove from the gui */
850    if( findTorrentInModel( self, gtor, &iter ) )
851        gtk_list_store_remove( GTK_LIST_STORE( model ), &iter );
852
853    /* maybe delete the downloaded files */
854    if( deleteFiles )
855        tr_torrent_delete_files( gtor );
856
857    /* remove the torrent */
858    tr_torrent_set_remove_flag( gtor, TRUE );
859    g_object_unref( G_OBJECT( gtor ) );
860}
861
862
863/***
864****
865***/
866
867static gboolean
868update_foreach( GtkTreeModel * model,
869                GtkTreePath  * path UNUSED,
870                GtkTreeIter  * iter,
871                gpointer       data UNUSED )
872{
873    int oldStatus;
874    int newStatus;
875    TrTorrent * gtor;
876
877    /* maybe update the status column in the model */
878    gtk_tree_model_get( model, iter,
879                        MC_TORRENT, &gtor,
880                        MC_STATUS, &oldStatus,
881                        -1 );
882    newStatus = tr_torrentGetStatus( tr_torrent_handle( gtor ) );
883    if( newStatus != oldStatus )
884        gtk_list_store_set( GTK_LIST_STORE( model ), iter,
885                            MC_STATUS, newStatus,
886                            -1 );
887
888    /* cleanup */
889    g_object_unref( gtor );
890    return FALSE;
891}
892
893void
894tr_core_update( TrCore * self )
895{
896    int column;
897    GtkSortType order;
898    GtkTreeSortable * sortable;
899    GtkTreeModel * model = tr_core_model( self );
900
901    /* pause sorting */
902    sortable = GTK_TREE_SORTABLE( model );
903    gtk_tree_sortable_get_sort_column_id( sortable, &column, &order );
904    gtk_tree_sortable_set_sort_column_id( sortable, GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, order );
905
906    /* refresh the model */
907    gtk_tree_model_foreach( model, update_foreach, NULL );
908
909    /* resume sorting */
910    gtk_tree_sortable_set_sort_column_id( sortable, column, order );
911}
912
913void
914tr_core_quit( TrCore * core )
915{
916    g_signal_emit( core, TR_CORE_GET_CLASS(core)->quitsig, 0 );
917}
918
919/**
920***  Hibernate
921**/
922
923#ifdef HAVE_DBUS_GLIB
924
925static DBusGProxy*
926get_hibernation_inhibit_proxy( void )
927{
928    GError * error = NULL;
929    DBusGConnection * conn;
930
931    conn = dbus_g_bus_get( DBUS_BUS_SESSION, &error );
932    if( error )
933    {
934        g_warning ("DBUS cannot connect : %s", error->message);
935        g_error_free (error);
936        return NULL;
937    }
938
939    return dbus_g_proxy_new_for_name (conn,
940               "org.freedesktop.PowerManagement",
941               "/org/freedesktop/PowerManagement/Inhibit",
942               "org.freedesktop.PowerManagement.Inhibit" );
943}
944
945static gboolean
946gtr_inhibit_hibernation( guint * cookie )
947{
948    gboolean success = FALSE;
949    DBusGProxy * proxy = get_hibernation_inhibit_proxy( );
950    if( proxy )
951    {
952        GError * error = NULL;
953        const char * application = _( "Transmission Bittorrent Client" );
954        const char * reason = _( "BitTorrent Activity" );
955        success = dbus_g_proxy_call( proxy, "Inhibit", &error,
956                                     G_TYPE_STRING, application,
957                                     G_TYPE_STRING, reason,
958                                     G_TYPE_INVALID,
959                                     G_TYPE_UINT, cookie,
960                                     G_TYPE_INVALID );
961        if( success )
962            tr_inf( _( "Disallowing desktop hibernation" ) );
963        else {
964            tr_err( _( "Couldn't disable desktop hibernation: %s" ), error->message );
965            g_error_free( error );
966        }
967
968        g_object_unref( G_OBJECT( proxy ) );
969    }
970
971    return success != 0;
972}
973
974static void
975gtr_uninhibit_hibernation( guint inhibit_cookie )
976{
977    DBusGProxy * proxy = get_hibernation_inhibit_proxy( );
978    if( proxy )
979    {
980        GError * error = NULL;
981        gboolean success = dbus_g_proxy_call( proxy, "UnInhibit", &error,
982                                              G_TYPE_UINT, inhibit_cookie,
983                                              G_TYPE_INVALID,
984                                              G_TYPE_INVALID );
985        if( success )
986            tr_inf( _( "Allowing desktop hibernation" ) );
987        else {
988            g_warning( "Couldn't uninhibit the system from suspending: %s.", error->message );
989            g_error_free( error );
990        }
991
992        g_object_unref( G_OBJECT( proxy ) );
993    }
994}
995
996#endif
997
998
999void
1000tr_core_set_hibernation_allowed( TrCore * core, gboolean allowed )
1001{
1002#ifdef HAVE_DBUS_GLIB
1003    g_return_if_fail( core );
1004    g_return_if_fail( core->priv );
1005
1006    core->priv->inhibit_allowed = allowed != 0;
1007
1008    if( allowed && core->priv->have_inhibit_cookie )
1009    {
1010        gtr_uninhibit_hibernation( core->priv->inhibit_cookie );
1011        core->priv->have_inhibit_cookie = FALSE;
1012    }
1013
1014    if( !allowed && !core->priv->have_inhibit_cookie )
1015    {
1016        core->priv->have_inhibit_cookie = gtr_inhibit_hibernation( &core->priv->inhibit_cookie );
1017    }
1018#endif
1019}
1020
1021/**
1022***  Prefs
1023**/
1024
1025static void
1026commitPrefsChange( TrCore * core, const char * key )
1027{
1028    pref_save( );
1029    g_signal_emit( core, TR_CORE_GET_CLASS(core)->prefsig, 0, key );
1030}
1031
1032void
1033tr_core_set_pref( TrCore * self, const char * key, const char * newval )
1034{
1035    const char * oldval = pref_string_get( key );
1036    if( tr_strcmp( oldval, newval ) )
1037    {
1038        pref_string_set( key, newval );
1039        commitPrefsChange( self, key );
1040    }
1041}
1042
1043void
1044tr_core_set_pref_bool( TrCore * self, const char * key, gboolean newval )
1045{
1046    const gboolean oldval = pref_flag_get( key );
1047    if( oldval != newval )
1048    {
1049        pref_flag_set( key, newval );
1050        commitPrefsChange( self, key );
1051    }
1052}
1053
1054void
1055tr_core_set_pref_int( TrCore * self, const char * key, int newval )
1056{
1057    const int oldval = pref_int_get( key );
1058    if( oldval != newval )
1059    {
1060        pref_int_set( key, newval );
1061        commitPrefsChange( self, key );
1062    }
1063}
Note: See TracBrowser for help on using the repository browser.