2 * This file Copyright (C) 2007-2010 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 11217 2010-09-17 13:23:20Z charles $
17 #include <glib/gi18n.h>
20 #include <libtransmission/transmission.h>
22 #include "file-list.h"
29 /* these two fields could be any number at all so long as they're not
30 * TR_PRI_LOW, TR_PRI_NORMAL, TR_PRI_HIGH, TRUE, or FALSE */
53 GtkTreeModel
* model
; /* same object as store, but recast */
54 GtkTreeStore
* store
; /* same object as model, but recast */
61 clearData( FileData
* data
)
65 if( data
->timeout_tag
) {
66 g_source_remove( data
->timeout_tag
);
67 data
->timeout_tag
= 0;
72 freeData( gpointer data
)
85 gboolean resort_needed
;
87 tr_file_stat
* refresh_file_stat
;
94 refreshFilesForeach( GtkTreeModel
* model
,
95 GtkTreePath
* path UNUSED
,
99 struct RefreshData
* refresh_data
= gdata
;
100 FileData
* data
= refresh_data
->file_data
;
107 const gboolean is_file
= !gtk_tree_model_iter_has_child( model
, iter
);
109 gtk_tree_model_get( model
, iter
, FC_ENABLED
, &old_enabled
,
110 FC_PRIORITY
, &old_priority
,
119 tr_torrent
* tor
= refresh_data
->tor
;
120 const tr_info
* inf
= tr_torrentInfo( tor
);
121 const int enabled
= !inf
->files
[index
].dnd
;
122 const int priority
= inf
->files
[index
].priority
;
123 const uint64_t have
= refresh_data
->refresh_file_stat
[index
].bytesCompleted
;
124 const int prog
= size
? (int)((100.0*have
)/size
) : 1;
126 if( (priority
!=old_priority
) || (enabled
!=old_enabled
) || (have
!=old_have
) || (prog
!=old_prog
) )
128 /* Changing a value in the sort column can trigger a resort
129 * which breaks this foreach() call. (See #3529)
130 * As a workaround: if that's about to happen, temporarily disable
131 * sorting until we finish walking the tree. */
132 if( !refresh_data
->resort_needed
)
134 if(( refresh_data
->resort_needed
=
135 (( refresh_data
->sort_column_id
==FC_PRIORITY
) && ( priority
!=old_priority
)) ||
136 (( refresh_data
->sort_column_id
==FC_ENABLED
) && ( enabled
!=old_enabled
))))
138 refresh_data
->resort_needed
= TRUE
;
139 gtk_tree_sortable_set_sort_column_id( GTK_TREE_SORTABLE( data
->model
),
140 GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID
,
141 GTK_SORT_ASCENDING
);
145 gtk_tree_store_set( data
->store
, iter
, FC_PRIORITY
, priority
,
155 uint64_t sub_size
= 0;
158 int enabled
= NOT_SET
;
159 int priority
= NOT_SET
;
161 /* since gtk_tree_model_foreach() is depth-first, we can
162 * get the `sub' info by walking the immediate children */
164 if( gtk_tree_model_iter_children( model
, &child
, iter
) ) do
168 int64_t child_have
, child_size
;
170 gtk_tree_model_get( model
, &child
, FC_SIZE
, &child_size
,
171 FC_HAVE
, &child_have
,
172 FC_PRIORITY
, &child_priority
,
173 FC_ENABLED
, &child_enabled
,
176 sub_size
+= child_size
;
179 if( enabled
== NOT_SET
)
180 enabled
= child_enabled
;
181 else if( enabled
!= child_enabled
)
184 if( priority
== NOT_SET
)
185 priority
= child_priority
;
186 else if( priority
!= child_priority
)
189 while( gtk_tree_model_iter_next( model
, &child
) );
191 prog
= sub_size
? (int)((100.0*have
)/sub_size
) : 1;
193 if( (size
!=sub_size
) || (have
!=old_have
)
194 || (priority
!=old_priority
)
195 || (enabled
!=old_enabled
)
196 || (prog
!=old_prog
) )
197 gtk_tree_store_set( data
->store
, iter
, FC_SIZE
, sub_size
,
199 FC_PRIORITY
, priority
,
205 return FALSE
; /* keep walking */
209 gtr_tree_model_foreach_postorder_subtree( GtkTreeModel
* model
,
210 GtkTreeIter
* parent
,
211 GtkTreeModelForeachFunc func
,
215 if( gtk_tree_model_iter_children( model
, &child
, parent
) ) do
216 gtr_tree_model_foreach_postorder_subtree( model
, &child
, func
, data
);
217 while( gtk_tree_model_iter_next( model
, &child
) );
219 func( model
, NULL
, parent
, data
);
223 gtr_tree_model_foreach_postorder( GtkTreeModel
* model
,
224 GtkTreeModelForeachFunc func
,
228 if( gtk_tree_model_get_iter_first( model
, &iter
) ) do
229 gtr_tree_model_foreach_postorder_subtree( model
, &iter
, func
, data
);
230 while( gtk_tree_model_iter_next( model
, &iter
) );
234 refresh( FileData
* data
)
236 tr_torrent
* tor
= NULL
;
237 tr_session
* session
= tr_core_session( data
->core
);
239 if( session
!= NULL
)
240 tor
= tr_torrentFindFromId( session
, data
->torrentId
);
244 file_list_clear( data
->top
);
250 tr_file_index_t fileCount
;
251 struct RefreshData refresh_data
;
252 GtkTreeSortable
* sortable
= GTK_TREE_SORTABLE( data
->model
);
253 gtk_tree_sortable_get_sort_column_id( sortable
, &sort_column_id
, &order
);
255 refresh_data
.sort_column_id
= sort_column_id
;
256 refresh_data
.resort_needed
= FALSE
;
257 refresh_data
.refresh_file_stat
= tr_torrentFiles( tor
, &fileCount
);
258 refresh_data
.tor
= tr_torrentFindFromId( session
, data
->torrentId
);
259 refresh_data
.file_data
= data
;
261 gtr_tree_model_foreach_postorder( data
->model
, refreshFilesForeach
, &refresh_data
);
263 if( refresh_data
.resort_needed
)
264 gtk_tree_sortable_set_sort_column_id( sortable
, sort_column_id
, order
);
266 tr_torrentFilesFree( refresh_data
.refresh_file_stat
, fileCount
);
271 refreshModel( gpointer file_data
)
273 refresh( file_data
);
283 GtkTreeSelection
* sel
;
288 getSelectedFilesForeach( GtkTreeModel
* model
,
289 GtkTreePath
* path UNUSED
,
293 const gboolean is_file
= !gtk_tree_model_iter_has_child( model
, iter
);
297 struct ActiveData
* data
= gdata
;
299 /* active means: if it's selected or any ancestor is selected */
301 gboolean is_active
= gtk_tree_selection_iter_is_selected( data
->sel
, iter
);
305 GtkTreeIter walk
= *iter
;
307 while( !is_active
&& gtk_tree_model_iter_parent( model
, &parent
, &walk
) )
309 is_active
= gtk_tree_selection_iter_is_selected( data
->sel
, &parent
);
317 gtk_tree_model_get( model
, iter
, FC_INDEX
, &i
, -1 );
318 g_array_append_val( data
->array
, i
);
322 return FALSE
; /* keep walking */
326 getSelectedFilesAndDescendants( GtkTreeView
* view
, GArray
* indices
)
328 struct ActiveData data
;
330 data
.sel
= gtk_tree_view_get_selection( view
);
331 data
.array
= indices
;
332 gtk_tree_model_foreach( gtk_tree_view_get_model( view
),
333 getSelectedFilesForeach
, &data
);
336 struct SubtreeForeachData
343 getSubtreeForeach( GtkTreeModel
* model
,
348 const gboolean is_file
= !gtk_tree_model_iter_has_child( model
, iter
);
352 struct SubtreeForeachData
* data
= gdata
;
354 if( !gtk_tree_path_compare( path
, data
->path
) || gtk_tree_path_is_descendant( path
, data
->path
) )
357 gtk_tree_model_get( model
, iter
, FC_INDEX
, &i
, -1 );
358 g_array_append_val( data
->array
, i
);
362 return FALSE
; /* keep walking */
366 getSubtree( GtkTreeView
* view
, GtkTreePath
* path
, GArray
* indices
)
368 struct SubtreeForeachData tmp
;
371 gtk_tree_model_foreach( gtk_tree_view_get_model( view
), getSubtreeForeach
, &tmp
);
374 /* if `path' is a selected row, all selected rows are returned.
375 * otherwise, only the row indicated by `path' is returned.
376 * this is for toggling all the selected rows' states in a batch.
379 getActiveFilesForPath( GtkTreeView
* view
, GtkTreePath
* path
)
381 GtkTreeSelection
* sel
= gtk_tree_view_get_selection( view
);
382 GArray
* indices
= g_array_new( FALSE
, FALSE
, sizeof( tr_file_index_t
) );
384 if( gtk_tree_selection_path_is_selected( sel
, path
) )
386 /* clicked in a selected row... use the current selection */
387 getSelectedFilesAndDescendants( view
, indices
);
391 /* clicked OUTSIDE of the selected row... just use the clicked row */
392 getSubtree( view
, path
, indices
);
403 file_list_clear( GtkWidget
* w
)
405 file_list_set_torrent( w
, -1 );
413 GtkTreeStore
* store
;
424 buildTree( GNode
* node
, gpointer gdata
)
426 GtkTreeIter child_iter
;
427 struct build_data
* build
= gdata
;
428 struct row_struct
*child_data
= node
->data
;
429 const gboolean isLeaf
= node
->children
== NULL
;
431 const char * mime_type
= isLeaf
? get_mime_type_from_filename( child_data
->name
) : DIRECTORY_MIME_TYPE
;
432 GdkPixbuf
* icon
= get_mime_type_icon( mime_type
, GTK_ICON_SIZE_MENU
, build
->w
);
433 const tr_info
* inf
= tr_torrentInfo( build
->tor
);
434 const int priority
= isLeaf
? inf
->files
[ child_data
->index
].priority
: 0;
435 const gboolean enabled
= isLeaf
? !inf
->files
[ child_data
->index
].dnd
: TRUE
;
436 #if GTK_CHECK_VERSION(2,10,0)
437 gtk_tree_store_insert_with_values( build
->store
, &child_iter
, build
->iter
, INT_MAX
,
438 FC_INDEX
, child_data
->index
,
439 FC_LABEL
, child_data
->name
,
440 FC_SIZE
, child_data
->length
,
442 FC_PRIORITY
, priority
,
446 gtk_tree_store_append( build
->store
, &child_iter
, build
->iter
);
447 gtk_tree_store_set( build
->store
, &child_iter
,
448 FC_INDEX
, child_data
->index
,
449 FC_LABEL
, child_data
->name
,
450 FC_SIZE
, child_data
->length
,
452 FC_PRIORITY
, priority
,
459 struct build_data b
= *build
;
460 b
.iter
= &child_iter
;
461 g_node_children_foreach( node
, G_TRAVERSE_ALL
, buildTree
, &b
);
464 g_object_unref( icon
);
466 /* we're done with this node */
467 g_free( child_data
->name
);
468 g_free( child_data
);
472 find_child( GNode
* parent
, const char * name
)
474 GNode
* child
= parent
->children
;
476 const struct row_struct
* child_data
= child
->data
;
477 if( ( *child_data
->name
== *name
) && !strcmp( child_data
->name
, name
) )
485 file_list_set_torrent( GtkWidget
* w
, int torrentId
)
487 GtkTreeStore
* store
;
488 FileData
* data
= g_object_get_data( G_OBJECT( w
), "file-data" );
490 /* unset the old fields */
493 /* instantiate the model */
494 store
= gtk_tree_store_new ( N_FILE_COLS
,
495 GDK_TYPE_PIXBUF
, /* icon */
496 G_TYPE_STRING
, /* label */
497 G_TYPE_INT
, /* prog [0..100] */
498 G_TYPE_UINT
, /* index */
499 G_TYPE_UINT64
, /* size */
500 G_TYPE_UINT64
, /* have */
501 G_TYPE_INT
, /* priority */
502 G_TYPE_INT
); /* dl enabled */
505 data
->model
= GTK_TREE_MODEL( store
);
506 data
->torrentId
= torrentId
;
508 /* populate the model */
511 tr_session
* session
= tr_core_session( data
->core
);
512 tr_torrent
* tor
= tr_torrentFindFromId( session
, torrentId
);
516 const tr_info
* inf
= tr_torrentInfo( tor
);
517 struct row_struct
* root_data
;
519 struct build_data build
;
521 /* build a GNode tree of the files */
522 root_data
= g_new0( struct row_struct
, 1 );
523 root_data
->name
= g_strdup( inf
->name
);
524 root_data
->index
= -1;
525 root_data
->length
= 0;
526 root
= g_node_new( root_data
);
527 for( i
=0; i
<inf
->fileCount
; ++i
) {
529 GNode
* parent
= root
;
530 const tr_file
* file
= &inf
->files
[i
];
531 char ** tokens
= g_strsplit( file
->name
, G_DIR_SEPARATOR_S
, 0 );
532 for( j
=0; tokens
[j
]; ++j
) {
533 const gboolean isLeaf
= tokens
[j
+1] == NULL
;
534 const char * name
= tokens
[j
];
535 GNode
* node
= find_child( parent
, name
);
537 struct row_struct
* row
= g_new( struct row_struct
, 1 );
538 row
->name
= g_strdup( name
);
539 row
->index
= isLeaf
? (int)i
: -1;
540 row
->length
= isLeaf
? file
->length
: 0;
541 node
= g_node_new( row
);
542 g_node_append( parent
, node
);
546 g_strfreev( tokens
);
549 /* now, add them to the model */
552 build
.store
= data
->store
;
554 g_node_children_foreach( root
, G_TRAVERSE_ALL
, buildTree
, &build
);
557 g_node_destroy( root
);
558 g_free( root_data
->name
);
563 data
->timeout_tag
= gtr_timeout_add_seconds( SECONDARY_WINDOW_REFRESH_INTERVAL_SECONDS
, refreshModel
, data
);
566 gtk_tree_view_set_model( GTK_TREE_VIEW( data
->view
), data
->model
);
567 gtk_tree_view_expand_all( GTK_TREE_VIEW( data
->view
) );
575 renderFilename( GtkTreeViewColumn
* column UNUSED
,
576 GtkCellRenderer
* renderer
,
577 GtkTreeModel
* model
,
579 gpointer data UNUSED
)
586 gtk_tree_model_get( model
, iter
, FC_LABEL
, &filename
,
589 tr_strlsize( buf
, size
, sizeof( buf
) );
590 str
= g_markup_printf_escaped( "<small>%s (%s)</small>", filename
, buf
);
591 g_object_set( renderer
, "markup", str
, NULL
);
597 renderDownload( GtkTreeViewColumn
* column UNUSED
,
598 GtkCellRenderer
* renderer
,
599 GtkTreeModel
* model
,
601 gpointer data UNUSED
)
604 gtk_tree_model_get( model
, iter
, FC_ENABLED
, &enabled
, -1 );
605 g_object_set( renderer
, "inconsistent", (enabled
==MIXED
),
606 "active", (enabled
==TRUE
),
611 renderPriority( GtkTreeViewColumn
* column UNUSED
,
612 GtkCellRenderer
* renderer
,
613 GtkTreeModel
* model
,
615 gpointer data UNUSED
)
619 gtk_tree_model_get( model
, iter
, FC_PRIORITY
, &priority
, -1 );
621 case TR_PRI_HIGH
: text
= _( "High" ); break;
622 case TR_PRI_NORMAL
: text
= _( "Normal" ); break;
623 case TR_PRI_LOW
: text
= _( "Low" ); break;
624 default: text
= _( "Mixed" ); break;
626 g_object_set( renderer
, "text", text
, NULL
);
629 /* build a filename from tr_torrentGetCurrentDir() + the model's FC_LABELs */
631 buildFilename( tr_torrent
* tor
, GtkTreeModel
* model
,
632 GtkTreePath
* path
, GtkTreeIter
* iter
)
636 GtkTreeIter parent
= *iter
;
637 int n
= gtk_tree_path_get_depth( path
);
638 char ** tokens
= g_new0( char*, n
+ 2 );
639 tokens
[0] = g_strdup( tr_torrentGetCurrentDir( tor
) );
642 gtk_tree_model_get( model
, &child
, FC_LABEL
, &tokens
[n
--], -1 );
643 } while( gtk_tree_model_iter_parent( model
, &parent
, &child
) );
644 ret
= g_build_filenamev( tokens
);
645 g_strfreev( tokens
);
650 onRowActivated( GtkTreeView
* view
, GtkTreePath
* path
,
651 GtkTreeViewColumn
* col UNUSED
, gpointer gdata
)
653 gboolean handled
= FALSE
;
654 FileData
* data
= gdata
;
655 tr_torrent
* tor
= tr_torrentFindFromId( tr_core_session( data
->core
), data
->torrentId
);
660 GtkTreeModel
* model
= gtk_tree_view_get_model( view
);
662 if( gtk_tree_model_get_iter( model
, &iter
, path
) )
665 char * filename
= buildFilename( tor
, model
, path
, &iter
);
666 gtk_tree_model_get( model
, &iter
, FC_PROG
, &prog
, -1 );
668 /* if the file's not done, walk up the directory tree until we find
669 * an ancestor that exists, and open that instead */
670 if( filename
&& ( prog
<100 || !g_file_test( filename
, G_FILE_TEST_EXISTS
) ) ) do
672 char * tmp
= g_path_get_dirname( filename
);
676 while( filename
&& *filename
&& !g_file_test( filename
, G_FILE_TEST_EXISTS
) );
678 if(( handled
= filename
&& *filename
))
679 gtr_open_file( filename
);
688 onViewButtonPressed( GtkWidget
* w
, GdkEventButton
* event
, gpointer gdata
)
690 FileData
* data
= gdata
;
691 gboolean handled
= FALSE
;
692 tr_torrent
* tor
= tr_torrentFindFromId( tr_core_session( data
->core
), data
->torrentId
);
697 if( ( event
->type
== GDK_BUTTON_PRESS
) && ( event
->button
== 1 )
698 && !( event
->state
& ( GDK_SHIFT_MASK
| GDK_CONTROL_MASK
) ) )
700 GtkTreeView
* view
= GTK_TREE_VIEW( w
);
702 GtkTreeViewColumn
* column
;
705 if( gtk_tree_view_get_path_at_pos( view
, event
->x
, event
->y
,
706 &path
, &column
, &cell_x
, &cell_y
) )
708 const char * column_title
= gtk_tree_view_column_get_title( column
);
709 const gboolean downloadColumn
= !strcmp( column_title
, _( "Download" ) );
710 const gboolean priorityColumn
= !strcmp( column_title
, _( "Priority" ) );
711 if( downloadColumn
|| priorityColumn
)
713 GArray
* a
= getActiveFilesForPath( view
, path
);
714 GtkTreeSelection
* sel
= gtk_tree_view_get_selection( view
);
715 const gboolean isSelected
= gtk_tree_selection_path_is_selected( sel
, path
);
716 GtkTreeModel
* model
= data
->model
;
719 gtk_tree_model_get_iter( model
, &iter
, path
);
725 /* get the `priority' state of the clicked row */
726 gtk_tree_model_get( model
, &iter
, FC_PRIORITY
, &priority
, -1 );
728 /* twiddle it to the next state */
730 case TR_PRI_NORMAL
: priority
= TR_PRI_HIGH
; break;
731 case TR_PRI_HIGH
: priority
= TR_PRI_LOW
; break;
732 default: priority
= TR_PRI_NORMAL
; break;
735 /* apply that new state to the active files */
736 tr_torrentSetFilePriorities( tor
,
737 (tr_file_index_t
*)a
->data
,
738 (tr_file_index_t
)a
->len
,
741 else if( downloadColumn
)
745 /* get the `enabled' state of the clicked row */
746 gtk_tree_model_get( model
, &iter
, FC_ENABLED
, &enabled
, -1 );
748 /* twiddle it to the next state */
751 /* apply that new state to the active files */
752 tr_torrentSetFileDLs( tor
,
753 (tr_file_index_t
*)a
->data
,
754 (tr_file_index_t
)a
->len
,
760 /* the click was meant to change the priority or enabled state,
761 not to alter which rows were selected, so don't pass this
762 event on to the other handlers. */
763 handled
= isSelected
;
766 g_array_free( a
, TRUE
);
769 gtk_tree_path_free( path
);
777 file_list_new( TrCore
* core
, int torrentId
)
783 GtkCellRenderer
* rend
;
784 GtkTreeSelection
* sel
;
785 GtkTreeViewColumn
* col
;
786 GtkTreeView
* tree_view
;
788 PangoLayout
* pango_layout
;
789 FileData
* data
= g_new0( FileData
, 1 );
793 /* create the view */
794 view
= gtk_tree_view_new( );
795 tree_view
= GTK_TREE_VIEW( view
);
796 gtk_tree_view_set_rules_hint( tree_view
, TRUE
);
797 gtk_container_set_border_width( GTK_CONTAINER( view
), GUI_PAD_BIG
);
798 g_signal_connect( view
, "button-press-event",
799 G_CALLBACK( onViewButtonPressed
), data
);
800 g_signal_connect( view
, "row_activated",
801 G_CALLBACK( onRowActivated
), data
);
802 g_signal_connect( view
, "button-release-event",
803 G_CALLBACK( on_tree_view_button_released
), NULL
);
807 sel
= gtk_tree_view_get_selection( tree_view
);
808 gtk_tree_selection_set_mode( sel
, GTK_SELECTION_MULTIPLE
);
809 gtk_tree_view_expand_all( tree_view
);
810 gtk_tree_view_set_search_column( tree_view
, FC_LABEL
);
812 /* add file column */
814 col
= GTK_TREE_VIEW_COLUMN ( g_object_new ( GTK_TYPE_TREE_VIEW_COLUMN
,
816 "title", _( "File" ),
818 gtk_tree_view_column_set_resizable( col
, TRUE
);
819 rend
= gtk_cell_renderer_pixbuf_new( );
820 gtk_tree_view_column_pack_start( col
, rend
, FALSE
);
821 gtk_tree_view_column_add_attribute( col
, rend
, "pixbuf", FC_ICON
);
822 /* add text renderer */
823 rend
= gtk_cell_renderer_text_new( );
824 g_object_set( rend
, "ellipsize", PANGO_ELLIPSIZE_END
, NULL
);
825 gtk_tree_view_column_pack_start( col
, rend
, TRUE
);
826 gtk_tree_view_column_set_cell_data_func( col
, rend
, renderFilename
, NULL
, NULL
);
827 gtk_tree_view_column_set_sort_column_id( col
, FC_LABEL
);
828 gtk_tree_view_append_column( tree_view
, col
);
830 /* add "progress" column */
831 title
= _( "Progress" );
832 pango_layout
= gtk_widget_create_pango_layout( view
, title
);
833 pango_layout_get_pixel_size( pango_layout
, &width
, NULL
);
834 width
+= 30; /* room for the sort indicator */
835 g_object_unref( G_OBJECT( pango_layout
) );
836 rend
= gtk_cell_renderer_progress_new( );
837 col
= gtk_tree_view_column_new_with_attributes( title
, rend
, "value", FC_PROG
, NULL
);
838 gtk_tree_view_column_set_fixed_width( col
, width
);
839 gtk_tree_view_column_set_sizing( col
, GTK_TREE_VIEW_COLUMN_FIXED
);
840 gtk_tree_view_column_set_sort_column_id( col
, FC_PROG
);
841 gtk_tree_view_append_column( tree_view
, col
);
843 /* add "enabled" column */
844 title
= _( "Download" );
845 pango_layout
= gtk_widget_create_pango_layout( view
, title
);
846 pango_layout_get_pixel_size( pango_layout
, &width
, NULL
);
847 width
+= 30; /* room for the sort indicator */
848 g_object_unref( G_OBJECT( pango_layout
) );
849 rend
= gtk_cell_renderer_toggle_new( );
850 col
= gtk_tree_view_column_new_with_attributes( title
, rend
, NULL
);
851 gtk_tree_view_column_set_fixed_width( col
, width
);
852 gtk_tree_view_column_set_sizing( col
, GTK_TREE_VIEW_COLUMN_FIXED
);
853 gtk_tree_view_column_set_cell_data_func( col
, rend
, renderDownload
, NULL
, NULL
);
854 gtk_tree_view_column_set_sort_column_id( col
, FC_ENABLED
);
855 gtk_tree_view_append_column( tree_view
, col
);
857 /* add priority column */
858 title
= _( "Priority" );
859 pango_layout
= gtk_widget_create_pango_layout( view
, title
);
860 pango_layout_get_pixel_size( pango_layout
, &width
, NULL
);
861 width
+= 30; /* room for the sort indicator */
862 g_object_unref( G_OBJECT( pango_layout
) );
863 rend
= gtk_cell_renderer_text_new( );
864 g_object_set( rend
, "xalign", (gfloat
)0.5, "yalign", (gfloat
)0.5, NULL
);
865 col
= gtk_tree_view_column_new_with_attributes( title
, rend
, NULL
);
866 gtk_tree_view_column_set_fixed_width( col
, width
);
867 gtk_tree_view_column_set_sizing( col
, GTK_TREE_VIEW_COLUMN_FIXED
);
868 gtk_tree_view_column_set_sort_column_id( col
, FC_PRIORITY
);
869 gtk_tree_view_column_set_cell_data_func( col
, rend
, renderPriority
, NULL
, NULL
);
870 gtk_tree_view_append_column( tree_view
, col
);
872 /* create the scrolled window and stick the view in it */
873 scroll
= gtk_scrolled_window_new( NULL
, NULL
);
874 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scroll
),
875 GTK_POLICY_AUTOMATIC
,
876 GTK_POLICY_AUTOMATIC
);
877 gtk_scrolled_window_set_shadow_type ( GTK_SCROLLED_WINDOW( scroll
),
879 gtk_container_add( GTK_CONTAINER( scroll
), view
);
880 gtk_widget_set_size_request ( scroll
, -1, 200 );
885 g_object_set_data_full( G_OBJECT( ret
), "file-data", data
, freeData
);
886 file_list_set_torrent( ret
, torrentId
);