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

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

(trunk gtk) #2337: GTK+ Tree view columns in the files dialog should be resizable. Thanks to lethalman for the patch

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