Revert "transmission: update from 2.13 to 2.22"
[tomato.git] / release / src / router / transmission / gtk / file-list.c
blobcf94fcf70425a48036a3be9dcd4e3a33a9c04d00
1 /*
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 $
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>
20 #include <libtransmission/transmission.h>
22 #include "file-list.h"
23 #include "hig.h"
24 #include "icons.h"
25 #include "tr-prefs.h"
27 enum
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 */
31 NOT_SET = 1000,
32 MIXED = 1001
35 enum
37 FC_ICON,
38 FC_LABEL,
39 FC_PROG,
40 FC_INDEX,
41 FC_SIZE,
42 FC_HAVE,
43 FC_PRIORITY,
44 FC_ENABLED,
45 N_FILE_COLS
48 typedef struct
50 TrCore * core;
51 GtkWidget * top;
52 GtkWidget * view;
53 GtkTreeModel * model; /* same object as store, but recast */
54 GtkTreeStore * store; /* same object as model, but recast */
55 int torrentId;
56 guint timeout_tag;
58 FileData;
60 static void
61 clearData( FileData * data )
63 data->torrentId = -1;
65 if( data->timeout_tag ) {
66 g_source_remove( data->timeout_tag );
67 data->timeout_tag = 0;
71 static void
72 freeData( gpointer data )
74 clearData( data );
75 g_free( data );
78 /***
79 ****
80 ***/
82 struct RefreshData
84 int sort_column_id;
85 gboolean resort_needed;
87 tr_file_stat * refresh_file_stat;
88 tr_torrent * tor;
90 FileData * file_data;
93 static gboolean
94 refreshFilesForeach( GtkTreeModel * model,
95 GtkTreePath * path UNUSED,
96 GtkTreeIter * iter,
97 gpointer gdata )
99 struct RefreshData * refresh_data = gdata;
100 FileData * data = refresh_data->file_data;
101 unsigned int index;
102 uint64_t size;
103 uint64_t old_have;
104 int old_prog;
105 int old_priority;
106 int old_enabled;
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,
111 FC_INDEX, &index,
112 FC_HAVE, &old_have,
113 FC_SIZE, &size,
114 FC_PROG, &old_prog,
115 -1 );
117 if( is_file )
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,
146 FC_ENABLED, enabled,
147 FC_HAVE, have,
148 FC_PROG, prog,
149 -1 );
152 else
154 GtkTreeIter child;
155 uint64_t sub_size = 0;
156 uint64_t have = 0;
157 int prog;
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
166 int child_enabled;
167 int child_priority;
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,
174 -1 );
176 sub_size += child_size;
177 have += child_have;
179 if( enabled == NOT_SET )
180 enabled = child_enabled;
181 else if( enabled != child_enabled )
182 enabled = MIXED;
184 if( priority == NOT_SET )
185 priority = child_priority;
186 else if( priority != child_priority )
187 priority = MIXED;
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,
198 FC_HAVE, have,
199 FC_PRIORITY, priority,
200 FC_ENABLED, enabled,
201 FC_PROG, prog,
202 -1 );
205 return FALSE; /* keep walking */
208 static void
209 gtr_tree_model_foreach_postorder_subtree( GtkTreeModel * model,
210 GtkTreeIter * parent,
211 GtkTreeModelForeachFunc func,
212 gpointer data )
214 GtkTreeIter child;
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 ) );
218 if( parent )
219 func( model, NULL, parent, data );
222 static void
223 gtr_tree_model_foreach_postorder( GtkTreeModel * model,
224 GtkTreeModelForeachFunc func,
225 gpointer data )
227 GtkTreeIter iter;
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 ) );
233 static void
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 );
242 if( tor == NULL )
244 file_list_clear( data->top );
246 else
248 GtkSortType order;
249 int sort_column_id;
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 );
270 static gboolean
271 refreshModel( gpointer file_data )
273 refresh( file_data );
274 return TRUE;
277 /***
278 ****
279 ***/
281 struct ActiveData
283 GtkTreeSelection * sel;
284 GArray * array;
287 static gboolean
288 getSelectedFilesForeach( GtkTreeModel * model,
289 GtkTreePath * path UNUSED,
290 GtkTreeIter * iter,
291 gpointer gdata )
293 const gboolean is_file = !gtk_tree_model_iter_has_child( model, iter );
295 if( is_file )
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 );
303 if( !is_active )
305 GtkTreeIter walk = *iter;
306 GtkTreeIter parent;
307 while( !is_active && gtk_tree_model_iter_parent( model, &parent, &walk ) )
309 is_active = gtk_tree_selection_iter_is_selected( data->sel, &parent );
310 walk = parent;
314 if( is_active )
316 unsigned int i;
317 gtk_tree_model_get( model, iter, FC_INDEX, &i, -1 );
318 g_array_append_val( data->array, i );
322 return FALSE; /* keep walking */
325 static void
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
338 GArray * array;
339 GtkTreePath * path;
342 static gboolean
343 getSubtreeForeach( GtkTreeModel * model,
344 GtkTreePath * path,
345 GtkTreeIter * iter,
346 gpointer gdata )
348 const gboolean is_file = !gtk_tree_model_iter_has_child( model, iter );
350 if( is_file )
352 struct SubtreeForeachData * data = gdata;
354 if( !gtk_tree_path_compare( path, data->path ) || gtk_tree_path_is_descendant( path, data->path ) )
356 unsigned int i;
357 gtk_tree_model_get( model, iter, FC_INDEX, &i, -1 );
358 g_array_append_val( data->array, i );
362 return FALSE; /* keep walking */
365 static void
366 getSubtree( GtkTreeView * view, GtkTreePath * path, GArray * indices )
368 struct SubtreeForeachData tmp;
369 tmp.array = indices;
370 tmp.path = path;
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.
378 static GArray*
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 );
389 else
391 /* clicked OUTSIDE of the selected row... just use the clicked row */
392 getSubtree( view, path, indices );
395 return indices;
398 /***
399 ****
400 ***/
402 void
403 file_list_clear( GtkWidget * w )
405 file_list_set_torrent( w, -1 );
408 struct build_data
410 GtkWidget * w;
411 tr_torrent * tor;
412 GtkTreeIter * iter;
413 GtkTreeStore * store;
416 struct row_struct
418 uint64_t length;
419 char * name;
420 int index;
423 static void
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,
441 FC_ICON, icon,
442 FC_PRIORITY, priority,
443 FC_ENABLED, enabled,
444 -1 );
445 #else
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,
451 FC_ICON, icon,
452 FC_PRIORITY, priority,
453 FC_ENABLED, enabled,
454 -1 );
455 #endif
457 if( !isLeaf )
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 );
471 static GNode*
472 find_child( GNode* parent, const char * name )
474 GNode * child = parent->children;
475 while( child ) {
476 const struct row_struct * child_data = child->data;
477 if( ( *child_data->name == *name ) && !strcmp( child_data->name, name ) )
478 break;
479 child = child->next;
481 return child;
484 void
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 */
491 clearData( data );
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 */
504 data->store = store;
505 data->model = GTK_TREE_MODEL( store );
506 data->torrentId = torrentId;
508 /* populate the model */
509 if( torrentId > 0 )
511 tr_session * session = tr_core_session( data->core );
512 tr_torrent * tor = tr_torrentFindFromId( session, torrentId );
513 if( tor != NULL )
515 tr_file_index_t i;
516 const tr_info * inf = tr_torrentInfo( tor );
517 struct row_struct * root_data;
518 GNode * root;
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 ) {
528 int j;
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 );
536 if( node == NULL ) {
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 );
544 parent = node;
546 g_strfreev( tokens );
549 /* now, add them to the model */
550 build.w = w;
551 build.tor = tor;
552 build.store = data->store;
553 build.iter = NULL;
554 g_node_children_foreach( root, G_TRAVERSE_ALL, buildTree, &build );
556 /* cleanup */
557 g_node_destroy( root );
558 g_free( root_data->name );
559 g_free( root_data );
562 refresh( data );
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 ) );
570 /***
571 ****
572 ***/
574 static void
575 renderFilename( GtkTreeViewColumn * column UNUSED,
576 GtkCellRenderer * renderer,
577 GtkTreeModel * model,
578 GtkTreeIter * iter,
579 gpointer data UNUSED )
581 char * filename;
582 char * str;
583 int64_t size;
584 char buf[64];
586 gtk_tree_model_get( model, iter, FC_LABEL, &filename,
587 FC_SIZE, &size,
588 -1 );
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 );
592 g_free( str );
593 g_free( filename );
596 static void
597 renderDownload( GtkTreeViewColumn * column UNUSED,
598 GtkCellRenderer * renderer,
599 GtkTreeModel * model,
600 GtkTreeIter * iter,
601 gpointer data UNUSED )
603 gboolean enabled;
604 gtk_tree_model_get( model, iter, FC_ENABLED, &enabled, -1 );
605 g_object_set( renderer, "inconsistent", (enabled==MIXED),
606 "active", (enabled==TRUE),
607 NULL );
610 static void
611 renderPriority( GtkTreeViewColumn * column UNUSED,
612 GtkCellRenderer * renderer,
613 GtkTreeModel * model,
614 GtkTreeIter * iter,
615 gpointer data UNUSED )
617 int priority;
618 const char * text;
619 gtk_tree_model_get( model, iter, FC_PRIORITY, &priority, -1 );
620 switch( priority ) {
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 */
630 static char*
631 buildFilename( tr_torrent * tor, GtkTreeModel * model,
632 GtkTreePath * path, GtkTreeIter * iter )
634 char * ret;
635 GtkTreeIter child;
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 ) );
640 do {
641 child = parent;
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 );
646 return ret;
649 static gboolean
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 );
657 if( tor != NULL )
659 GtkTreeIter iter;
660 GtkTreeModel * model = gtk_tree_view_get_model( view );
662 if( gtk_tree_model_get_iter( model, &iter, path ) )
664 int prog;
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 );
673 g_free( filename );
674 filename = tmp;
676 while( filename && *filename && !g_file_test( filename, G_FILE_TEST_EXISTS ) );
678 if(( handled = filename && *filename ))
679 gtr_open_file( filename );
683 return handled;
687 static gboolean
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 );
694 if( tor == NULL )
695 return handled;
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 );
701 GtkTreePath * path;
702 GtkTreeViewColumn * column;
703 int cell_x;
704 int cell_y;
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;
717 GtkTreeIter iter;
719 gtk_tree_model_get_iter( model, &iter, path );
721 if( priorityColumn )
723 int priority;
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 */
729 switch( priority ) {
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,
739 priority );
741 else if( downloadColumn )
743 int enabled;
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 */
749 enabled = !enabled;
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,
755 enabled );
758 refresh( data );
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;
765 /* cleanup */
766 g_array_free( a, TRUE );
769 gtk_tree_path_free( path );
773 return handled;
776 GtkWidget *
777 file_list_new( TrCore * core, int torrentId )
779 int width;
780 GtkWidget * ret;
781 GtkWidget * view;
782 GtkWidget * scroll;
783 GtkCellRenderer * rend;
784 GtkTreeSelection * sel;
785 GtkTreeViewColumn * col;
786 GtkTreeView * tree_view;
787 const char * title;
788 PangoLayout * pango_layout;
789 FileData * data = g_new0( FileData, 1 );
791 data->core = core;
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 );
806 /* set up view */
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,
815 "expand", TRUE,
816 "title", _( "File" ),
817 NULL ) );
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 ),
878 GTK_SHADOW_IN );
879 gtk_container_add( GTK_CONTAINER( scroll ), view );
880 gtk_widget_set_size_request ( scroll, -1, 200 );
882 ret = scroll;
883 data->view = view;
884 data->top = scroll;
885 g_object_set_data_full( G_OBJECT( ret ), "file-data", data, freeData );
886 file_list_set_torrent( ret, torrentId );
888 return ret;