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

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

(trunk gtk) make it easier to change the periodic refresh intervals at the code level

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