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

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

(trunk gtk) #2894 "tr_torrentGetFileDL() and tr_torrentGetFilePriority() should go away" -- finished removing them from the GTK+ client

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