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

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

happy new year!

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