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 12036 2011-02-24 16:00:34Z jordan $
17 #include <glib/gi18n.h>
20 #include <libtransmission/transmission.h>
22 #include "file-list.h"
27 #define TR_DOWNLOAD_KEY "tr-download-key"
28 #define TR_COLUMN_ID_KEY "tr-model-column-id-key"
29 #define TR_PRIORITY_KEY "tr-priority-key"
33 /* these two fields could be any number at all so long as they're not
34 * TR_PRI_LOW, TR_PRI_NORMAL, TR_PRI_HIGH, TRUE, or FALSE */
58 GtkTreeModel
* model
; /* same object as store, but recast */
59 GtkTreeStore
* store
; /* same object as model, but recast */
66 clearData( FileData
* data
)
70 if( data
->timeout_tag
) {
71 g_source_remove( data
->timeout_tag
);
72 data
->timeout_tag
= 0;
77 freeData( gpointer data
)
90 gboolean resort_needed
;
92 tr_file_stat
* refresh_file_stat
;
99 refreshFilesForeach( GtkTreeModel
* model
,
100 GtkTreePath
* path UNUSED
,
104 struct RefreshData
* refresh_data
= gdata
;
105 FileData
* data
= refresh_data
->file_data
;
112 const gboolean is_file
= !gtk_tree_model_iter_has_child( model
, iter
);
114 gtk_tree_model_get( model
, iter
, FC_ENABLED
, &old_enabled
,
115 FC_PRIORITY
, &old_priority
,
124 tr_torrent
* tor
= refresh_data
->tor
;
125 const tr_info
* inf
= tr_torrentInfo( tor
);
126 const int enabled
= !inf
->files
[index
].dnd
;
127 const int priority
= inf
->files
[index
].priority
;
128 const uint64_t have
= refresh_data
->refresh_file_stat
[index
].bytesCompleted
;
129 const int prog
= size
? (int)((100.0*have
)/size
) : 1;
131 if( (priority
!=old_priority
) || (enabled
!=old_enabled
) || (have
!=old_have
) || (prog
!=old_prog
) )
133 /* Changing a value in the sort column can trigger a resort
134 * which breaks this foreach() call. (See #3529)
135 * As a workaround: if that's about to happen, temporarily disable
136 * sorting until we finish walking the tree. */
137 if( !refresh_data
->resort_needed
)
139 if(( refresh_data
->resort_needed
=
140 (( refresh_data
->sort_column_id
==FC_PRIORITY
) && ( priority
!=old_priority
)) ||
141 (( refresh_data
->sort_column_id
==FC_ENABLED
) && ( enabled
!=old_enabled
))))
143 refresh_data
->resort_needed
= TRUE
;
144 gtk_tree_sortable_set_sort_column_id( GTK_TREE_SORTABLE( data
->model
),
145 GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID
,
146 GTK_SORT_ASCENDING
);
150 gtk_tree_store_set( data
->store
, iter
, FC_PRIORITY
, priority
,
160 uint64_t sub_size
= 0;
163 int enabled
= NOT_SET
;
164 int priority
= NOT_SET
;
166 /* since gtk_tree_model_foreach() is depth-first, we can
167 * get the `sub' info by walking the immediate children */
169 if( gtk_tree_model_iter_children( model
, &child
, iter
) ) do
173 int64_t child_have
, child_size
;
175 gtk_tree_model_get( model
, &child
, FC_SIZE
, &child_size
,
176 FC_HAVE
, &child_have
,
177 FC_PRIORITY
, &child_priority
,
178 FC_ENABLED
, &child_enabled
,
181 sub_size
+= child_size
;
184 if( enabled
== NOT_SET
)
185 enabled
= child_enabled
;
186 else if( enabled
!= child_enabled
)
189 if( priority
== NOT_SET
)
190 priority
= child_priority
;
191 else if( priority
!= child_priority
)
194 while( gtk_tree_model_iter_next( model
, &child
) );
196 prog
= sub_size
? (int)((100.0*have
)/sub_size
) : 1;
198 if( (size
!=sub_size
) || (have
!=old_have
)
199 || (priority
!=old_priority
)
200 || (enabled
!=old_enabled
)
201 || (prog
!=old_prog
) )
204 tr_strlsize( size_str
, sub_size
, sizeof size_str
);
205 gtk_tree_store_set( data
->store
, iter
, FC_SIZE
, sub_size
,
206 FC_SIZE_STR
, size_str
,
208 FC_PRIORITY
, priority
,
215 return FALSE
; /* keep walking */
219 gtr_tree_model_foreach_postorder_subtree( GtkTreeModel
* model
,
220 GtkTreeIter
* parent
,
221 GtkTreeModelForeachFunc func
,
225 if( gtk_tree_model_iter_children( model
, &child
, parent
) ) do
226 gtr_tree_model_foreach_postorder_subtree( model
, &child
, func
, data
);
227 while( gtk_tree_model_iter_next( model
, &child
) );
229 func( model
, NULL
, parent
, data
);
233 gtr_tree_model_foreach_postorder( GtkTreeModel
* model
,
234 GtkTreeModelForeachFunc func
,
238 if( gtk_tree_model_get_iter_first( model
, &iter
) ) do
239 gtr_tree_model_foreach_postorder_subtree( model
, &iter
, func
, data
);
240 while( gtk_tree_model_iter_next( model
, &iter
) );
244 refresh( FileData
* data
)
246 tr_torrent
* tor
= NULL
;
247 tr_session
* session
= tr_core_session( data
->core
);
249 if( session
!= NULL
)
250 tor
= tr_torrentFindFromId( session
, data
->torrentId
);
254 gtr_file_list_clear( data
->top
);
260 tr_file_index_t fileCount
;
261 struct RefreshData refresh_data
;
262 GtkTreeSortable
* sortable
= GTK_TREE_SORTABLE( data
->model
);
263 gtk_tree_sortable_get_sort_column_id( sortable
, &sort_column_id
, &order
);
265 refresh_data
.sort_column_id
= sort_column_id
;
266 refresh_data
.resort_needed
= FALSE
;
267 refresh_data
.refresh_file_stat
= tr_torrentFiles( tor
, &fileCount
);
268 refresh_data
.tor
= tr_torrentFindFromId( session
, data
->torrentId
);
269 refresh_data
.file_data
= data
;
271 gtr_tree_model_foreach_postorder( data
->model
, refreshFilesForeach
, &refresh_data
);
273 if( refresh_data
.resort_needed
)
274 gtk_tree_sortable_set_sort_column_id( sortable
, sort_column_id
, order
);
276 tr_torrentFilesFree( refresh_data
.refresh_file_stat
, fileCount
);
281 refreshModel( gpointer file_data
)
283 refresh( file_data
);
293 GtkTreeSelection
* sel
;
298 getSelectedFilesForeach( GtkTreeModel
* model
,
299 GtkTreePath
* path UNUSED
,
303 const gboolean is_file
= !gtk_tree_model_iter_has_child( model
, iter
);
307 struct ActiveData
* data
= gdata
;
309 /* active means: if it's selected or any ancestor is selected */
311 gboolean is_active
= gtk_tree_selection_iter_is_selected( data
->sel
, iter
);
315 GtkTreeIter walk
= *iter
;
317 while( !is_active
&& gtk_tree_model_iter_parent( model
, &parent
, &walk
) )
319 is_active
= gtk_tree_selection_iter_is_selected( data
->sel
, &parent
);
327 gtk_tree_model_get( model
, iter
, FC_INDEX
, &i
, -1 );
328 g_array_append_val( data
->array
, i
);
332 return FALSE
; /* keep walking */
336 getSelectedFilesAndDescendants( GtkTreeView
* view
)
338 struct ActiveData data
;
340 data
.sel
= gtk_tree_view_get_selection( view
);
341 data
.array
= g_array_new( FALSE
, FALSE
, sizeof( tr_file_index_t
) );
342 gtk_tree_model_foreach( gtk_tree_view_get_model( view
),
343 getSelectedFilesForeach
, &data
);
347 struct SubtreeForeachData
354 getSubtreeForeach( GtkTreeModel
* model
,
359 const gboolean is_file
= !gtk_tree_model_iter_has_child( model
, iter
);
363 struct SubtreeForeachData
* data
= gdata
;
365 if( !gtk_tree_path_compare( path
, data
->path
) || gtk_tree_path_is_descendant( path
, data
->path
) )
368 gtk_tree_model_get( model
, iter
, FC_INDEX
, &i
, -1 );
369 g_array_append_val( data
->array
, i
);
373 return FALSE
; /* keep walking */
377 getSubtree( GtkTreeView
* view
, GtkTreePath
* path
, GArray
* indices
)
379 struct SubtreeForeachData tmp
;
382 gtk_tree_model_foreach( gtk_tree_view_get_model( view
), getSubtreeForeach
, &tmp
);
385 /* if `path' is a selected row, all selected rows are returned.
386 * otherwise, only the row indicated by `path' is returned.
387 * this is for toggling all the selected rows' states in a batch.
390 getActiveFilesForPath( GtkTreeView
* view
, GtkTreePath
* path
)
393 GtkTreeSelection
* sel
= gtk_tree_view_get_selection( view
);
395 if( gtk_tree_selection_path_is_selected( sel
, path
) )
397 /* clicked in a selected row... use the current selection */
398 indices
= getSelectedFilesAndDescendants( view
);
402 /* clicked OUTSIDE of the selected row... just use the clicked row */
403 indices
= g_array_new( FALSE
, FALSE
, sizeof( tr_file_index_t
) );
404 getSubtree( view
, path
, indices
);
415 gtr_file_list_clear( GtkWidget
* w
)
417 gtr_file_list_set_torrent( w
, -1 );
425 GtkTreeStore
* store
;
436 buildTree( GNode
* node
, gpointer gdata
)
439 GtkTreeIter child_iter
;
440 struct build_data
* build
= gdata
;
441 struct row_struct
*child_data
= node
->data
;
442 const gboolean isLeaf
= node
->children
== NULL
;
444 const char * mime_type
= isLeaf
? gtr_get_mime_type_from_filename( child_data
->name
) : DIRECTORY_MIME_TYPE
;
445 GdkPixbuf
* icon
= gtr_get_mime_type_icon( mime_type
, GTK_ICON_SIZE_MENU
, build
->w
);
446 const tr_info
* inf
= tr_torrentInfo( build
->tor
);
447 const int priority
= isLeaf
? inf
->files
[ child_data
->index
].priority
: 0;
448 const gboolean enabled
= isLeaf
? !inf
->files
[ child_data
->index
].dnd
: TRUE
;
450 tr_strlsize( size_str
, child_data
->length
, sizeof size_str
);
452 #if GTK_CHECK_VERSION(2,10,0)
453 gtk_tree_store_insert_with_values( build
->store
, &child_iter
, build
->iter
, INT_MAX
,
454 FC_INDEX
, child_data
->index
,
455 FC_LABEL
, child_data
->name
,
456 FC_SIZE
, child_data
->length
,
457 FC_SIZE_STR
, size_str
,
459 FC_PRIORITY
, priority
,
463 gtk_tree_store_append( build
->store
, &child_iter
, build
->iter
);
464 gtk_tree_store_set( build
->store
, &child_iter
,
465 FC_INDEX
, child_data
->index
,
466 FC_LABEL
, child_data
->name
,
467 FC_SIZE
, child_data
->length
,
468 FC_SIZE_STR
, size_str
,
470 FC_PRIORITY
, priority
,
477 struct build_data b
= *build
;
478 b
.iter
= &child_iter
;
479 g_node_children_foreach( node
, G_TRAVERSE_ALL
, buildTree
, &b
);
482 g_object_unref( icon
);
484 /* we're done with this node */
485 g_free( child_data
->name
);
486 g_free( child_data
);
490 find_child( GNode
* parent
, const char * name
)
492 GNode
* child
= parent
->children
;
494 const struct row_struct
* child_data
= child
->data
;
495 if( ( *child_data
->name
== *name
) && !strcmp( child_data
->name
, name
) )
503 gtr_file_list_set_torrent( GtkWidget
* w
, int torrentId
)
505 GtkTreeStore
* store
;
506 FileData
* data
= g_object_get_data( G_OBJECT( w
), "file-data" );
508 /* unset the old fields */
511 /* instantiate the model */
512 store
= gtk_tree_store_new ( N_FILE_COLS
,
513 GDK_TYPE_PIXBUF
, /* icon */
514 G_TYPE_STRING
, /* label */
515 G_TYPE_INT
, /* prog [0..100] */
516 G_TYPE_UINT
, /* index */
517 G_TYPE_UINT64
, /* size */
518 G_TYPE_STRING
, /* size str */
519 G_TYPE_UINT64
, /* have */
520 G_TYPE_INT
, /* priority */
521 G_TYPE_INT
); /* dl enabled */
524 data
->model
= GTK_TREE_MODEL( store
);
525 data
->torrentId
= torrentId
;
527 /* populate the model */
530 tr_session
* session
= tr_core_session( data
->core
);
531 tr_torrent
* tor
= tr_torrentFindFromId( session
, torrentId
);
535 const tr_info
* inf
= tr_torrentInfo( tor
);
536 struct row_struct
* root_data
;
538 struct build_data build
;
540 /* build a GNode tree of the files */
541 root_data
= g_new0( struct row_struct
, 1 );
542 root_data
->name
= g_strdup( inf
->name
);
543 root_data
->index
= -1;
544 root_data
->length
= 0;
545 root
= g_node_new( root_data
);
546 for( i
=0; i
<inf
->fileCount
; ++i
) {
548 GNode
* parent
= root
;
549 const tr_file
* file
= &inf
->files
[i
];
550 char ** tokens
= g_strsplit( file
->name
, G_DIR_SEPARATOR_S
, 0 );
551 for( j
=0; tokens
[j
]; ++j
) {
552 const gboolean isLeaf
= tokens
[j
+1] == NULL
;
553 const char * name
= tokens
[j
];
554 GNode
* node
= find_child( parent
, name
);
556 struct row_struct
* row
= g_new( struct row_struct
, 1 );
557 row
->name
= g_strdup( name
);
558 row
->index
= isLeaf
? (int)i
: -1;
559 row
->length
= isLeaf
? file
->length
: 0;
560 node
= g_node_new( row
);
561 g_node_append( parent
, node
);
565 g_strfreev( tokens
);
568 /* now, add them to the model */
571 build
.store
= data
->store
;
573 g_node_children_foreach( root
, G_TRAVERSE_ALL
, buildTree
, &build
);
576 g_node_destroy( root
);
577 g_free( root_data
->name
);
582 data
->timeout_tag
= gtr_timeout_add_seconds( SECONDARY_WINDOW_REFRESH_INTERVAL_SECONDS
, refreshModel
, data
);
585 gtk_tree_view_set_model( GTK_TREE_VIEW( data
->view
), data
->model
);
586 gtk_tree_view_expand_all( GTK_TREE_VIEW( data
->view
) );
594 renderDownload( GtkTreeViewColumn
* column UNUSED
,
595 GtkCellRenderer
* renderer
,
596 GtkTreeModel
* model
,
598 gpointer data UNUSED
)
601 gtk_tree_model_get( model
, iter
, FC_ENABLED
, &enabled
, -1 );
602 g_object_set( renderer
, "inconsistent", (enabled
==MIXED
),
603 "active", (enabled
==TRUE
),
608 renderPriority( GtkTreeViewColumn
* column UNUSED
,
609 GtkCellRenderer
* renderer
,
610 GtkTreeModel
* model
,
612 gpointer data UNUSED
)
616 gtk_tree_model_get( model
, iter
, FC_PRIORITY
, &priority
, -1 );
618 case TR_PRI_HIGH
: text
= _( "High" ); break;
619 case TR_PRI_NORMAL
: text
= _( "Normal" ); break;
620 case TR_PRI_LOW
: text
= _( "Low" ); break;
621 default: text
= _( "Mixed" ); break;
623 g_object_set( renderer
, "text", text
, NULL
);
626 /* build a filename from tr_torrentGetCurrentDir() + the model's FC_LABELs */
628 buildFilename( tr_torrent
* tor
, GtkTreeModel
* model
,
629 GtkTreePath
* path
, GtkTreeIter
* iter
)
633 GtkTreeIter parent
= *iter
;
634 int n
= gtk_tree_path_get_depth( path
);
635 char ** tokens
= g_new0( char*, n
+ 2 );
636 tokens
[0] = g_strdup( tr_torrentGetCurrentDir( tor
) );
639 gtk_tree_model_get( model
, &child
, FC_LABEL
, &tokens
[n
--], -1 );
640 } while( gtk_tree_model_iter_parent( model
, &parent
, &child
) );
641 ret
= g_build_filenamev( tokens
);
642 g_strfreev( tokens
);
647 onRowActivated( GtkTreeView
* view
, GtkTreePath
* path
,
648 GtkTreeViewColumn
* col UNUSED
, gpointer gdata
)
650 gboolean handled
= FALSE
;
651 FileData
* data
= gdata
;
652 tr_torrent
* tor
= tr_torrentFindFromId( tr_core_session( data
->core
), data
->torrentId
);
657 GtkTreeModel
* model
= gtk_tree_view_get_model( view
);
659 if( gtk_tree_model_get_iter( model
, &iter
, path
) )
662 char * filename
= buildFilename( tor
, model
, path
, &iter
);
663 gtk_tree_model_get( model
, &iter
, FC_PROG
, &prog
, -1 );
665 /* if the file's not done, walk up the directory tree until we find
666 * an ancestor that exists, and open that instead */
667 if( filename
&& ( prog
<100 || !g_file_test( filename
, G_FILE_TEST_EXISTS
) ) ) do
669 char * tmp
= g_path_get_dirname( filename
);
673 while( filename
&& *filename
&& !g_file_test( filename
, G_FILE_TEST_EXISTS
) );
675 if(( handled
= filename
&& *filename
))
676 gtr_open_file( filename
);
684 onViewPathToggled( GtkTreeView
* view
,
685 GtkTreeViewColumn
* col
,
691 gboolean handled
= FALSE
;
696 cid
= GPOINTER_TO_INT( g_object_get_data( G_OBJECT( col
), TR_COLUMN_ID_KEY
) );
697 tor
= tr_torrentFindFromId( tr_core_session( data
->core
), data
->torrentId
);
698 if( ( tor
!= NULL
) && ( ( cid
== FC_PRIORITY
) || ( cid
== FC_ENABLED
) ) )
701 GArray
* indices
= getActiveFilesForPath( view
, path
);
702 GtkTreeModel
* model
= data
->model
;
704 gtk_tree_model_get_iter( model
, &iter
, path
);
706 if( cid
== FC_PRIORITY
)
709 gtk_tree_model_get( model
, &iter
, FC_PRIORITY
, &priority
, -1 );
711 case TR_PRI_NORMAL
: priority
= TR_PRI_HIGH
; break;
712 case TR_PRI_HIGH
: priority
= TR_PRI_LOW
; break;
713 default: priority
= TR_PRI_NORMAL
; break;
715 tr_torrentSetFilePriorities( tor
,
716 (tr_file_index_t
*) indices
->data
,
717 (tr_file_index_t
) indices
->len
,
723 gtk_tree_model_get( model
, &iter
, FC_ENABLED
, &enabled
, -1 );
726 tr_torrentSetFileDLs( tor
,
727 (tr_file_index_t
*) indices
->data
,
728 (tr_file_index_t
) indices
->len
,
733 g_array_free( indices
, TRUE
);
741 * @note 'col' and 'path' are assumed not to be NULL.
744 getAndSelectEventPath( GtkTreeView
* treeview
,
745 GdkEventButton
* event
,
746 GtkTreeViewColumn
** col
,
747 GtkTreePath
** path
)
749 GtkTreeSelection
* sel
;
751 if( gtk_tree_view_get_path_at_pos( treeview
,
753 path
, col
, NULL
, NULL
) )
755 sel
= gtk_tree_view_get_selection( treeview
);
756 if( !gtk_tree_selection_path_is_selected( sel
, *path
) )
758 gtk_tree_selection_unselect_all( sel
);
759 gtk_tree_selection_select_path( sel
, *path
);
768 onViewButtonPressed( GtkWidget
* w
, GdkEventButton
* event
, gpointer gdata
)
771 GtkTreeViewColumn
* col
= NULL
;
772 GtkTreePath
* path
= NULL
;
773 FileData
* data
= gdata
;
774 gboolean handled
= FALSE
;
775 GtkTreeView
* treeview
= GTK_TREE_VIEW( w
);
777 tor
= tr_torrentFindFromId( tr_core_session( data
->core
),
782 if( event
->type
== GDK_BUTTON_PRESS
&& event
->button
== 1
783 && !( event
->state
& ( GDK_SHIFT_MASK
| GDK_CONTROL_MASK
) )
784 && getAndSelectEventPath( treeview
, event
, &col
, &path
) )
786 handled
= onViewPathToggled( treeview
, col
, path
, data
);
789 gtk_tree_path_free( path
);
794 gtr_file_list_new( TrCore
* core
, int torrentId
)
801 GtkCellRenderer
* rend
;
802 GtkTreeSelection
* sel
;
803 GtkTreeViewColumn
* col
;
804 GtkTreeView
* tree_view
;
806 PangoLayout
* pango_layout
;
807 PangoContext
* pango_context
;
808 PangoFontDescription
* pango_font_description
;
809 FileData
* data
= g_new0( FileData
, 1 );
813 /* create the view */
814 view
= gtk_tree_view_new( );
815 tree_view
= GTK_TREE_VIEW( view
);
816 gtk_tree_view_set_rules_hint( tree_view
, TRUE
);
817 gtk_container_set_border_width( GTK_CONTAINER( view
), GUI_PAD_BIG
);
818 g_signal_connect( view
, "button-press-event",
819 G_CALLBACK( onViewButtonPressed
), data
);
820 g_signal_connect( view
, "row_activated",
821 G_CALLBACK( onRowActivated
), data
);
822 g_signal_connect( view
, "button-release-event",
823 G_CALLBACK( on_tree_view_button_released
), NULL
);
826 pango_context
= gtk_widget_create_pango_context( view
);
827 pango_font_description
= pango_font_description_copy( pango_context_get_font_description( pango_context
) );
828 size
= pango_font_description_get_size( pango_font_description
);
829 pango_font_description_set_size( pango_font_description
, size
* 0.8 );
830 g_object_unref( G_OBJECT( pango_context
) );
833 sel
= gtk_tree_view_get_selection( tree_view
);
834 gtk_tree_selection_set_mode( sel
, GTK_SELECTION_MULTIPLE
);
835 gtk_tree_view_expand_all( tree_view
);
836 gtk_tree_view_set_search_column( tree_view
, FC_LABEL
);
838 /* add file column */
839 col
= GTK_TREE_VIEW_COLUMN ( g_object_new ( GTK_TYPE_TREE_VIEW_COLUMN
,
841 "title", _( "Name" ),
843 gtk_tree_view_column_set_resizable( col
, TRUE
);
844 rend
= gtk_cell_renderer_pixbuf_new( );
845 gtk_tree_view_column_pack_start( col
, rend
, FALSE
);
846 gtk_tree_view_column_add_attribute( col
, rend
, "pixbuf", FC_ICON
);
847 /* add text renderer */
848 rend
= gtk_cell_renderer_text_new( );
849 g_object_set( rend
, "ellipsize", PANGO_ELLIPSIZE_END
, "font-desc", pango_font_description
, NULL
);
850 gtk_tree_view_column_pack_start( col
, rend
, TRUE
);
851 gtk_tree_view_column_set_attributes( col
, rend
, "text", FC_LABEL
, NULL
);
852 gtk_tree_view_column_set_sort_column_id( col
, FC_LABEL
);
853 gtk_tree_view_append_column( tree_view
, col
);
855 /* add "size" column */
857 rend
= gtk_cell_renderer_text_new( );
858 g_object_set( rend
, "alignment", PANGO_ALIGN_RIGHT
,
859 "font-desc", pango_font_description
,
864 col
= gtk_tree_view_column_new_with_attributes( title
, rend
, NULL
);
865 gtk_tree_view_column_set_sizing( col
, GTK_TREE_VIEW_COLUMN_GROW_ONLY
);
866 gtk_tree_view_column_set_sort_column_id( col
, FC_SIZE
);
867 gtk_tree_view_column_set_attributes( col
, rend
, "text", FC_SIZE_STR
, NULL
);
868 gtk_tree_view_append_column( tree_view
, col
);
870 /* add "progress" column */
872 pango_layout
= gtk_widget_create_pango_layout( view
, title
);
873 pango_layout_get_pixel_size( pango_layout
, &width
, NULL
);
874 width
+= 30; /* room for the sort indicator */
875 g_object_unref( G_OBJECT( pango_layout
) );
876 rend
= gtk_cell_renderer_progress_new( );
877 col
= gtk_tree_view_column_new_with_attributes( title
, rend
, "value", FC_PROG
, NULL
);
878 gtk_tree_view_column_set_fixed_width( col
, width
);
879 gtk_tree_view_column_set_sizing( col
, GTK_TREE_VIEW_COLUMN_FIXED
);
880 gtk_tree_view_column_set_sort_column_id( col
, FC_PROG
);
881 gtk_tree_view_append_column( tree_view
, col
);
883 /* add "enabled" column */
884 title
= _( "Download" );
885 pango_layout
= gtk_widget_create_pango_layout( view
, title
);
886 pango_layout_get_pixel_size( pango_layout
, &width
, NULL
);
887 width
+= 30; /* room for the sort indicator */
888 g_object_unref( G_OBJECT( pango_layout
) );
889 rend
= gtk_cell_renderer_toggle_new( );
890 col
= gtk_tree_view_column_new_with_attributes( title
, rend
, NULL
);
891 g_object_set_data( G_OBJECT( col
), TR_COLUMN_ID_KEY
,
892 GINT_TO_POINTER( FC_ENABLED
) );
893 gtk_tree_view_column_set_fixed_width( col
, width
);
894 gtk_tree_view_column_set_sizing( col
, GTK_TREE_VIEW_COLUMN_FIXED
);
895 gtk_tree_view_column_set_cell_data_func( col
, rend
, renderDownload
, NULL
, NULL
);
896 gtk_tree_view_column_set_sort_column_id( col
, FC_ENABLED
);
897 gtk_tree_view_append_column( tree_view
, col
);
899 /* add priority column */
900 title
= _( "Priority" );
901 pango_layout
= gtk_widget_create_pango_layout( view
, title
);
902 pango_layout_get_pixel_size( pango_layout
, &width
, NULL
);
903 width
+= 30; /* room for the sort indicator */
904 g_object_unref( G_OBJECT( pango_layout
) );
905 rend
= gtk_cell_renderer_text_new( );
906 g_object_set( rend
, "xalign", (gfloat
)0.5, "yalign", (gfloat
)0.5, NULL
);
907 col
= gtk_tree_view_column_new_with_attributes( title
, rend
, NULL
);
908 g_object_set_data( G_OBJECT( col
), TR_COLUMN_ID_KEY
,
909 GINT_TO_POINTER( FC_PRIORITY
) );
910 gtk_tree_view_column_set_fixed_width( col
, width
);
911 gtk_tree_view_column_set_sizing( col
, GTK_TREE_VIEW_COLUMN_FIXED
);
912 gtk_tree_view_column_set_sort_column_id( col
, FC_PRIORITY
);
913 gtk_tree_view_column_set_cell_data_func( col
, rend
, renderPriority
, NULL
, NULL
);
914 gtk_tree_view_append_column( tree_view
, col
);
916 /* create the scrolled window and stick the view in it */
917 scroll
= gtk_scrolled_window_new( NULL
, NULL
);
918 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scroll
),
919 GTK_POLICY_AUTOMATIC
,
920 GTK_POLICY_AUTOMATIC
);
921 gtk_scrolled_window_set_shadow_type ( GTK_SCROLLED_WINDOW( scroll
),
923 gtk_container_add( GTK_CONTAINER( scroll
), view
);
924 gtk_widget_set_size_request ( scroll
, -1, 200 );
929 g_object_set_data_full( G_OBJECT( ret
), "file-data", data
, freeData
);
930 gtr_file_list_set_torrent( ret
, torrentId
);
932 pango_font_description_free( pango_font_description
);