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

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

(trunk gtk) Use gtk_tree_view_set_fixed_height_mode() in a few more places

  • Property svn:keywords set to Date Rev Author Id
File size: 27.6 KB
Line 
1/******************************************************************************
2 * $Id: file-list.c 7998 2009-03-03 00:48:50Z 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_fixed_height_mode( GTK_TREE_VIEW( view ), TRUE );
784    gtk_tree_view_set_rules_hint( GTK_TREE_VIEW( view ), TRUE );
785    gtk_container_set_border_width( GTK_CONTAINER( view ), GUI_PAD_BIG );
786    g_signal_connect( view, "button-press-event",
787                      G_CALLBACK( onViewButtonPressed ), data );
788    g_signal_connect( view, "button-release-event",
789                      G_CALLBACK( on_tree_view_button_released ), NULL );
790
791
792    /* set up view */
793    sel = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) );
794    gtk_tree_selection_set_mode( sel, GTK_SELECTION_MULTIPLE );
795    gtk_tree_view_expand_all( GTK_TREE_VIEW( view ) );
796    gtk_tree_view_set_search_column( GTK_TREE_VIEW( view ), FC_LABEL );
797
798    /* add file column */
799
800    col = GTK_TREE_VIEW_COLUMN ( g_object_new ( GTK_TYPE_TREE_VIEW_COLUMN,
801                                                "expand", TRUE,
802                                                "title", _( "File" ),
803                                                NULL ) );
804    rend = gtk_cell_renderer_pixbuf_new( );
805    gtk_tree_view_column_pack_start( col, rend, FALSE );
806    gtk_tree_view_column_add_attribute( col, rend, "pixbuf", FC_ICON );
807    /* add text renderer */
808    rend = gtk_cell_renderer_text_new( );
809    g_object_set( rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL );
810    gtk_tree_view_column_pack_start( col, rend, TRUE );
811    gtk_tree_view_column_set_cell_data_func( col, rend, renderFilename, NULL, NULL );
812    gtk_tree_view_column_set_resizable( col, TRUE );
813    gtk_tree_view_append_column( GTK_TREE_VIEW( view ), col );
814
815    rend = gtk_cell_renderer_progress_new( );
816    col = gtk_tree_view_column_new_with_attributes( _( "Progress" ), rend, NULL );
817    gtk_tree_view_column_set_cell_data_func( col, rend, renderProgress, NULL, NULL );
818    gtk_tree_view_column_set_resizable( col, FALSE );
819    gtk_tree_view_append_column ( GTK_TREE_VIEW( view ), col );
820
821    /* add "enabled" column */
822    rend = gtk_cell_renderer_toggle_new( );
823    col = gtk_tree_view_column_new_with_attributes( _( "Download" ), rend, NULL );
824    gtk_tree_view_column_set_cell_data_func( col, rend, renderDownload, NULL, NULL );
825    gtk_tree_view_column_set_resizable( col, FALSE );
826    gtk_tree_view_append_column ( GTK_TREE_VIEW( view ), col );
827
828    /* add priority column */
829    rend = gtk_cell_renderer_text_new( );
830    col = gtk_tree_view_column_new_with_attributes( _( "Priority" ), rend, NULL );
831    gtk_tree_view_column_set_cell_data_func( col, rend, renderPriority, NULL, NULL );
832    gtk_tree_view_column_set_resizable( col, FALSE );
833    gtk_tree_view_append_column ( GTK_TREE_VIEW( view ), col );
834
835    /* create the scrolled window and stick the view in it */
836    scroll = gtk_scrolled_window_new( NULL, NULL );
837    gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scroll ),
838                                    GTK_POLICY_AUTOMATIC,
839                                    GTK_POLICY_AUTOMATIC );
840    gtk_scrolled_window_set_shadow_type ( GTK_SCROLLED_WINDOW( scroll ),
841                                          GTK_SHADOW_IN );
842    gtk_container_add( GTK_CONTAINER( scroll ), view );
843    gtk_widget_set_size_request ( scroll, -1, 200 );
844
845    ret = scroll;
846    data->view = view;
847    data->top = scroll;
848    g_object_set_data_full( G_OBJECT( ret ), "file-data", data, freeData );
849    file_list_set_torrent( ret, gtor );
850
851    return ret;
852}
853
Note: See TracBrowser for help on using the repository browser.