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

Last change on this file since 8211 was 8211, checked in by charles, 13 years ago

(trunk) have the gtk and qt clients use the same minimum seed ratio in the preferences dialog as the mac uses

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