4 * Copyright (C) 2008 - 2009 The Geeqie Team
8 * This software is released under the GNU General Public License (GNU GPL).
9 * Please read the included file COPYING for more information.
10 * This software comes with no warranty of any kind, use at your own risk!
14 #include "view_file_list.h"
17 #include "cache_maint.h"
22 #include "layout_image.h"
27 #include "ui_fileops.h"
29 #include "ui_tree_edit.h"
30 #include "uri_utils.h"
31 #include "view_file.h"
33 #include <gdk/gdkkeysyms.h> /* for keyboard values */
35 /* Index to tree store */
37 FILE_COLUMN_POINTER = 0,
41 FILE_COLUMN_MULTILINE,
46 FILE_COLUMN_MARKS_LAST = FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
51 /* Index to tree view */
53 FILE_VIEW_COLUMN_MARKS = 0,
54 FILE_VIEW_COLUMN_MARKS_LAST = FILE_VIEW_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
55 FILE_VIEW_COLUMN_THUMB,
56 FILE_VIEW_COLUMN_MULTILINE,
57 FILE_VIEW_COLUMN_NAME,
58 FILE_VIEW_COLUMN_SIZE,
59 FILE_VIEW_COLUMN_DATE,
60 FILE_VIEW_COLUMN_COUNT
65 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd);
66 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data);
67 static void vflist_populate_view(ViewFile *vf);
71 *-----------------------------------------------------------------------------
73 *-----------------------------------------------------------------------------
80 } ViewFileFindRowData;
82 static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
84 ViewFileFindRowData *find = data;
86 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
97 static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter)
100 ViewFileFindRowData data = {fd, iter, FALSE, 0};
102 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
103 gtk_tree_model_foreach(store, vflist_find_row_cb, &data);
113 static FileData *vflist_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter)
116 GtkTreeViewColumn *column;
118 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), x, y,
119 &tpath, &column, NULL, NULL))
125 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
126 gtk_tree_model_get_iter(store, &row, tpath);
127 gtk_tree_path_free(tpath);
128 gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &fd, -1);
137 static gint vflist_find_sidecar_list_idx(GList *work, FileData *fd)
142 FileData *fd_p = work->data;
143 if (fd == fd_p) return i;
147 GList *work2 = fd_p->sidecar_files;
151 if (fd == fd_p) return i;
161 static gint vflist_sidecar_list_count(GList *work)
166 FileData *fd = work->data;
169 GList *work2 = fd->sidecar_files;
181 static gboolean vflist_store_clear_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
184 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
189 static void vflist_store_clear(ViewFile *vf)
192 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
193 gtk_tree_model_foreach(store, vflist_store_clear_cb, NULL);
194 gtk_tree_store_clear(GTK_TREE_STORE(store));
197 void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set)
202 if (vflist_find_row(vf, fd, &iter) < 0) return;
203 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
204 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
207 static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
212 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
214 tpath = gtk_tree_model_get_path(store, iter);
215 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
216 gtk_tree_path_free(tpath);
220 static gint vflist_column_idx(ViewFile *vf, gint store_idx)
222 GList *columns, *work;
225 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
229 GtkTreeViewColumn *column = work->data;
230 if (store_idx == GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx")))
236 g_list_free(columns);
242 *-----------------------------------------------------------------------------
244 *-----------------------------------------------------------------------------
247 static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
248 GtkSelectionData *selection_data, guint info,
249 guint time, gpointer data)
253 gchar *uri_text = NULL;
256 if (!VFLIST(vf)->click_fd) return;
258 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
260 list = vf_selection_get_list(vf);
264 list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
269 uri_text = uri_text_from_filelist(list, &total, (info == TARGET_TEXT_PLAIN));
272 DEBUG_1("%s", uri_text);
274 gtk_selection_data_set(selection_data, selection_data->target,
275 8, (guchar *)uri_text, total);
279 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
283 vflist_color_set(vf, VFLIST(vf)->click_fd, TRUE);
285 if (VFLIST(vf)->thumbs_enabled &&
286 VFLIST(vf)->click_fd && VFLIST(vf)->click_fd->thumb_pixbuf)
290 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
291 items = vf_selection_count(vf, NULL);
295 dnd_set_drag_icon(widget, context, VFLIST(vf)->click_fd->thumb_pixbuf, items);
299 static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
303 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
305 if (context->action == GDK_ACTION_MOVE)
311 static void vflist_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context,
312 int x, int y, GtkSelectionData *selection,
313 guint info, guint time, gpointer data)
317 if (info == TARGET_TEXT_PLAIN) {
318 FileData *fd = vflist_find_data_by_coord(vf, x, y, NULL);
321 /* Add keywords to file */
322 gchar *str = g_strndup((gchar *)selection->data, selection->length);
323 GList *kw_list = string_to_keywords_list(str);
325 metadata_append_list(fd, KEYWORD_KEY, kw_list);
326 string_list_free(kw_list);
329 file notification should handle this automatically
330 if (vf->layout && vf->layout->bar_info) {
331 bar_set_fd(vf->layout->bar_info, fd);
338 void vflist_dnd_init(ViewFile *vf)
340 gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
341 dnd_file_drag_types, dnd_file_drag_types_count,
342 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
343 gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL,
344 dnd_file_drag_types, dnd_file_drag_types_count,
345 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
347 g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
348 G_CALLBACK(vflist_dnd_get), vf);
349 g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
350 G_CALLBACK(vflist_dnd_begin), vf);
351 g_signal_connect(G_OBJECT(vf->listview), "drag_end",
352 G_CALLBACK(vflist_dnd_end), vf);
353 g_signal_connect(G_OBJECT(vf->listview), "drag_data_received",
354 G_CALLBACK(vflist_drag_data_received), vf);
358 *-----------------------------------------------------------------------------
360 *-----------------------------------------------------------------------------
363 GList *vflist_pop_menu_file_list(ViewFile *vf)
365 if (!VFLIST(vf)->click_fd) return NULL;
367 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
369 return vf_selection_get_list(vf);
372 return g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
375 void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
379 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
383 list = vf_selection_get_list(vf);
384 view_window_new_from_list(list);
389 view_window_new(VFLIST(vf)->click_fd);
393 void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
398 list = vf_pop_menu_file_list(vf);
399 if (options->file_ops.enable_in_place_rename &&
400 list && !list->next && VFLIST(vf)->click_fd)
407 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
408 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
412 tpath = gtk_tree_model_get_path(store, &iter);
413 tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
414 vflist_column_idx(vf, FILE_COLUMN_NAME), VFLIST(vf)->click_fd->name,
415 vflist_row_rename_cb, vf);
416 gtk_tree_path_free(tpath);
421 file_util_rename(NULL, list, vf->listview);
424 void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
428 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
431 layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
435 vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled);
439 void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
443 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
447 void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
450 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
451 VFLIST(vf)->click_fd = NULL;
457 *-----------------------------------------------------------------------------
459 *-----------------------------------------------------------------------------
462 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
467 if (!new || !new[0]) return FALSE;
469 new_path = g_build_filename(vf->dir_fd->path, new, NULL);
471 if (strchr(new, G_DIR_SEPARATOR) != NULL)
473 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
474 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
479 gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL);
480 FileData *fd = file_data_new_simple(old_path); /* get the fd from cache */
481 file_util_rename_simple(fd, new_path, vf->listview);
491 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
499 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return;
500 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
501 tpath = gtk_tree_model_get_path(store, &iter);
502 tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
503 gtk_tree_path_free(tpath);
505 popup_menu_position_clamp(menu, x, y, 0);
508 gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
513 if (event->keyval != GDK_Menu) return FALSE;
515 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
521 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
522 gtk_tree_model_get_iter(store, &iter, tpath);
523 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
524 gtk_tree_path_free(tpath);
528 VFLIST(vf)->click_fd = NULL;
531 vf->popup = vf_pop_menu(vf);
532 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
537 gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
543 GtkTreeViewColumn *column;
545 vf->clicked_mark = 0;
547 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
548 &tpath, &column, NULL, NULL))
551 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
553 if (bevent->button == MOUSE_BUTTON_LEFT &&
554 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
557 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
558 vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
560 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
562 gtk_tree_model_get_iter(store, &iter, tpath);
563 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
565 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE);
567 gtk_tree_path_free(tpath);
570 VFLIST(vf)->click_fd = fd;
572 if (bevent->button == MOUSE_BUTTON_RIGHT)
574 vf->popup = vf_pop_menu(vf);
575 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
576 bevent->button, bevent->time);
580 if (!fd) return FALSE;
582 if (bevent->button == MOUSE_BUTTON_MIDDLE)
584 if (!vflist_row_is_selected(vf, fd))
586 vflist_color_set(vf, fd, TRUE);
592 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
593 !(bevent->state & GDK_SHIFT_MASK ) &&
594 !(bevent->state & GDK_CONTROL_MASK ) &&
595 vflist_row_is_selected(vf, fd))
597 GtkTreeSelection *selection;
599 gtk_widget_grab_focus(widget);
602 /* returning FALSE and further processing of the event is needed for
603 correct operation of the expander, to show the sidecar files.
604 It however resets the selection of multiple files. With this condition
605 it should work for both cases */
606 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
607 return (gtk_tree_selection_count_selected_rows(selection) > 1);
611 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
613 if (vf->layout) layout_image_full_screen_start(vf->layout);
620 gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
627 if (bevent->button == MOUSE_BUTTON_MIDDLE)
629 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
632 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
637 if ((bevent->x != 0 || bevent->y != 0) &&
638 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
639 &tpath, NULL, NULL, NULL))
643 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
644 gtk_tree_model_get_iter(store, &iter, tpath);
645 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
646 gtk_tree_path_free(tpath);
649 if (bevent->button == MOUSE_BUTTON_MIDDLE)
651 if (fd && VFLIST(vf)->click_fd == fd)
653 GtkTreeSelection *selection;
655 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
656 if (vflist_row_is_selected(vf, fd))
658 gtk_tree_selection_unselect_iter(selection, &iter);
662 gtk_tree_selection_select_iter(selection, &iter);
668 if (fd && VFLIST(vf)->click_fd == fd &&
669 !(bevent->state & GDK_SHIFT_MASK ) &&
670 !(bevent->state & GDK_CONTROL_MASK ) &&
671 vflist_row_is_selected(vf, fd))
673 GtkTreeSelection *selection;
675 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
676 gtk_tree_selection_unselect_all(selection);
677 gtk_tree_selection_select_iter(selection, &iter);
678 vflist_move_cursor(vf, &iter);
679 // return TRUE;// FIXME - expand
685 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
687 FileData *read_ahead_fd = NULL;
693 cur_fd = layout_image_get_fd(vf->layout);
694 if (sel_fd == cur_fd) return; /* no change */
696 row = g_list_index(vf->list, sel_fd);
697 // FIXME sidecar data
699 if (sel_fd && options->image.enable_read_ahead && row >= 0)
701 if (row > g_list_index(vf->list, cur_fd) &&
702 (guint) (row + 1) < vf_count(vf, NULL))
704 read_ahead_fd = vf_index_get_data(vf, row + 1);
708 read_ahead_fd = vf_index_get_data(vf, row - 1);
712 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
715 static gboolean vflist_select_idle_cb(gpointer data)
721 VFLIST(vf)->select_idle_id = 0;
727 if (VFLIST(vf)->select_fd)
729 vflist_select_image(vf, VFLIST(vf)->select_fd);
730 VFLIST(vf)->select_fd = NULL;
733 VFLIST(vf)->select_idle_id = 0;
737 static void vflist_select_idle_cancel(ViewFile *vf)
739 if (VFLIST(vf)->select_idle_id)
741 g_source_remove(VFLIST(vf)->select_idle_id);
742 VFLIST(vf)->select_idle_id = 0;
746 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
747 gboolean path_currently_selected, gpointer data)
752 if (!path_currently_selected &&
753 gtk_tree_model_get_iter(store, &iter, tpath))
755 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
759 VFLIST(vf)->select_fd = NULL;
763 !VFLIST(vf)->select_idle_id)
765 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
772 *-----------------------------------------------------------------------------
774 *-----------------------------------------------------------------------------
778 static gboolean vflist_dummy_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
779 gboolean path_currently_selected, gpointer data)
785 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
788 gchar *sidecars = NULL;
789 gchar *name_sidecars;
791 const gchar *time = text_from_time(fd->date);
792 gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
795 if (fd->sidecar_files)
797 sidecars = file_data_sc_list_to_string(fd);
798 name_sidecars = g_strdup_printf("%s%s %s", link, fd->name, sidecars);
802 name_sidecars = g_strdup_printf("%s%s", link, fd->name);
804 size = text_from_size(fd->size);
806 multiline = g_strdup_printf("%s\n%s\n%s", name_sidecars, size, time);
808 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
809 FILE_COLUMN_VERSION, fd->version,
810 FILE_COLUMN_THUMB, fd->thumb_pixbuf,
811 FILE_COLUMN_MULTILINE, multiline,
812 FILE_COLUMN_NAME, name_sidecars,
813 FILE_COLUMN_SIZE, size,
814 FILE_COLUMN_DATE, time,
815 #define STORE_SET_IS_SLOW 1
816 #if STORE_SET_IS_SLOW
817 /* this is 3x faster on a directory with 20000 files */
818 FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
819 FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
820 FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
821 FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
822 FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
823 FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
824 #if FILEDATA_MARKS_SIZE != 6
825 #error this needs to be updated
828 FILE_COLUMN_COLOR, FALSE, -1);
830 #if !STORE_SET_IS_SLOW
833 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
834 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
839 g_free(name_sidecars);
843 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected)
848 gint num_ordered = 0;
849 gint num_prepended = 0;
851 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
857 FileData *fd = work->data;
858 gboolean done = FALSE;
862 FileData *old_fd = NULL;
863 gint old_version = 0;
868 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
869 FILE_COLUMN_POINTER, &old_fd,
870 FILE_COLUMN_VERSION, &old_version,
880 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
882 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
884 if (match == 0) g_warning("multiple fd for the same path");
899 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
904 here should be used gtk_tree_store_append, but this function seems to be O(n)
905 and it seems to be much faster to add new entries to the beginning and reorder later
908 gtk_tree_store_prepend(store, &new, parent_iter);
911 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
912 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected);
914 if (g_list_find(selected, fd))
916 /* renamed files - the same fd appears at different position - select it again*/
917 GtkTreeSelection *selection;
918 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
919 gtk_tree_selection_select_iter(selection, &new);
926 file_data_unref(old_fd);
927 valid = gtk_tree_store_remove(store, &iter);
931 if (fd->version != old_version)
933 vflist_setup_iter(vf, store, &iter, fd);
934 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected);
937 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
948 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
949 file_data_unref(old_fd);
951 valid = gtk_tree_store_remove(store, &iter);
954 /* move the prepended entries to the correct position */
958 gint num_total = num_prepended + num_ordered;
959 gint *new_order = g_malloc(num_total * sizeof(gint));
961 for (i = 0; i < num_total; i++)
964 new_order[i] = num_prepended + i;
966 new_order[i] = num_total - 1 - i;
968 gtk_tree_store_reorder(store, parent_iter, new_order);
974 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
977 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
982 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
983 if (!vf->list) return;
989 FileData *fd = work->data;
990 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
995 vf->sort_method = type;
996 vf->sort_ascend = ascend;
998 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1000 new_order = g_malloc(i * sizeof(gint));
1006 FileData *fd = work->data;
1007 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1012 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1013 gtk_tree_store_reorder(store, NULL, new_order);
1016 g_hash_table_destroy(fd_idx_hash);
1020 *-----------------------------------------------------------------------------
1022 *-----------------------------------------------------------------------------
1025 static gboolean vflist_thumb_next(ViewFile *vf);
1027 static void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1032 FileData *fd = work->data;
1035 if (fd->thumb_pixbuf) (*done)++;
1037 if (fd->sidecar_files)
1039 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1045 static gdouble vflist_thumb_progress(ViewFile *vf)
1050 vflist_thumb_progress_count(vf->list, &count, &done);
1052 DEBUG_1("thumb progress: %d of %d", done, count);
1053 return (gdouble)done / count;
1057 static void vflist_thumb_status(ViewFile *vf, gdouble val, const gchar *text)
1059 if (vf->func_thumb_status)
1061 vf->func_thumb_status(vf, val, text, vf->data_thumb_status);
1065 static void vflist_thumb_cleanup(ViewFile *vf)
1067 vflist_thumb_status(vf, 0.0, NULL);
1069 vf->thumbs_running = FALSE;
1071 thumb_loader_free(vf->thumbs_loader);
1072 vf->thumbs_loader = NULL;
1074 vf->thumbs_filedata = NULL;
1077 static void vflist_thumb_stop(ViewFile *vf)
1079 if (vf->thumbs_running) vflist_thumb_cleanup(vf);
1082 static void vflist_thumb_do(ViewFile *vf, ThumbLoader *tl, FileData *fd)
1084 GtkTreeStore *store;
1087 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1089 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1090 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1092 vflist_thumb_status(vf, vflist_thumb_progress(vf), _("Loading thumbs..."));
1095 static void vflist_thumb_error_cb(ThumbLoader *tl, gpointer data)
1097 ViewFile *vf = data;
1099 if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1101 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
1104 while (vflist_thumb_next(vf));
1107 static void vflist_thumb_done_cb(ThumbLoader *tl, gpointer data)
1109 ViewFile *vf = data;
1111 if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1113 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
1116 while (vflist_thumb_next(vf));
1119 static gboolean vflist_thumb_next(ViewFile *vf)
1122 FileData *fd = NULL;
1124 /* first check the visible files */
1126 if (GTK_WIDGET_REALIZED(vf->listview) &&
1127 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1129 GtkTreeModel *store;
1131 gboolean valid = TRUE;
1133 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1134 gtk_tree_model_get_iter(store, &iter, tpath);
1135 gtk_tree_path_free(tpath);
1137 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1141 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1143 if (!nfd->thumb_pixbuf) fd = nfd;
1145 valid = gtk_tree_model_iter_next(store, &iter);
1149 /* then find first undone */
1153 GList *work = vf->list;
1156 FileData *fd_p = work->data;
1157 if (!fd_p->thumb_pixbuf)
1161 GList *work2 = fd_p->sidecar_files;
1163 while (work2 && !fd)
1166 if (!fd_p->thumb_pixbuf) fd = fd_p;
1167 work2 = work2->next;
1177 vflist_thumb_cleanup(vf);
1181 vf->thumbs_filedata = fd;
1183 thumb_loader_free(vf->thumbs_loader);
1185 vf->thumbs_loader = thumb_loader_new(options->thumbnails.max_width, options->thumbnails.max_height);
1186 thumb_loader_set_callbacks(vf->thumbs_loader,
1187 vflist_thumb_done_cb,
1188 vflist_thumb_error_cb,
1192 if (!thumb_loader_start(vf->thumbs_loader, fd))
1194 /* set icon to unknown, continue */
1195 DEBUG_1("thumb loader start failed %s", fd->path);
1196 vflist_thumb_do(vf, vf->thumbs_loader, fd);
1204 void vflist_thumb_update(ViewFile *vf)
1206 vflist_thumb_stop(vf);
1207 if (!VFLIST(vf)->thumbs_enabled) return;
1209 vflist_thumb_status(vf, 0.0, _("Loading thumbs..."));
1210 vf->thumbs_running = TRUE;
1212 if (thumb_format_changed)
1214 GList *work = vf->list;
1217 FileData *fd = work->data;
1218 if (fd->thumb_pixbuf)
1220 g_object_unref(fd->thumb_pixbuf);
1221 fd->thumb_pixbuf = NULL;
1226 thumb_format_changed = FALSE;
1229 while (vflist_thumb_next(vf));
1233 *-----------------------------------------------------------------------------
1235 *-----------------------------------------------------------------------------
1238 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1240 return g_list_nth_data(vf->list, row);
1243 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1246 GList *work, *work2;
1251 FileData *list_fd = work->data;
1252 if (list_fd == fd) return p;
1254 work2 = list_fd->sidecar_files;
1257 /* FIXME: return the same index also for sidecars
1258 it is sufficient for next/prev navigation but it should be rewritten
1259 without using indexes at all
1261 FileData *sidecar_fd = work2->data;
1262 if (sidecar_fd == fd) return p;
1263 work2 = work2->next;
1273 guint vflist_count(ViewFile *vf, gint64 *bytes)
1283 FileData *fd = work->data;
1291 return g_list_length(vf->list);
1294 GList *vflist_get_list(ViewFile *vf)
1302 FileData *fd = work->data;
1305 list = g_list_prepend(list, file_data_ref(fd));
1308 return g_list_reverse(list);
1312 *-----------------------------------------------------------------------------
1314 *-----------------------------------------------------------------------------
1317 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1319 GtkTreeModel *store;
1320 GtkTreeSelection *selection;
1323 gboolean found = FALSE;
1325 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1326 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1328 while (!found && work)
1330 GtkTreePath *tpath = work->data;
1334 gtk_tree_model_get_iter(store, &iter, tpath);
1335 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1336 if (fd_n == fd) found = TRUE;
1339 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1345 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1349 fd = vf_index_get_data(vf, row);
1350 return vflist_row_is_selected(vf, fd);
1353 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1355 GtkTreeModel *store;
1356 GtkTreeSelection *selection;
1360 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1361 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1371 GtkTreePath *tpath = work->data;
1375 gtk_tree_model_get_iter(store, &iter, tpath);
1376 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1385 count = g_list_length(slist);
1386 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1392 GList *vflist_selection_get_list(ViewFile *vf)
1394 GtkTreeModel *store;
1395 GtkTreeSelection *selection;
1400 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1401 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1405 GtkTreePath *tpath = work->data;
1409 gtk_tree_model_get_iter(store, &iter, tpath);
1410 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1412 list = g_list_prepend(list, file_data_ref(fd));
1416 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1419 return g_list_reverse(list);
1422 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1424 GtkTreeModel *store;
1425 GtkTreeSelection *selection;
1430 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1431 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1435 GtkTreePath *tpath = work->data;
1439 gtk_tree_model_get_iter(store, &iter, tpath);
1440 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1442 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1446 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1449 return g_list_reverse(list);
1452 void vflist_select_all(ViewFile *vf)
1454 GtkTreeSelection *selection;
1456 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1457 gtk_tree_selection_select_all(selection);
1459 VFLIST(vf)->select_fd = NULL;
1462 void vflist_select_none(ViewFile *vf)
1464 GtkTreeSelection *selection;
1466 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1467 gtk_tree_selection_unselect_all(selection);
1470 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1475 tpath = gtk_tree_model_get_path(store, iter);
1476 result = gtk_tree_path_prev(tpath);
1478 gtk_tree_model_get_iter(store, iter, tpath);
1480 gtk_tree_path_free(tpath);
1485 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1487 if (!gtk_tree_model_get_iter_first(store, iter))
1492 GtkTreeIter next = *iter;
1494 if (gtk_tree_model_iter_next(store, &next))
1503 void vflist_select_invert(ViewFile *vf)
1506 GtkTreeSelection *selection;
1507 GtkTreeModel *store;
1510 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1511 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1513 /* Backward iteration prevents scrolling to the end of the list,
1514 * it scrolls to the first selected row instead. */
1515 valid = tree_model_get_iter_last(store, &iter);
1519 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1522 gtk_tree_selection_unselect_iter(selection, &iter);
1524 gtk_tree_selection_select_iter(selection, &iter);
1526 valid = tree_model_iter_prev(store, &iter);
1530 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1534 if (vflist_find_row(vf, fd, &iter) < 0) return;
1536 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1538 if (!vflist_row_is_selected(vf, fd))
1540 GtkTreeSelection *selection;
1541 GtkTreeModel *store;
1544 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1545 gtk_tree_selection_unselect_all(selection);
1546 gtk_tree_selection_select_iter(selection, &iter);
1547 vflist_move_cursor(vf, &iter);
1549 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1550 tpath = gtk_tree_model_get_path(store, &iter);
1551 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1552 gtk_tree_path_free(tpath);
1556 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1559 FileData *fd = NULL;
1561 if (sel_fd->parent) sel_fd = sel_fd->parent;
1570 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1572 if (match >= 0) break;
1575 if (fd) vflist_select_by_fd(vf, fd);
1579 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1581 GtkTreeModel *store;
1583 GtkTreeSelection *selection;
1587 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1589 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1590 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1592 valid = gtk_tree_model_get_iter_first(store, &iter);
1596 gboolean mark_val, selected;
1597 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1599 mark_val = file_data_get_mark(fd, n);
1600 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1604 case MTS_MODE_SET: selected = mark_val;
1606 case MTS_MODE_OR: selected = mark_val | selected;
1608 case MTS_MODE_AND: selected = mark_val & selected;
1610 case MTS_MODE_MINUS: selected = !mark_val & selected;
1615 gtk_tree_selection_select_iter(selection, &iter);
1617 gtk_tree_selection_unselect_iter(selection, &iter);
1619 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1623 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1625 GtkTreeModel *store;
1626 GtkTreeSelection *selection;
1631 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1633 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1634 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1638 GtkTreePath *tpath = work->data;
1642 gtk_tree_model_get_iter(store, &iter, tpath);
1643 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1645 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1649 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1651 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1653 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1657 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1659 vf_refresh_idle(vf);
1663 /* mark functions can have various side effects - update all columns to be sure */
1664 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1668 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1672 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1677 *-----------------------------------------------------------------------------
1679 *-----------------------------------------------------------------------------
1682 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb)
1684 GtkTreeViewColumn *column;
1685 GtkCellRenderer *cell;
1689 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1690 if (!column) return;
1692 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1694 list = gtk_tree_view_column_get_cell_renderers(column);
1699 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1700 gtk_tree_view_column_set_visible(column, thumb);
1702 multiline = (thumb && options->thumbnails.max_height >= 48);
1704 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_MULTILINE);
1705 if (!column) return;
1706 gtk_tree_view_column_set_visible(column, multiline);
1707 if (multiline) gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1709 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_NAME);
1710 if (!column) return;
1711 gtk_tree_view_column_set_visible(column, !multiline);
1712 if (!multiline) gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1714 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1715 if (!column) return;
1716 gtk_tree_view_column_set_visible(column, !multiline);
1718 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1719 if (!column) return;
1720 gtk_tree_view_column_set_visible(column, !multiline);
1722 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1725 static void vflist_populate_view(ViewFile *vf)
1727 GtkTreeStore *store;
1728 gboolean thumbs_enabled;
1731 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1732 thumbs_enabled = VFLIST(vf)->thumbs_enabled;
1734 vflist_thumb_stop(vf);
1738 vflist_store_clear(vf);
1743 vflist_listview_set_columns(vf->listview, thumbs_enabled);
1745 selected = vflist_selection_get_list(vf);
1747 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected);
1749 if (selected && vflist_selection_count(vf, NULL) == 0)
1751 /* all selected files disappeared */
1752 vflist_select_closest(vf, selected->data);
1755 filelist_free(selected);
1758 vflist_thumb_update(vf);
1761 gboolean vflist_refresh(ViewFile *vf)
1764 gboolean ret = TRUE;
1766 old_list = vf->list;
1769 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1772 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1774 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1775 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1776 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1778 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1779 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1782 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1784 vflist_populate_view(vf);
1786 filelist_free(old_list);
1787 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1794 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1796 #define CELL_HEIGHT_OVERRIDE 512
1798 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1802 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1803 if (spec && G_IS_PARAM_SPEC_INT(spec))
1805 GParamSpecInt *spec_int;
1807 spec_int = G_PARAM_SPEC_INT(spec);
1808 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1812 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1814 static GdkColor color;
1815 static GtkWidget *done = NULL;
1821 style = gtk_widget_get_style(widget);
1822 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1823 shift_color(&color, -1, 0);
1830 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1831 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1833 ViewFile *vf = data;
1836 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1837 g_object_set(G_OBJECT(cell),
1838 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1839 "cell-background-set", set, NULL);
1842 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1844 GtkTreeViewColumn *column;
1845 GtkCellRenderer *renderer;
1847 column = gtk_tree_view_column_new();
1848 gtk_tree_view_column_set_title(column, title);
1849 gtk_tree_view_column_set_min_width(column, 4);
1853 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1854 renderer = gtk_cell_renderer_text_new();
1857 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1859 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1860 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1862 gtk_tree_view_column_set_expand(column, TRUE);
1866 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1867 renderer = gtk_cell_renderer_pixbuf_new();
1868 cell_renderer_height_override(renderer);
1869 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1870 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1873 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1874 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1875 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1877 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1880 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1882 ViewFile *vf = data;
1883 GtkTreeStore *store;
1884 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1890 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1891 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1894 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1896 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1898 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1900 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1901 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1902 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1904 vf_refresh_idle(vf);
1908 /* mark functions can have various side effects - update all columns to be sure */
1909 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1911 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1913 gtk_tree_path_free(path);
1916 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1918 GtkTreeViewColumn *column;
1919 GtkCellRenderer *renderer;
1920 GtkTreeStore *store;
1923 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1925 renderer = gtk_cell_renderer_toggle_new();
1926 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1928 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1929 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1930 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1932 index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1933 gtk_tree_view_column_set_fixed_width(column, 22);
1934 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1937 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1941 *-----------------------------------------------------------------------------
1943 *-----------------------------------------------------------------------------
1946 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1948 if (!dir_fd) return FALSE;
1949 if (vf->dir_fd == dir_fd) return TRUE;
1951 file_data_unref(vf->dir_fd);
1952 vf->dir_fd = file_data_ref(dir_fd);
1954 /* force complete reload */
1955 vflist_store_clear(vf);
1957 filelist_free(vf->list);
1960 return vf_refresh(vf);
1963 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1965 ViewFile *vf = data;
1967 file_data_unregister_notify_func(vf_notify_cb, vf);
1969 vflist_select_idle_cancel(vf);
1970 vf_refresh_idle_cancel(vf);
1971 vflist_thumb_stop(vf);
1973 filelist_free(vf->list);
1976 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1978 GtkTreeStore *store;
1979 GtkTreeSelection *selection;
1980 GType flist_types[FILE_COLUMN_COUNT];
1984 vf->info = g_new0(ViewFileInfoList, 1);
1986 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1987 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1988 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1989 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1990 flist_types[FILE_COLUMN_MULTILINE] = G_TYPE_STRING;
1991 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1992 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1993 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1994 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1995 flist_types[i] = G_TYPE_BOOLEAN;
1997 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
1999 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2000 g_object_unref(store);
2002 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2003 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
2004 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
2006 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2007 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2011 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2013 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2014 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
2018 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2019 g_assert(column == FILE_VIEW_COLUMN_THUMB);
2022 vflist_listview_add_column(vf, FILE_COLUMN_MULTILINE, _("Name"), FALSE, FALSE, TRUE);
2023 g_assert(column == FILE_VIEW_COLUMN_MULTILINE);
2026 vflist_listview_add_column(vf, FILE_COLUMN_NAME, _("Name"), FALSE, FALSE, TRUE);
2027 g_assert(column == FILE_VIEW_COLUMN_NAME);
2030 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2031 g_assert(column == FILE_VIEW_COLUMN_SIZE);
2034 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2035 g_assert(column == FILE_VIEW_COLUMN_DATE);
2038 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2042 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2044 if (VFLIST(vf)->thumbs_enabled == enable) return;
2046 VFLIST(vf)->thumbs_enabled = enable;
2047 if (vf->layout) vf_refresh(vf);
2050 void vflist_marks_set(ViewFile *vf, gboolean enable)
2052 GList *columns, *work;
2054 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2059 GtkTreeViewColumn *column = work->data;
2060 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2063 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2064 gtk_tree_view_column_set_visible(column, enable);
2067 g_list_free(columns);
2071 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */