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

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

(#954) rpc: add "torrent-remove" method. rename tr_torrentDelete() as tr_torrentRemove() for consistency with various parts of the code.

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