source: trunk/gtk/file-list.c @ 7464

Last change on this file since 7464 was 7464, checked in by charles, 12 years ago

(trunk gtk) #1585: use g_timeout_add_seconds() where appropriate to group timers together for fewer scheduled wakeups

  • Property svn:keywords set to Date Rev Author Id
File size: 27.2 KB
Line 
1/******************************************************************************
2 * $Id: file-list.c 7464 2008-12-22 05:39:03Z charles $
3 *
4 * Copyright (c) 2005-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 <stddef.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <glib/gi18n.h>
30#include <gtk/gtk.h>
31
32#include <libtransmission/transmission.h>
33#include <libtransmission/utils.h> /* tr_getRatio */
34
35#include "file-list.h"
36#include "hig.h"
37
38enum
39{
40    SUB_STATE_HIGH          = ( 1 << 0 ),
41    SUB_STATE_NORMAL        = ( 1 << 1 ),
42    SUB_STATE_LOW           = ( 1 << 2 ),
43    SUB_STATE_PRIORITY_MASK =
44        ( SUB_STATE_HIGH | SUB_STATE_NORMAL | SUB_STATE_LOW ),
45    SUB_STATE_DOWNLOAD      = ( 1 << 4 ),
46    SUB_STATE_IGNORE        = ( 1 << 5 ),
47    SUB_STATE_DOWNLOAD_MASK = ( SUB_STATE_DOWNLOAD | SUB_STATE_IGNORE )
48};
49
50enum
51{
52    FC_STOCK,
53    FC_LABEL,
54    FC_PROG,
55    FC_KEY,
56    FC_INDEX,
57    FC_SIZE,
58    FC_HAVE,
59    FC_PRIORITY,
60    FC_ENABLED,
61    FC_IS_FILE,
62    FC_SUB_SIZE,
63    FC_SUB_HAVE,
64    FC_SUB_STATE,
65    N_FILE_COLS
66};
67
68typedef struct
69{
70    TrTorrent *     gtor;
71    GtkWidget *     top;
72    GtkWidget *     view;
73    GtkTreeModel *  model; /* same object as store, but recast */
74    GtkTreeStore *  store; /* same object as model, but recast */
75    tr_file_stat *  refresh_file_stat;
76    guint           timeout_tag;
77}
78FileData;
79
80static void
81clearData( FileData * data )
82{
83    data->gtor = NULL;
84
85    if( data->timeout_tag )
86    {
87        g_source_remove( data->timeout_tag );
88        data->timeout_tag = 0;
89    }
90}
91
92static void
93freeData( gpointer gdata )
94{
95    FileData * data = gdata;
96
97    clearData( data );
98    g_free( data );
99}
100
101/***
102****
103***/
104
105static void
106parsepath( const tr_torrent * tor,
107           GtkTreeStore *     store,
108           GtkTreeIter *      ret,
109           const char *       path,
110           tr_file_index_t    index,
111           uint64_t           size )
112{
113    GtkTreeModel * model;
114    GtkTreeIter *  parent, start, iter;
115    char *         file, * lower, * mykey;
116    const char *   stock;
117    int            priority = 0;
118    gboolean       enabled = TRUE;
119    gboolean       is_file;
120
121    model  = GTK_TREE_MODEL( store );
122    parent = NULL;
123    file   = g_path_get_basename( path );
124    if( 0 != strcmp( file, path ) )
125    {
126        char * dir = g_path_get_dirname( path );
127        parsepath( tor, store, &start, dir, index, size );
128        parent = &start;
129        g_free( dir );
130    }
131
132    lower = g_utf8_casefold( file, -1 );
133    mykey = g_utf8_collate_key( lower, -1 );
134    if( gtk_tree_model_iter_children( model, &iter, parent ) ) do
135        {
136            gboolean stop;
137            char *   modelkey;
138            gtk_tree_model_get( model, &iter, FC_KEY, &modelkey, -1 );
139            stop = ( modelkey != NULL ) && !strcmp( mykey, modelkey );
140            g_free ( modelkey );
141            if( stop ) goto done;
142        }
143        while( gtk_tree_model_iter_next( model, &iter ) );
144
145    gtk_tree_store_append( store, &iter, parent );
146    if( ( is_file = !ret ) )
147    {
148        stock = GTK_STOCK_FILE;
149        priority = tr_torrentGetFilePriority( tor, index );
150        enabled  = tr_torrentGetFileDL( tor, index );
151    }
152    else
153    {
154        stock = GTK_STOCK_DIRECTORY;
155        size  = 0;
156    }
157
158#if 0
159    gtk_tree_store_set( store, &iter, FC_INDEX, index,
160                        FC_LABEL, file,
161                        FC_KEY, mykey,
162                        FC_STOCK, stock,
163                        FC_PRIORITY, priority,
164                        FC_ENABLED, enabled,
165                        FC_IS_FILE, is_file,
166                        FC_SIZE, size,
167                        FC_HAVE, 0,
168                        -1 );
169#else
170    gtk_tree_store_set( store, &iter, FC_INDEX, index, -1 );
171    gtk_tree_store_set( store, &iter, FC_LABEL, file, -1 );
172    gtk_tree_store_set( store, &iter, FC_KEY, mykey, -1 );
173    gtk_tree_store_set( store, &iter, FC_STOCK, stock, -1 );
174    gtk_tree_store_set( store, &iter, FC_PRIORITY, priority, -1 );
175    gtk_tree_store_set( store, &iter, FC_ENABLED, enabled, -1 );
176    gtk_tree_store_set( store, &iter, FC_IS_FILE, is_file, -1 );
177    gtk_tree_store_set( store, &iter, FC_SIZE, size, -1 );
178    gtk_tree_store_set( store, &iter, FC_HAVE, 0, -1 );
179#endif
180
181done:
182    g_free( mykey );
183    g_free( lower );
184    g_free( file );
185    if( NULL != ret )
186        *ret = iter;
187}
188
189/***
190****
191***/
192
193static gboolean
194refreshFilesForeach( GtkTreeModel *       model,
195                     GtkTreePath   * path UNUSED,
196                     GtkTreeIter *        iter,
197                     gpointer             gdata )
198{
199    FileData *   data = gdata;
200    gboolean     is_file;
201    unsigned int index;
202
203    gtk_tree_model_get( model, iter, FC_IS_FILE, &is_file, FC_INDEX, &index,
204                        -1  );
205    if( is_file )
206    {
207        GtkTreeStore * store = GTK_TREE_STORE( model );
208        tr_torrent *   tor = tr_torrent_handle( data->gtor );
209        int            download = tr_torrentGetFileDL( tor, index );
210        int            priority = tr_torrentGetFilePriority( tor, index );
211        uint64_t       have = data->refresh_file_stat[index].bytesCompleted;
212        gtk_tree_store_set( store, iter, FC_PRIORITY, priority,
213                            FC_ENABLED, download,
214                            FC_HAVE, have,
215                            -1 );
216    }
217    return FALSE; /* keep walking */
218}
219
220static gboolean
221resetSubForeach( GtkTreeModel *        model,
222                 GtkTreePath   * path  UNUSED,
223                 GtkTreeIter *         iter,
224                 gpointer        gdata UNUSED )
225{
226    /* set the subs to the lowest values... */
227    gtk_tree_store_set( GTK_TREE_STORE( model ), iter,
228                        FC_SUB_STATE, 0,
229                        FC_SUB_SIZE, (uint64_t)0,
230                        FC_SUB_HAVE, (uint64_t)0,
231                        -1 );
232    return FALSE; /* keep walking */
233}
234
235static gboolean
236addSubForeach( GtkTreeModel *        model,
237               GtkTreePath   * path  UNUSED,
238               GtkTreeIter *         iter,
239               gpointer        gdata UNUSED )
240{
241    uint64_t size;
242    uint64_t have;
243    int      priority;
244    gboolean enabled;
245    gboolean is_file;
246
247    gtk_tree_model_get( model, iter, FC_SIZE, &size,
248                        FC_HAVE, &have,
249                        FC_PRIORITY, &priority,
250                        FC_ENABLED, &enabled,
251                        FC_IS_FILE, &is_file,
252                        -1 );
253    if( is_file )
254    {
255        GtkTreeIter child = *iter;
256        GtkTreeIter parent;
257        while( ( gtk_tree_model_iter_parent( model, &parent, &child ) ) )
258        {
259            uint64_t sub_size;
260            uint64_t sub_have;
261            int      sub_state;
262            gtk_tree_model_get( model, &parent, FC_SUB_SIZE, &sub_size,
263                                FC_SUB_HAVE, &sub_have,
264                                FC_SUB_STATE, &sub_state,
265                                -1 );
266            sub_have += have;
267            sub_size += size;
268            switch( priority )
269            {
270                case TR_PRI_HIGH:
271                    sub_state |= SUB_STATE_HIGH;   break;
272
273                case TR_PRI_NORMAL:
274                    sub_state |= SUB_STATE_NORMAL; break;
275
276                case TR_PRI_LOW:
277                    sub_state |= SUB_STATE_LOW;    break;
278            }
279            sub_state |= ( enabled ? SUB_STATE_DOWNLOAD : SUB_STATE_IGNORE );
280            gtk_tree_store_set( GTK_TREE_STORE( model ), &parent,
281                                FC_SUB_SIZE, sub_size,
282                                FC_SUB_HAVE, sub_have,
283                                FC_SUB_STATE, sub_state,
284                                -1 );
285            child = parent;
286        }
287    }
288    return FALSE; /* keep walking */
289}
290
291static void
292refresh( FileData * data )
293{
294    tr_file_index_t fileCount;
295    tr_torrent *    tor = tr_torrent_handle( data->gtor );
296
297    data->refresh_file_stat = tr_torrentFiles( tor, &fileCount );
298
299    gtk_tree_model_foreach( data->model, refreshFilesForeach, data );
300    gtk_tree_model_foreach( data->model, resetSubForeach, data );
301    gtk_tree_model_foreach( data->model, addSubForeach, data );
302
303    tr_torrentFilesFree( data->refresh_file_stat, fileCount );
304    data->refresh_file_stat = NULL;
305}
306
307static gboolean
308refreshModel( gpointer file_data )
309{
310    refresh( file_data );
311    return TRUE;
312}
313
314/***
315****
316***/
317
318struct ActiveData
319{
320    GtkTreeSelection *  sel;
321    GArray *            array;
322};
323
324static gboolean
325getSelectedFilesForeach( GtkTreeModel *       model,
326                         GtkTreePath   * path UNUSED,
327                         GtkTreeIter *        iter,
328                         gpointer             gdata )
329{
330    struct ActiveData * data = gdata;
331    unsigned int        i;
332    gboolean            is_file = FALSE;
333    gboolean            is_active = FALSE;
334
335    /* active == if it's selected, or any ancestor is selected */
336    gtk_tree_model_get( model, iter, FC_IS_FILE, &is_file, FC_INDEX, &i, -1 );
337    if( is_file )
338    {
339        is_active = gtk_tree_selection_iter_is_selected( data->sel, iter );
340        if( !is_active )
341        {
342            GtkTreeIter walk = *iter;
343            GtkTreeIter parent;
344            while( !is_active
345                 && gtk_tree_model_iter_parent( model, &parent, &walk ) )
346            {
347                is_active = gtk_tree_selection_iter_is_selected( data->sel,
348                                                                 &parent );
349                walk = parent;
350            }
351        }
352    }
353
354    if( is_active )
355        g_array_append_val( data->array, i );
356
357    return FALSE; /* keep walking */
358}
359
360static void
361getSelectedFilesAndDescendants( GtkTreeView * view,
362                                GArray *      indices )
363{
364    struct ActiveData data;
365
366    data.sel = gtk_tree_view_get_selection( view );
367    data.array  = indices;
368    gtk_tree_model_foreach( gtk_tree_view_get_model( view ),
369                            getSelectedFilesForeach, &data );
370}
371
372struct SubtreeForeachData
373{
374    GArray * array;
375    GtkTreePath * path;
376};
377
378static gboolean
379getSubtreeForeach( GtkTreeModel   * model,
380                   GtkTreePath    * path,
381                   GtkTreeIter    * iter,
382                   gpointer         gdata )
383{
384    struct SubtreeForeachData * data = gdata;
385    unsigned int i;
386    gboolean is_file = FALSE;
387
388    gtk_tree_model_get( model, iter,
389                        FC_IS_FILE, &is_file,
390                        FC_INDEX, &i, -1 );
391    if( is_file )
392        if( !gtk_tree_path_compare( path, data->path ) || gtk_tree_path_is_descendant( path, data->path ) )
393            g_array_append_val( data->array, i );
394
395    return FALSE; /* keep walking */
396}
397
398static void
399getSubtree( GtkTreeView * view, GtkTreePath * path, GArray * indices )
400{
401    struct SubtreeForeachData tmp;
402    tmp.array = indices;
403    tmp.path = path;
404    gtk_tree_model_foreach( gtk_tree_view_get_model( view ), getSubtreeForeach, &tmp );
405}
406
407/* if `path' is a selected row, all selected rows are returned.
408 * otherwise, only the row indicated by `path' is returned.
409 * this is for toggling all the selected rows' states in a batch.
410 */
411static GArray*
412getActiveFilesForPath( GtkTreeView * view,
413                       GtkTreePath * path )
414{
415    GtkTreeSelection * sel = gtk_tree_view_get_selection( view );
416    GArray * indices = g_array_new( FALSE, FALSE, sizeof( tr_file_index_t ) );
417
418    if( gtk_tree_selection_path_is_selected( sel, path ) )
419    {
420        /* clicked in a selected row... use the current selection */
421        getSelectedFilesAndDescendants( view, indices );
422    }
423    else
424    {
425        /* clicked OUTSIDE of the selected row... just use the clicked row */
426        getSubtree( view, path, indices );
427    }
428
429    return indices;
430}
431
432/***
433****
434***/
435
436void
437file_list_set_torrent( GtkWidget * w,
438                       TrTorrent * gtor )
439{
440    GtkTreeStore * store;
441    FileData *     data;
442
443    data = g_object_get_data( G_OBJECT( w ), "file-data" );
444
445    /* unset the old fields */
446    clearData( data );
447
448    /* instantiate the model */
449    store = gtk_tree_store_new ( N_FILE_COLS,
450                                 G_TYPE_STRING,    /* stock */
451                                 G_TYPE_STRING,    /* label */
452                                 G_TYPE_INT,       /* prog [0..100] */
453                                 G_TYPE_STRING,    /* key */
454                                 G_TYPE_UINT,      /* index */
455                                 G_TYPE_UINT64,    /* size */
456                                 G_TYPE_UINT64,    /* have */
457                                 G_TYPE_INT,       /* priority */
458                                 G_TYPE_BOOLEAN,   /* dl enabled */
459                                 G_TYPE_BOOLEAN,   /* is file */
460                                 G_TYPE_UINT64,    /* sub size */
461                                 G_TYPE_UINT64,    /* sub have */
462                                 G_TYPE_INT );     /* sub state */
463    data->store = store;
464    data->model = GTK_TREE_MODEL( store );
465    data->gtor = gtor;
466
467
468    /* populate the model */
469    if( gtor )
470    {
471        tr_file_index_t i;
472        const tr_info * inf = tr_torrent_info( gtor );
473        tr_torrent *    tor = tr_torrent_handle( gtor );
474
475        for( i = 0; inf && i < inf->fileCount; ++i )
476        {
477            const char * path = inf->files[i].name;
478            const char * base =
479                g_path_is_absolute( path ) ? g_path_skip_root( path ) :
480                path;
481            parsepath( tor, store, NULL, base, i, inf->files[i].length );
482        }
483
484        refresh( data );
485
486        data->timeout_tag = gtr_timeout_add_seconds( 2, refreshModel, data );
487    }
488
489    gtk_tree_view_set_model( GTK_TREE_VIEW( data->view ),
490                            GTK_TREE_MODEL( store ) );
491    gtk_tree_view_expand_all( GTK_TREE_VIEW( data->view ) );
492}
493
494/***
495****
496***/
497
498static void
499renderProgress( GtkTreeViewColumn  * column UNUSED,
500                GtkCellRenderer *           renderer,
501                GtkTreeModel *              model,
502                GtkTreeIter *               iter,
503                gpointer             data   UNUSED )
504{
505    gboolean is_file;
506    uint64_t size, have, subsize, subhave;
507    double   progress;
508
509    gtk_tree_model_get( model, iter, FC_SIZE, &size,
510                        FC_HAVE, &have,
511                        FC_SUB_SIZE, &subsize,
512                        FC_SUB_HAVE, &subhave,
513                        FC_IS_FILE, &is_file,
514                        -1 );
515    progress = is_file ? tr_getRatio( have, size )
516               : tr_getRatio( subhave, subsize );
517    g_object_set( renderer, "value", (int)( progress * 100 ), NULL );
518}
519
520static void
521renderFilename( GtkTreeViewColumn  * column UNUSED,
522                GtkCellRenderer *           renderer,
523                GtkTreeModel *              model,
524                GtkTreeIter *               iter,
525                gpointer             data   UNUSED )
526{
527    char *   filename;
528    char *   str;
529    int64_t  size;
530    int64_t  subsize;
531    gboolean is_file;
532    char     buf[64];
533
534    gtk_tree_model_get( model, iter, FC_LABEL, &filename,
535                        FC_SIZE, &size,
536                        FC_SUB_SIZE, &subsize,
537                        FC_IS_FILE, &is_file,
538                        -1 );
539    tr_strlsize( buf, is_file ? size : subsize, sizeof( buf ) );
540    str = g_markup_printf_escaped( "<small>%s (%s)</small>",
541                                   filename, buf );
542    g_object_set( renderer, "markup", str, NULL );
543    g_free( str );
544    g_free( filename );
545}
546
547static void
548renderDownload( GtkTreeViewColumn  * column UNUSED,
549                GtkCellRenderer *           renderer,
550                GtkTreeModel *              model,
551                GtkTreeIter *               iter,
552                gpointer             data   UNUSED )
553{
554    int      sub_state;
555    gboolean enabled;
556    gboolean active = FALSE;
557    gboolean inconsistent = FALSE;
558    gboolean is_file = FALSE;
559
560    gtk_tree_model_get( model, iter, FC_IS_FILE, &is_file,
561                        FC_ENABLED, &enabled,
562                        FC_SUB_STATE, &sub_state,
563                        -1 );
564    if( is_file && enabled )
565        active = TRUE;
566    else if( is_file )
567        active = FALSE;
568    else switch( sub_state & SUB_STATE_DOWNLOAD_MASK )
569        {
570            case SUB_STATE_DOWNLOAD:
571                active = TRUE; break;
572
573            case SUB_STATE_IGNORE:
574                active = FALSE; break;
575
576            default:
577                inconsistent = TRUE; break;
578        }
579
580    g_object_set( renderer, "inconsistent", inconsistent,
581                  "active", active,
582                  NULL );
583}
584
585static void
586renderPriority( GtkTreeViewColumn  * column UNUSED,
587                GtkCellRenderer *           renderer,
588                GtkTreeModel *              model,
589                GtkTreeIter *               iter,
590                gpointer             data   UNUSED )
591{
592    int          priority;
593    int          sub_state;
594    gboolean     is_file = FALSE;
595    const char * text = "";
596
597    gtk_tree_model_get( model, iter, FC_IS_FILE, &is_file,
598                        FC_PRIORITY, &priority,
599                        FC_SUB_STATE, &sub_state,
600                        -1 );
601    if( !is_file )
602    {
603        switch( sub_state & SUB_STATE_PRIORITY_MASK )
604        {
605            case SUB_STATE_HIGH:
606                priority = TR_PRI_HIGH;   break;
607
608            case SUB_STATE_NORMAL:
609                priority = TR_PRI_NORMAL; break;
610
611            case SUB_STATE_LOW:
612                priority = TR_PRI_LOW;    break;
613
614            default:
615                priority = 666;           break;
616        }
617    }
618
619    switch( priority )
620    {
621        case TR_PRI_HIGH:
622            text = _( "High" );
623            break;
624
625        case TR_PRI_NORMAL:
626            text = _( "Normal" );
627            break;
628
629        case TR_PRI_LOW:
630            text = _( "Low" );
631            break;
632
633        default:
634            text = _( "Mixed" );
635            break;
636    }
637
638    g_object_set( renderer, "text", text,
639                  "xalign", (gfloat)0.5,
640                  "yalign", (gfloat)0.5,
641                  NULL );
642}
643
644static gboolean
645onViewButtonPressed( GtkWidget *      w,
646                     GdkEventButton * event,
647                     gpointer         gdata )
648{
649    FileData * data = gdata;
650    gboolean   handled = FALSE;
651
652    if( ( event->type == GDK_BUTTON_PRESS ) && ( event->button == 1 )
653      && !( event->state & ( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) )
654    {
655        GtkTreeView *       view = GTK_TREE_VIEW( w );
656        GtkTreePath *       path;
657        GtkTreeViewColumn * column;
658        int                 cell_x;
659        int                 cell_y;
660        if( gtk_tree_view_get_path_at_pos( view, event->x, event->y,
661                                           &path, &column, &cell_x, &cell_y ) )
662        {
663            const char * column_title = gtk_tree_view_column_get_title( column );
664            const gboolean downloadColumn = !strcmp( column_title, _( "Download" ) );
665            const gboolean priorityColumn = !strcmp( column_title, _( "Priority" ) );
666            if( downloadColumn || priorityColumn )
667            {
668                GArray *           a = getActiveFilesForPath( view, path );
669                GtkTreeSelection * sel = gtk_tree_view_get_selection( view );
670                const gboolean     isSelected =
671                    gtk_tree_selection_path_is_selected( sel, path );
672                GtkTreeModel *     model = gtk_tree_view_get_model( view );
673                GtkTreeIter        iter;
674
675                gtk_tree_model_get_iter( model, &iter, path );
676
677                if( priorityColumn )
678                {
679                    gboolean is_file;
680                    int      sub_state;
681                    int      priority;
682
683                    /* get the `priority' state of the clicked row */
684                    gtk_tree_model_get( model, &iter, FC_IS_FILE, &is_file,
685                                        FC_PRIORITY, &priority,
686                                        FC_SUB_STATE, &sub_state,
687                                        -1 );
688
689                    /* twiddle it to the next state */
690                    if( !is_file ) switch( sub_state &
691                                           SUB_STATE_PRIORITY_MASK )
692                        {
693                            case SUB_STATE_NORMAL:
694                                priority = TR_PRI_HIGH; break;
695
696                            case SUB_STATE_HIGH:
697                                priority = TR_PRI_LOW; break;
698
699                            default:
700                                priority = TR_PRI_NORMAL; break;
701                        }
702                    else switch( priority )
703                        {
704                            case TR_PRI_LOW:
705                                priority = TR_PRI_NORMAL; break;
706
707                            case TR_PRI_NORMAL:
708                                priority = TR_PRI_HIGH; break;
709
710                            case TR_PRI_HIGH:
711                                priority = TR_PRI_LOW; break;
712                        }
713
714                    /* apply that new state to the active files */
715                    tr_torrentSetFilePriorities( tr_torrent_handle( data->
716                                                                    gtor ),
717                                                 (tr_file_index_t*)a->data,
718                                                 (tr_file_index_t)a->len,
719                                                 priority );
720                }
721                else if( downloadColumn )
722                {
723                    gboolean is_file;
724                    int      sub_state;
725                    gboolean enabled;
726
727                    /* get the `enabled' state of the clicked row */
728                    gtk_tree_model_get( model, &iter, FC_IS_FILE, &is_file,
729                                        FC_ENABLED, &enabled,
730                                        FC_SUB_STATE, &sub_state, -1 );
731
732                    /* twiddle it to the next state */
733                    if( is_file )
734                        enabled = !enabled;
735                    else
736                        enabled = ( sub_state & SUB_STATE_IGNORE ) ? 1 : 0;
737
738                    /* apply that new state to the active files */
739                    tr_torrentSetFileDLs( tr_torrent_handle( data->gtor ),
740                                          (tr_file_index_t*)a->data,
741                                          (tr_file_index_t)a->len,
742                                          enabled );
743                }
744
745                refresh( data );
746
747                /* the click was meant to change the priority or enabled state,
748                   not to alter which rows were selected, so don't pass this
749                   event on to the other handlers. */
750                handled = isSelected;
751
752                /* cleanup */
753                g_array_free( a, TRUE );
754            }
755
756            gtk_tree_path_free( path );
757        }
758    }
759
760    return handled;
761}
762
763GtkWidget *
764file_list_new( TrTorrent * gtor )
765{
766    GtkWidget *         ret;
767    GtkWidget *         view, * scroll;
768    GtkCellRenderer *   rend;
769    GtkTreeViewColumn * col;
770    GtkTreeSelection *  sel;
771    FileData *          data = g_new0( FileData, 1 );
772
773    /* create the view */
774    view = gtk_tree_view_new( );
775    gtk_tree_view_set_rules_hint( GTK_TREE_VIEW( view ), TRUE );
776    gtk_container_set_border_width( GTK_CONTAINER( view ), GUI_PAD_BIG );
777    g_signal_connect( view, "button-press-event",
778                      G_CALLBACK( onViewButtonPressed ), data );
779    g_signal_connect( view, "button-release-event",
780                      G_CALLBACK( on_tree_view_button_released ), NULL );
781
782
783    /* set up view */
784    sel = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) );
785    gtk_tree_selection_set_mode( sel, GTK_SELECTION_MULTIPLE );
786    gtk_tree_view_expand_all( GTK_TREE_VIEW( view ) );
787    gtk_tree_view_set_search_column( GTK_TREE_VIEW( view ), FC_LABEL );
788
789    /* add file column */
790
791    col = GTK_TREE_VIEW_COLUMN ( g_object_new ( GTK_TYPE_TREE_VIEW_COLUMN,
792                                                "expand", TRUE,
793                                                "title", _( "File" ),
794                                                NULL ) );
795    rend = gtk_cell_renderer_pixbuf_new( );
796    gtk_tree_view_column_pack_start( col, rend, FALSE );
797    gtk_tree_view_column_add_attribute( col, rend, "stock-id", FC_STOCK );
798    /* add text renderer */
799    rend = gtk_cell_renderer_text_new( );
800    g_object_set( rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL );
801    gtk_tree_view_column_pack_start( col, rend, TRUE );
802    gtk_tree_view_column_set_cell_data_func( col, rend, renderFilename, NULL, NULL );
803    gtk_tree_view_column_set_resizable( col, TRUE );
804    gtk_tree_view_append_column( GTK_TREE_VIEW( view ), col );
805
806    rend = gtk_cell_renderer_progress_new( );
807    col = gtk_tree_view_column_new_with_attributes( _( "Progress" ), rend, NULL );
808    gtk_tree_view_column_set_cell_data_func( col, rend, renderProgress, NULL, NULL );
809    gtk_tree_view_column_set_resizable( col, FALSE );
810    gtk_tree_view_append_column ( GTK_TREE_VIEW( view ), col );
811
812    /* add "enabled" column */
813    rend = gtk_cell_renderer_toggle_new( );
814    col = gtk_tree_view_column_new_with_attributes( _( "Download" ), rend, NULL );
815    gtk_tree_view_column_set_cell_data_func( col, rend, renderDownload, NULL, NULL );
816    gtk_tree_view_column_set_resizable( col, FALSE );
817    gtk_tree_view_append_column ( GTK_TREE_VIEW( view ), col );
818
819    /* add priority column */
820    rend = gtk_cell_renderer_text_new( );
821    col = gtk_tree_view_column_new_with_attributes( _( "Priority" ), rend, NULL );
822    gtk_tree_view_column_set_cell_data_func( col, rend, renderPriority, NULL, NULL );
823    gtk_tree_view_column_set_resizable( col, FALSE );
824    gtk_tree_view_append_column ( GTK_TREE_VIEW( view ), col );
825
826    /* create the scrolled window and stick the view in it */
827    scroll = gtk_scrolled_window_new( NULL, NULL );
828    gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scroll ),
829                                    GTK_POLICY_AUTOMATIC,
830                                    GTK_POLICY_AUTOMATIC );
831    gtk_scrolled_window_set_shadow_type ( GTK_SCROLLED_WINDOW( scroll ),
832                                          GTK_SHADOW_IN );
833    gtk_container_add( GTK_CONTAINER( scroll ), view );
834    gtk_widget_set_size_request ( scroll, -1, 200 );
835
836    ret = scroll;
837    data->view = view;
838    data->top = scroll;
839    g_object_set_data_full( G_OBJECT( ret ), "file-data", data, freeData );
840    file_list_set_torrent( ret, gtor );
841
842    return ret;
843}
844
Note: See TracBrowser for help on using the repository browser.