2 * This file Copyright (C) Mnemosyne LLC
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.
10 * $Id: file-list.c 13243 2012-03-04 13:12:42Z jordan $
16 #include <glib/gi18n.h>
19 #include <libtransmission/transmission.h>
20 #include <libtransmission/utils.h>
22 #include "file-list.h"
28 #define TR_DOWNLOAD_KEY "tr-download-key"
29 #define TR_COLUMN_ID_KEY "tr-model-column-id-key"
30 #define TR_PRIORITY_KEY "tr-priority-key"
34 /* these two fields could be any number at all so long as they're not
35 * TR_PRI_LOW, TR_PRI_NORMAL, TR_PRI_HIGH, TRUE, or FALSE */
60 GtkTreeModel
* model
; /* same object as store, but recast */
61 GtkTreeStore
* store
; /* same object as model, but recast */
68 clearData( FileData
* data
)
72 if( data
->timeout_tag
) {
73 g_source_remove( data
->timeout_tag
);
74 data
->timeout_tag
= 0;
79 freeData( gpointer data
)
92 gboolean resort_needed
;
94 tr_file_stat
* refresh_file_stat
;
101 refreshFilesForeach( GtkTreeModel
* model
,
102 GtkTreePath
* path UNUSED
,
106 struct RefreshData
* refresh_data
= gdata
;
107 FileData
* data
= refresh_data
->file_data
;
114 const gboolean is_file
= !gtk_tree_model_iter_has_child( model
, iter
);
116 gtk_tree_model_get( model
, iter
, FC_ENABLED
, &old_enabled
,
117 FC_PRIORITY
, &old_priority
,
126 tr_torrent
* tor
= refresh_data
->tor
;
127 const tr_info
* inf
= tr_torrentInfo( tor
);
128 const int enabled
= !inf
->files
[index
].dnd
;
129 const int priority
= inf
->files
[index
].priority
;
130 const uint64_t have
= refresh_data
->refresh_file_stat
[index
].bytesCompleted
;
131 const int prog
= size
? (int)((100.0*have
)/size
) : 1;
133 if( (priority
!=old_priority
) || (enabled
!=old_enabled
) || (have
!=old_have
) || (prog
!=old_prog
) )
135 /* Changing a value in the sort column can trigger a resort
136 * which breaks this foreach() call. (See #3529)
137 * As a workaround: if that's about to happen, temporarily disable
138 * sorting until we finish walking the tree. */
139 if( !refresh_data
->resort_needed
)
141 if(( refresh_data
->resort_needed
=
142 (( refresh_data
->sort_column_id
==FC_PRIORITY
) && ( priority
!=old_priority
)) ||
143 (( refresh_data
->sort_column_id
==FC_ENABLED
) && ( enabled
!=old_enabled
))))
145 refresh_data
->resort_needed
= TRUE
;
146 gtk_tree_sortable_set_sort_column_id( GTK_TREE_SORTABLE( data
->model
),
147 GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID
,
148 GTK_SORT_ASCENDING
);
152 gtk_tree_store_set( data
->store
, iter
, FC_PRIORITY
, priority
,
162 uint64_t sub_size
= 0;
165 int enabled
= NOT_SET
;
166 int priority
= NOT_SET
;
168 /* since gtk_tree_model_foreach() is depth-first, we can
169 * get the `sub' info by walking the immediate children */
171 if( gtk_tree_model_iter_children( model
, &child
, iter
) ) do
175 int64_t child_have
, child_size
;
177 gtk_tree_model_get( model
, &child
, FC_SIZE
, &child_size
,
178 FC_HAVE
, &child_have
,
179 FC_PRIORITY
, &child_priority
,
180 FC_ENABLED
, &child_enabled
,
183 sub_size
+= child_size
;
186 if( enabled
== NOT_SET
)
187 enabled
= child_enabled
;
188 else if( enabled
!= child_enabled
)
191 if( priority
== NOT_SET
)
192 priority
= child_priority
;
193 else if( priority
!= child_priority
)
196 while( gtk_tree_model_iter_next( model
, &child
) );
198 prog
= sub_size
? (int)((100.0*have
)/sub_size
) : 1;
200 if( (size
!=sub_size
) || (have
!=old_have
)
201 || (priority
!=old_priority
)
202 || (enabled
!=old_enabled
)
203 || (prog
!=old_prog
) )
206 tr_strlsize( size_str
, sub_size
, sizeof size_str
);
207 gtk_tree_store_set( data
->store
, iter
, FC_SIZE
, sub_size
,
208 FC_SIZE_STR
, size_str
,
210 FC_PRIORITY
, priority
,
217 return FALSE
; /* keep walking */
221 gtr_tree_model_foreach_postorder_subtree( GtkTreeModel
* model
,
222 GtkTreeIter
* parent
,
223 GtkTreeModelForeachFunc func
,
227 if( gtk_tree_model_iter_children( model
, &child
, parent
) ) do
228 gtr_tree_model_foreach_postorder_subtree( model
, &child
, func
, data
);
229 while( gtk_tree_model_iter_next( model
, &child
) );
231 func( model
, NULL
, parent
, data
);
235 gtr_tree_model_foreach_postorder( GtkTreeModel
* model
,
236 GtkTreeModelForeachFunc func
,
240 if( gtk_tree_model_iter_nth_child( model
, &iter
, NULL
, 0 ) ) do
241 gtr_tree_model_foreach_postorder_subtree( model
, &iter
, func
, data
);
242 while( gtk_tree_model_iter_next( model
, &iter
) );
246 refresh( FileData
* data
)
248 tr_torrent
* tor
= gtr_core_find_torrent( data
->core
, data
->torrentId
);
252 gtr_file_list_clear( data
->top
);
258 tr_file_index_t fileCount
;
259 struct RefreshData refresh_data
;
260 GtkTreeSortable
* sortable
= GTK_TREE_SORTABLE( data
->model
);
261 gtk_tree_sortable_get_sort_column_id( sortable
, &sort_column_id
, &order
);
263 refresh_data
.sort_column_id
= sort_column_id
;
264 refresh_data
.resort_needed
= FALSE
;
265 refresh_data
.refresh_file_stat
= tr_torrentFiles( tor
, &fileCount
);
266 refresh_data
.tor
= tor
;
267 refresh_data
.file_data
= data
;
269 gtr_tree_model_foreach_postorder( data
->model
, refreshFilesForeach
, &refresh_data
);
271 if( refresh_data
.resort_needed
)
272 gtk_tree_sortable_set_sort_column_id( sortable
, sort_column_id
, order
);
274 tr_torrentFilesFree( refresh_data
.refresh_file_stat
, fileCount
);
279 refreshModel( gpointer file_data
)
281 refresh( file_data
);
291 GtkTreeSelection
* sel
;
296 getSelectedFilesForeach( GtkTreeModel
* model
,
297 GtkTreePath
* path UNUSED
,
301 const gboolean is_file
= !gtk_tree_model_iter_has_child( model
, iter
);
305 struct ActiveData
* data
= gdata
;
307 /* active means: if it's selected or any ancestor is selected */
309 gboolean is_active
= gtk_tree_selection_iter_is_selected( data
->sel
, iter
);
313 GtkTreeIter walk
= *iter
;
315 while( !is_active
&& gtk_tree_model_iter_parent( model
, &parent
, &walk
) )
317 is_active
= gtk_tree_selection_iter_is_selected( data
->sel
, &parent
);
325 gtk_tree_model_get( model
, iter
, FC_INDEX
, &i
, -1 );
326 g_array_append_val( data
->array
, i
);
330 return FALSE
; /* keep walking */
334 getSelectedFilesAndDescendants( GtkTreeView
* view
)
336 struct ActiveData data
;
338 data
.sel
= gtk_tree_view_get_selection( view
);
339 data
.array
= g_array_new( FALSE
, FALSE
, sizeof( tr_file_index_t
) );
340 gtk_tree_model_foreach( gtk_tree_view_get_model( view
),
341 getSelectedFilesForeach
, &data
);
345 struct SubtreeForeachData
352 getSubtreeForeach( GtkTreeModel
* model
,
357 const gboolean is_file
= !gtk_tree_model_iter_has_child( model
, iter
);
361 struct SubtreeForeachData
* data
= gdata
;
363 if( !gtk_tree_path_compare( path
, data
->path
) || gtk_tree_path_is_descendant( path
, data
->path
) )
366 gtk_tree_model_get( model
, iter
, FC_INDEX
, &i
, -1 );
367 g_array_append_val( data
->array
, i
);
371 return FALSE
; /* keep walking */
375 getSubtree( GtkTreeView
* view
, GtkTreePath
* path
, GArray
* indices
)
377 struct SubtreeForeachData tmp
;
380 gtk_tree_model_foreach( gtk_tree_view_get_model( view
), getSubtreeForeach
, &tmp
);
383 /* if `path' is a selected row, all selected rows are returned.
384 * otherwise, only the row indicated by `path' is returned.
385 * this is for toggling all the selected rows' states in a batch.
388 getActiveFilesForPath( GtkTreeView
* view
, GtkTreePath
* path
)
391 GtkTreeSelection
* sel
= gtk_tree_view_get_selection( view
);
393 if( gtk_tree_selection_path_is_selected( sel
, path
) )
395 /* clicked in a selected row... use the current selection */
396 indices
= getSelectedFilesAndDescendants( view
);
400 /* clicked OUTSIDE of the selected row... just use the clicked row */
401 indices
= g_array_new( FALSE
, FALSE
, sizeof( tr_file_index_t
) );
402 getSubtree( view
, path
, indices
);
413 gtr_file_list_clear( GtkWidget
* w
)
415 gtr_file_list_set_torrent( w
, -1 );
423 GtkTreeStore
* store
;
434 buildTree( GNode
* node
, gpointer gdata
)
437 GtkTreeIter child_iter
;
438 struct build_data
* build
= gdata
;
439 struct row_struct
*child_data
= node
->data
;
440 const gboolean isLeaf
= node
->children
== NULL
;
442 const char * mime_type
= isLeaf
? gtr_get_mime_type_from_filename( child_data
->name
) : DIRECTORY_MIME_TYPE
;
443 GdkPixbuf
* icon
= gtr_get_mime_type_icon( mime_type
, GTK_ICON_SIZE_MENU
, build
->w
);
444 const tr_info
* inf
= tr_torrentInfo( build
->tor
);
445 const int priority
= isLeaf
? inf
->files
[ child_data
->index
].priority
: 0;
446 const gboolean enabled
= isLeaf
? !inf
->files
[ child_data
->index
].dnd
: TRUE
;
447 char * name_esc
= g_markup_escape_text( child_data
->name
, -1 );
449 tr_strlsize( size_str
, child_data
->length
, sizeof size_str
);
451 gtk_tree_store_insert_with_values( build
->store
, &child_iter
, build
->iter
, INT_MAX
,
452 FC_INDEX
, child_data
->index
,
453 FC_LABEL
, child_data
->name
,
454 FC_LABEL_ESC
, name_esc
,
455 FC_SIZE
, child_data
->length
,
456 FC_SIZE_STR
, size_str
,
458 FC_PRIORITY
, priority
,
464 struct build_data b
= *build
;
465 b
.iter
= &child_iter
;
466 g_node_children_foreach( node
, G_TRAVERSE_ALL
, buildTree
, &b
);
470 g_object_unref( icon
);
472 /* we're done with this node */
473 g_free( child_data
->name
);
474 g_free( child_data
);
478 find_child( GNode
* parent
, const char * name
)
480 GNode
* child
= parent
->children
;
482 const struct row_struct
* child_data
= child
->data
;
483 if( ( *child_data
->name
== *name
) && !strcmp( child_data
->name
, name
) )
491 gtr_file_list_set_torrent( GtkWidget
* w
, int torrentId
)
493 GtkTreeStore
* store
;
494 FileData
* data
= g_object_get_data( G_OBJECT( w
), "file-data" );
496 /* unset the old fields */
499 /* instantiate the model */
500 store
= gtk_tree_store_new ( N_FILE_COLS
,
501 GDK_TYPE_PIXBUF
, /* icon */
502 G_TYPE_STRING
, /* label */
503 G_TYPE_STRING
, /* label esc */
504 G_TYPE_INT
, /* prog [0..100] */
505 G_TYPE_UINT
, /* index */
506 G_TYPE_UINT64
, /* size */
507 G_TYPE_STRING
, /* size str */
508 G_TYPE_UINT64
, /* have */
509 G_TYPE_INT
, /* priority */
510 G_TYPE_INT
); /* dl enabled */
513 data
->model
= GTK_TREE_MODEL( store
);
514 data
->torrentId
= torrentId
;
516 /* populate the model */
519 tr_torrent
* tor
= gtr_core_find_torrent( data
->core
, torrentId
);
523 const tr_info
* inf
= tr_torrentInfo( tor
);
524 struct row_struct
* root_data
;
526 struct build_data build
;
528 /* build a GNode tree of the files */
529 root_data
= g_new0( struct row_struct
, 1 );
530 root_data
->name
= g_strdup( tr_torrentName( tor
) );
531 root_data
->index
= -1;
532 root_data
->length
= 0;
533 root
= g_node_new( root_data
);
534 for( i
=0; i
<inf
->fileCount
; ++i
) {
536 GNode
* parent
= root
;
537 const tr_file
* file
= &inf
->files
[i
];
538 char ** tokens
= g_strsplit( file
->name
, G_DIR_SEPARATOR_S
, 0 );
539 for( j
=0; tokens
[j
]; ++j
) {
540 const gboolean isLeaf
= tokens
[j
+1] == NULL
;
541 const char * name
= tokens
[j
];
542 GNode
* node
= find_child( parent
, name
);
544 struct row_struct
* row
= g_new( struct row_struct
, 1 );
545 row
->name
= g_strdup( name
);
546 row
->index
= isLeaf
? (int)i
: -1;
547 row
->length
= isLeaf
? file
->length
: 0;
548 node
= g_node_new( row
);
549 g_node_append( parent
, node
);
553 g_strfreev( tokens
);
556 /* now, add them to the model */
559 build
.store
= data
->store
;
561 g_node_children_foreach( root
, G_TRAVERSE_ALL
, buildTree
, &build
);
564 g_node_destroy( root
);
565 g_free( root_data
->name
);
570 data
->timeout_tag
= gdk_threads_add_timeout_seconds( SECONDARY_WINDOW_REFRESH_INTERVAL_SECONDS
, refreshModel
, data
);
573 gtk_tree_view_set_model( GTK_TREE_VIEW( data
->view
), data
->model
);
574 gtk_tree_view_expand_all( GTK_TREE_VIEW( data
->view
) );
575 g_object_unref( data
->model
);
583 renderDownload( GtkTreeViewColumn
* column UNUSED
,
584 GtkCellRenderer
* renderer
,
585 GtkTreeModel
* model
,
587 gpointer data UNUSED
)
590 gtk_tree_model_get( model
, iter
, FC_ENABLED
, &enabled
, -1 );
591 g_object_set( renderer
, "inconsistent", (enabled
==MIXED
),
592 "active", (enabled
==TRUE
),
597 renderPriority( GtkTreeViewColumn
* column UNUSED
,
598 GtkCellRenderer
* renderer
,
599 GtkTreeModel
* model
,
601 gpointer data UNUSED
)
605 gtk_tree_model_get( model
, iter
, FC_PRIORITY
, &priority
, -1 );
607 case TR_PRI_HIGH
: text
= _( "High" ); break;
608 case TR_PRI_NORMAL
: text
= _( "Normal" ); break;
609 case TR_PRI_LOW
: text
= _( "Low" ); break;
610 default: text
= _( "Mixed" ); break;
612 g_object_set( renderer
, "text", text
, NULL
);
615 /* build a filename from tr_torrentGetCurrentDir() + the model's FC_LABELs */
617 buildFilename( tr_torrent
* tor
, GtkTreeModel
* model
,
618 GtkTreePath
* path
, GtkTreeIter
* iter
)
622 GtkTreeIter parent
= *iter
;
623 int n
= gtk_tree_path_get_depth( path
);
624 char ** tokens
= g_new0( char*, n
+ 2 );
625 tokens
[0] = g_strdup( tr_torrentGetCurrentDir( tor
) );
628 gtk_tree_model_get( model
, &child
, FC_LABEL
, &tokens
[n
--], -1 );
629 } while( gtk_tree_model_iter_parent( model
, &parent
, &child
) );
630 ret
= g_build_filenamev( tokens
);
631 g_strfreev( tokens
);
636 onRowActivated( GtkTreeView
* view
, GtkTreePath
* path
,
637 GtkTreeViewColumn
* col UNUSED
, gpointer gdata
)
639 gboolean handled
= FALSE
;
640 FileData
* data
= gdata
;
641 tr_torrent
* tor
= gtr_core_find_torrent( data
->core
, data
->torrentId
);
646 GtkTreeModel
* model
= gtk_tree_view_get_model( view
);
648 if( gtk_tree_model_get_iter( model
, &iter
, path
) )
651 char * filename
= buildFilename( tor
, model
, path
, &iter
);
652 gtk_tree_model_get( model
, &iter
, FC_PROG
, &prog
, -1 );
654 /* if the file's not done, walk up the directory tree until we find
655 * an ancestor that exists, and open that instead */
656 if( filename
&& ( prog
<100 || !g_file_test( filename
, G_FILE_TEST_EXISTS
) ) ) do
658 char * tmp
= g_path_get_dirname( filename
);
662 while( filename
&& *filename
&& !g_file_test( filename
, G_FILE_TEST_EXISTS
) );
664 if(( handled
= filename
&& *filename
))
665 gtr_open_file( filename
);
673 onViewPathToggled( GtkTreeView
* view
,
674 GtkTreeViewColumn
* col
,
680 gboolean handled
= FALSE
;
685 cid
= GPOINTER_TO_INT( g_object_get_data( G_OBJECT( col
), TR_COLUMN_ID_KEY
) );
686 tor
= gtr_core_find_torrent( data
->core
, data
->torrentId
);
687 if( ( tor
!= NULL
) && ( ( cid
== FC_PRIORITY
) || ( cid
== FC_ENABLED
) ) )
690 GArray
* indices
= getActiveFilesForPath( view
, path
);
691 GtkTreeModel
* model
= data
->model
;
693 gtk_tree_model_get_iter( model
, &iter
, path
);
695 if( cid
== FC_PRIORITY
)
698 gtk_tree_model_get( model
, &iter
, FC_PRIORITY
, &priority
, -1 );
700 case TR_PRI_NORMAL
: priority
= TR_PRI_HIGH
; break;
701 case TR_PRI_HIGH
: priority
= TR_PRI_LOW
; break;
702 default: priority
= TR_PRI_NORMAL
; break;
704 tr_torrentSetFilePriorities( tor
,
705 (tr_file_index_t
*) indices
->data
,
706 (tr_file_index_t
) indices
->len
,
712 gtk_tree_model_get( model
, &iter
, FC_ENABLED
, &enabled
, -1 );
715 tr_torrentSetFileDLs( tor
,
716 (tr_file_index_t
*) indices
->data
,
717 (tr_file_index_t
) indices
->len
,
722 g_array_free( indices
, TRUE
);
730 * @note 'col' and 'path' are assumed not to be NULL.
733 getAndSelectEventPath( GtkTreeView
* treeview
,
734 GdkEventButton
* event
,
735 GtkTreeViewColumn
** col
,
736 GtkTreePath
** path
)
738 GtkTreeSelection
* sel
;
740 if( gtk_tree_view_get_path_at_pos( treeview
,
742 path
, col
, NULL
, NULL
) )
744 sel
= gtk_tree_view_get_selection( treeview
);
745 if( !gtk_tree_selection_path_is_selected( sel
, *path
) )
747 gtk_tree_selection_unselect_all( sel
);
748 gtk_tree_selection_select_path( sel
, *path
);
757 onViewButtonPressed( GtkWidget
* w
, GdkEventButton
* event
, gpointer gdata
)
759 GtkTreeViewColumn
* col
;
760 GtkTreePath
* path
= NULL
;
761 gboolean handled
= FALSE
;
762 GtkTreeView
* treeview
= GTK_TREE_VIEW( w
);
763 FileData
* data
= gdata
;
765 if( ( event
->type
== GDK_BUTTON_PRESS
)
766 && ( event
->button
== 1 )
767 && !( event
->state
& ( GDK_SHIFT_MASK
| GDK_CONTROL_MASK
) )
768 && getAndSelectEventPath( treeview
, event
, &col
, &path
) )
770 handled
= onViewPathToggled( treeview
, col
, path
, data
);
773 gtk_tree_path_free( path
);
780 gtr_file_list_new( TrCore
* core
, int torrentId
)
787 GtkCellRenderer
* rend
;
788 GtkTreeSelection
* sel
;
789 GtkTreeViewColumn
* col
;
790 GtkTreeView
* tree_view
;
792 PangoLayout
* pango_layout
;
793 PangoContext
* pango_context
;
794 PangoFontDescription
* pango_font_description
;
795 FileData
* data
= g_new0( FileData
, 1 );
799 /* create the view */
800 view
= gtk_tree_view_new( );
801 tree_view
= GTK_TREE_VIEW( view
);
802 gtk_tree_view_set_rules_hint( tree_view
, TRUE
);
803 gtk_container_set_border_width( GTK_CONTAINER( view
), GUI_PAD_BIG
);
804 g_signal_connect( view
, "button-press-event",
805 G_CALLBACK( onViewButtonPressed
), data
);
806 g_signal_connect( view
, "row_activated",
807 G_CALLBACK( onRowActivated
), data
);
808 g_signal_connect( view
, "button-release-event",
809 G_CALLBACK( on_tree_view_button_released
), NULL
);
812 pango_context
= gtk_widget_create_pango_context( view
);
813 pango_font_description
= pango_font_description_copy( pango_context_get_font_description( pango_context
) );
814 size
= pango_font_description_get_size( pango_font_description
);
815 pango_font_description_set_size( pango_font_description
, size
* 0.8 );
816 g_object_unref( G_OBJECT( pango_context
) );
819 sel
= gtk_tree_view_get_selection( tree_view
);
820 gtk_tree_selection_set_mode( sel
, GTK_SELECTION_MULTIPLE
);
821 gtk_tree_view_expand_all( tree_view
);
822 gtk_tree_view_set_search_column( tree_view
, FC_LABEL
);
824 /* add file column */
825 col
= GTK_TREE_VIEW_COLUMN ( g_object_new ( GTK_TYPE_TREE_VIEW_COLUMN
,
827 "title", _( "Name" ),
829 gtk_tree_view_column_set_resizable( col
, TRUE
);
830 rend
= gtk_cell_renderer_pixbuf_new( );
831 gtk_tree_view_column_pack_start( col
, rend
, FALSE
);
832 gtk_tree_view_column_add_attribute( col
, rend
, "pixbuf", FC_ICON
);
833 /* add text renderer */
834 rend
= gtk_cell_renderer_text_new( );
835 g_object_set( rend
, "ellipsize", PANGO_ELLIPSIZE_END
, "font-desc", pango_font_description
, NULL
);
836 gtk_tree_view_column_pack_start( col
, rend
, TRUE
);
837 gtk_tree_view_column_set_attributes( col
, rend
, "text", FC_LABEL
, NULL
);
838 gtk_tree_view_column_set_sort_column_id( col
, FC_LABEL
);
839 gtk_tree_view_append_column( tree_view
, col
);
841 /* add "size" column */
843 rend
= gtk_cell_renderer_text_new( );
844 g_object_set( rend
, "alignment", PANGO_ALIGN_RIGHT
,
845 "font-desc", pango_font_description
,
850 col
= gtk_tree_view_column_new_with_attributes( title
, rend
, NULL
);
851 gtk_tree_view_column_set_sizing( col
, GTK_TREE_VIEW_COLUMN_GROW_ONLY
);
852 gtk_tree_view_column_set_sort_column_id( col
, FC_SIZE
);
853 gtk_tree_view_column_set_attributes( col
, rend
, "text", FC_SIZE_STR
, NULL
);
854 gtk_tree_view_append_column( tree_view
, col
);
856 /* add "progress" column */
858 pango_layout
= gtk_widget_create_pango_layout( view
, title
);
859 pango_layout_get_pixel_size( pango_layout
, &width
, NULL
);
860 width
+= 30; /* room for the sort indicator */
861 g_object_unref( G_OBJECT( pango_layout
) );
862 rend
= gtk_cell_renderer_progress_new( );
863 col
= gtk_tree_view_column_new_with_attributes( title
, rend
, "value", FC_PROG
, NULL
);
864 gtk_tree_view_column_set_fixed_width( col
, width
);
865 gtk_tree_view_column_set_sizing( col
, GTK_TREE_VIEW_COLUMN_FIXED
);
866 gtk_tree_view_column_set_sort_column_id( col
, FC_PROG
);
867 gtk_tree_view_append_column( tree_view
, col
);
869 /* add "enabled" column */
870 title
= _( "Download" );
871 pango_layout
= gtk_widget_create_pango_layout( view
, title
);
872 pango_layout_get_pixel_size( pango_layout
, &width
, NULL
);
873 width
+= 30; /* room for the sort indicator */
874 g_object_unref( G_OBJECT( pango_layout
) );
875 rend
= gtk_cell_renderer_toggle_new( );
876 col
= gtk_tree_view_column_new_with_attributes( title
, rend
, NULL
);
877 g_object_set_data( G_OBJECT( col
), TR_COLUMN_ID_KEY
,
878 GINT_TO_POINTER( FC_ENABLED
) );
879 gtk_tree_view_column_set_fixed_width( col
, width
);
880 gtk_tree_view_column_set_sizing( col
, GTK_TREE_VIEW_COLUMN_FIXED
);
881 gtk_tree_view_column_set_cell_data_func( col
, rend
, renderDownload
, NULL
, NULL
);
882 gtk_tree_view_column_set_sort_column_id( col
, FC_ENABLED
);
883 gtk_tree_view_append_column( tree_view
, col
);
885 /* add priority column */
886 title
= _( "Priority" );
887 pango_layout
= gtk_widget_create_pango_layout( view
, title
);
888 pango_layout_get_pixel_size( pango_layout
, &width
, NULL
);
889 width
+= 30; /* room for the sort indicator */
890 g_object_unref( G_OBJECT( pango_layout
) );
891 rend
= gtk_cell_renderer_text_new( );
892 g_object_set( rend
, "xalign", (gfloat
)0.5, "yalign", (gfloat
)0.5, NULL
);
893 col
= gtk_tree_view_column_new_with_attributes( title
, rend
, NULL
);
894 g_object_set_data( G_OBJECT( col
), TR_COLUMN_ID_KEY
,
895 GINT_TO_POINTER( FC_PRIORITY
) );
896 gtk_tree_view_column_set_fixed_width( col
, width
);
897 gtk_tree_view_column_set_sizing( col
, GTK_TREE_VIEW_COLUMN_FIXED
);
898 gtk_tree_view_column_set_sort_column_id( col
, FC_PRIORITY
);
899 gtk_tree_view_column_set_cell_data_func( col
, rend
, renderPriority
, NULL
, NULL
);
900 gtk_tree_view_append_column( tree_view
, col
);
902 /* add tooltip to tree */
903 gtk_tree_view_set_tooltip_column( tree_view
, FC_LABEL_ESC
);
905 /* create the scrolled window and stick the view in it */
906 scroll
= gtk_scrolled_window_new( NULL
, NULL
);
907 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scroll
),
908 GTK_POLICY_AUTOMATIC
,
909 GTK_POLICY_AUTOMATIC
);
910 gtk_scrolled_window_set_shadow_type ( GTK_SCROLLED_WINDOW( scroll
),
912 gtk_container_add( GTK_CONTAINER( scroll
), view
);
913 gtk_widget_set_size_request ( scroll
, -1, 200 );
918 g_object_set_data_full( G_OBJECT( ret
), "file-data", data
, freeData
);
919 gtr_file_list_set_torrent( ret
, torrentId
);
921 pango_font_description_free( pango_font_description
);