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,
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_NAME,
57 FILE_VIEW_COLUMN_SIZE,
58 FILE_VIEW_COLUMN_DATE,
59 FILE_VIEW_COLUMN_COUNT
64 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd);
65 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data);
66 static void vflist_populate_view(ViewFile *vf);
67 static gboolean vflist_is_multiline(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)
366 if (!VFLIST(vf)->click_fd) return NULL;
368 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
370 return vf_selection_get_list(vf);
373 list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
375 if (VFLIST(vf)->click_fd->sidecar_files)
377 /* check if the row is expanded */
381 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
382 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
386 tpath = gtk_tree_model_get_path(store, &iter);
387 if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
389 /* unexpanded - add whole group */
390 GList *work = VFLIST(vf)->click_fd->sidecar_files;
393 FileData *sfd = work->data;
394 list = g_list_prepend(list, file_data_ref(sfd));
398 gtk_tree_path_free(tpath);
400 list = g_list_reverse(list);
406 void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
410 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
414 list = vf_selection_get_list(vf);
415 view_window_new_from_list(list);
420 view_window_new(VFLIST(vf)->click_fd);
424 void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
429 list = vf_pop_menu_file_list(vf);
430 if (options->file_ops.enable_in_place_rename &&
431 list && !list->next && VFLIST(vf)->click_fd)
438 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
439 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
443 tpath = gtk_tree_model_get_path(store, &iter);
444 tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
445 vflist_column_idx(vf, FILE_COLUMN_NAME), VFLIST(vf)->click_fd->name,
446 vflist_row_rename_cb, vf);
447 gtk_tree_path_free(tpath);
452 file_util_rename(NULL, list, vf->listview);
455 void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
459 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
462 layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
466 vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled);
470 void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
474 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
478 void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
481 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
482 VFLIST(vf)->click_fd = NULL;
488 *-----------------------------------------------------------------------------
490 *-----------------------------------------------------------------------------
493 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
498 if (!new || !new[0]) return FALSE;
500 new_path = g_build_filename(vf->dir_fd->path, new, NULL);
502 if (strchr(new, G_DIR_SEPARATOR) != NULL)
504 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
505 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
510 gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL);
511 FileData *fd = file_data_new_simple(old_path); /* get the fd from cache */
512 file_util_rename_simple(fd, new_path, vf->listview);
522 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
530 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return;
531 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
532 tpath = gtk_tree_model_get_path(store, &iter);
533 tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
534 gtk_tree_path_free(tpath);
536 popup_menu_position_clamp(menu, x, y, 0);
539 gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
544 if (event->keyval != GDK_Menu) return FALSE;
546 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
552 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
553 gtk_tree_model_get_iter(store, &iter, tpath);
554 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
555 gtk_tree_path_free(tpath);
559 VFLIST(vf)->click_fd = NULL;
562 vf->popup = vf_pop_menu(vf);
563 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
568 gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
574 GtkTreeViewColumn *column;
576 vf->clicked_mark = 0;
578 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
579 &tpath, &column, NULL, NULL))
582 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
584 if (bevent->button == MOUSE_BUTTON_LEFT &&
585 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
588 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
589 vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
591 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
593 gtk_tree_model_get_iter(store, &iter, tpath);
594 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
596 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE);
598 gtk_tree_path_free(tpath);
601 VFLIST(vf)->click_fd = fd;
603 if (bevent->button == MOUSE_BUTTON_RIGHT)
605 vf->popup = vf_pop_menu(vf);
606 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
607 bevent->button, bevent->time);
611 if (!fd) return FALSE;
613 if (bevent->button == MOUSE_BUTTON_MIDDLE)
615 if (!vflist_row_is_selected(vf, fd))
617 vflist_color_set(vf, fd, TRUE);
623 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
624 !(bevent->state & GDK_SHIFT_MASK ) &&
625 !(bevent->state & GDK_CONTROL_MASK ) &&
626 vflist_row_is_selected(vf, fd))
628 GtkTreeSelection *selection;
630 gtk_widget_grab_focus(widget);
633 /* returning FALSE and further processing of the event is needed for
634 correct operation of the expander, to show the sidecar files.
635 It however resets the selection of multiple files. With this condition
636 it should work for both cases */
637 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
638 return (gtk_tree_selection_count_selected_rows(selection) > 1);
642 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
644 if (vf->layout) layout_image_full_screen_start(vf->layout);
651 gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
658 if (bevent->button == MOUSE_BUTTON_MIDDLE)
660 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
663 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
668 if ((bevent->x != 0 || bevent->y != 0) &&
669 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
670 &tpath, NULL, NULL, NULL))
674 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
675 gtk_tree_model_get_iter(store, &iter, tpath);
676 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
677 gtk_tree_path_free(tpath);
680 if (bevent->button == MOUSE_BUTTON_MIDDLE)
682 if (fd && VFLIST(vf)->click_fd == fd)
684 GtkTreeSelection *selection;
686 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
687 if (vflist_row_is_selected(vf, fd))
689 gtk_tree_selection_unselect_iter(selection, &iter);
693 gtk_tree_selection_select_iter(selection, &iter);
699 if (fd && VFLIST(vf)->click_fd == fd &&
700 !(bevent->state & GDK_SHIFT_MASK ) &&
701 !(bevent->state & GDK_CONTROL_MASK ) &&
702 vflist_row_is_selected(vf, fd))
704 GtkTreeSelection *selection;
706 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
707 gtk_tree_selection_unselect_all(selection);
708 gtk_tree_selection_select_iter(selection, &iter);
709 vflist_move_cursor(vf, &iter);
710 // return TRUE;// FIXME - expand
716 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
718 FileData *read_ahead_fd = NULL;
724 cur_fd = layout_image_get_fd(vf->layout);
725 if (sel_fd == cur_fd) return; /* no change */
727 row = g_list_index(vf->list, sel_fd);
728 // FIXME sidecar data
730 if (sel_fd && options->image.enable_read_ahead && row >= 0)
732 if (row > g_list_index(vf->list, cur_fd) &&
733 (guint) (row + 1) < vf_count(vf, NULL))
735 read_ahead_fd = vf_index_get_data(vf, row + 1);
739 read_ahead_fd = vf_index_get_data(vf, row - 1);
743 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
746 static gboolean vflist_select_idle_cb(gpointer data)
752 VFLIST(vf)->select_idle_id = 0;
758 if (VFLIST(vf)->select_fd)
760 vflist_select_image(vf, VFLIST(vf)->select_fd);
761 VFLIST(vf)->select_fd = NULL;
764 VFLIST(vf)->select_idle_id = 0;
768 static void vflist_select_idle_cancel(ViewFile *vf)
770 if (VFLIST(vf)->select_idle_id)
772 g_source_remove(VFLIST(vf)->select_idle_id);
773 VFLIST(vf)->select_idle_id = 0;
777 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
778 gboolean path_currently_selected, gpointer data)
783 if (!path_currently_selected &&
784 gtk_tree_model_get_iter(store, &iter, tpath))
786 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
790 VFLIST(vf)->select_fd = NULL;
794 !VFLIST(vf)->select_idle_id)
796 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
803 *-----------------------------------------------------------------------------
805 *-----------------------------------------------------------------------------
809 static gboolean vflist_dummy_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
810 gboolean path_currently_selected, gpointer data)
815 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
818 gchar *sidecars = NULL;
820 const gchar *time = text_from_time(fd->date);
821 gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
822 const gchar *disabled_grouping;
824 sidecars = file_data_sc_list_to_string(fd);
826 disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
827 name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
828 size = text_from_size(fd->size);
830 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
831 FILE_COLUMN_VERSION, fd->version,
832 FILE_COLUMN_THUMB, fd->thumb_pixbuf,
833 FILE_COLUMN_SIDECARS, sidecars,
834 FILE_COLUMN_NAME, name,
835 FILE_COLUMN_SIZE, size,
836 FILE_COLUMN_DATE, time,
837 #define STORE_SET_IS_SLOW 1
838 #if STORE_SET_IS_SLOW
839 /* this is 3x faster on a directory with 20000 files */
840 FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
841 FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
842 FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
843 FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
844 FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
845 FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
846 #if FILEDATA_MARKS_SIZE != 6
847 #error this needs to be updated
850 FILE_COLUMN_COLOR, FALSE, -1);
852 #if !STORE_SET_IS_SLOW
855 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
856 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
864 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected)
869 gint num_ordered = 0;
870 gint num_prepended = 0;
872 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
878 FileData *fd = work->data;
879 gboolean done = FALSE;
883 FileData *old_fd = NULL;
884 gint old_version = 0;
889 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
890 FILE_COLUMN_POINTER, &old_fd,
891 FILE_COLUMN_VERSION, &old_version,
901 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
903 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
905 if (match == 0) g_warning("multiple fd for the same path");
920 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
925 here should be used gtk_tree_store_append, but this function seems to be O(n)
926 and it seems to be much faster to add new entries to the beginning and reorder later
929 gtk_tree_store_prepend(store, &new, parent_iter);
932 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
933 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected);
935 if (g_list_find(selected, fd))
937 /* renamed files - the same fd appears at different position - select it again*/
938 GtkTreeSelection *selection;
939 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
940 gtk_tree_selection_select_iter(selection, &new);
947 file_data_unref(old_fd);
948 valid = gtk_tree_store_remove(store, &iter);
952 if (fd->version != old_version)
954 vflist_setup_iter(vf, store, &iter, fd);
955 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected);
958 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
969 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
970 file_data_unref(old_fd);
972 valid = gtk_tree_store_remove(store, &iter);
975 /* move the prepended entries to the correct position */
979 gint num_total = num_prepended + num_ordered;
980 gint *new_order = g_malloc(num_total * sizeof(gint));
982 for (i = 0; i < num_total; i++)
985 new_order[i] = num_prepended + i;
987 new_order[i] = num_total - 1 - i;
989 gtk_tree_store_reorder(store, parent_iter, new_order);
995 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
998 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
1000 GtkTreeStore *store;
1003 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1004 if (!vf->list) return;
1010 FileData *fd = work->data;
1011 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1016 vf->sort_method = type;
1017 vf->sort_ascend = ascend;
1019 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1021 new_order = g_malloc(i * sizeof(gint));
1027 FileData *fd = work->data;
1028 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1033 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1034 gtk_tree_store_reorder(store, NULL, new_order);
1037 g_hash_table_destroy(fd_idx_hash);
1041 *-----------------------------------------------------------------------------
1043 *-----------------------------------------------------------------------------
1047 void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1052 FileData *fd = work->data;
1055 if (fd->thumb_pixbuf) (*done)++;
1057 if (fd->sidecar_files)
1059 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1065 void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
1067 GtkTreeStore *store;
1070 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1072 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1073 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1076 FileData *vflist_thumb_next_fd(ViewFile *vf)
1079 FileData *fd = NULL;
1081 /* first check the visible files */
1083 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1085 GtkTreeModel *store;
1087 gboolean valid = TRUE;
1089 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1090 gtk_tree_model_get_iter(store, &iter, tpath);
1091 gtk_tree_path_free(tpath);
1093 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1097 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1099 if (!nfd->thumb_pixbuf) fd = nfd;
1101 valid = gtk_tree_model_iter_next(store, &iter);
1105 /* then find first undone */
1109 GList *work = vf->list;
1112 FileData *fd_p = work->data;
1113 if (!fd_p->thumb_pixbuf)
1117 GList *work2 = fd_p->sidecar_files;
1119 while (work2 && !fd)
1122 if (!fd_p->thumb_pixbuf) fd = fd_p;
1123 work2 = work2->next;
1134 void vflist_thumb_reset_all(ViewFile *vf)
1136 GList *work = vf->list;
1139 FileData *fd = work->data;
1140 if (fd->thumb_pixbuf)
1142 g_object_unref(fd->thumb_pixbuf);
1143 fd->thumb_pixbuf = NULL;
1150 *-----------------------------------------------------------------------------
1152 *-----------------------------------------------------------------------------
1155 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1157 return g_list_nth_data(vf->list, row);
1160 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1163 GList *work, *work2;
1168 FileData *list_fd = work->data;
1169 if (list_fd == fd) return p;
1171 work2 = list_fd->sidecar_files;
1174 /* FIXME: return the same index also for sidecars
1175 it is sufficient for next/prev navigation but it should be rewritten
1176 without using indexes at all
1178 FileData *sidecar_fd = work2->data;
1179 if (sidecar_fd == fd) return p;
1180 work2 = work2->next;
1190 guint vflist_count(ViewFile *vf, gint64 *bytes)
1200 FileData *fd = work->data;
1208 return g_list_length(vf->list);
1211 GList *vflist_get_list(ViewFile *vf)
1219 FileData *fd = work->data;
1222 list = g_list_prepend(list, file_data_ref(fd));
1225 return g_list_reverse(list);
1229 *-----------------------------------------------------------------------------
1231 *-----------------------------------------------------------------------------
1234 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1236 GtkTreeModel *store;
1237 GtkTreeSelection *selection;
1240 gboolean found = FALSE;
1242 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1243 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1245 while (!found && work)
1247 GtkTreePath *tpath = work->data;
1251 gtk_tree_model_get_iter(store, &iter, tpath);
1252 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1253 if (fd_n == fd) found = TRUE;
1256 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1262 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1266 fd = vf_index_get_data(vf, row);
1267 return vflist_row_is_selected(vf, fd);
1270 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1272 GtkTreeModel *store;
1273 GtkTreeSelection *selection;
1277 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1278 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1288 GtkTreePath *tpath = work->data;
1292 gtk_tree_model_get_iter(store, &iter, tpath);
1293 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1302 count = g_list_length(slist);
1303 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1309 GList *vflist_selection_get_list(ViewFile *vf)
1311 GtkTreeModel *store;
1312 GtkTreeSelection *selection;
1317 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1318 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1322 GtkTreePath *tpath = work->data;
1326 gtk_tree_model_get_iter(store, &iter, tpath);
1327 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1329 list = g_list_prepend(list, file_data_ref(fd));
1331 if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
1333 /* unexpanded - add whole group */
1334 GList *work2 = fd->sidecar_files;
1337 FileData *sfd = work2->data;
1338 list = g_list_prepend(list, file_data_ref(sfd));
1339 work2 = work2->next;
1345 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1348 return g_list_reverse(list);
1351 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1353 GtkTreeModel *store;
1354 GtkTreeSelection *selection;
1359 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1360 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1364 GtkTreePath *tpath = work->data;
1368 gtk_tree_model_get_iter(store, &iter, tpath);
1369 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1371 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1375 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1378 return g_list_reverse(list);
1381 void vflist_select_all(ViewFile *vf)
1383 GtkTreeSelection *selection;
1385 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1386 gtk_tree_selection_select_all(selection);
1388 VFLIST(vf)->select_fd = NULL;
1391 void vflist_select_none(ViewFile *vf)
1393 GtkTreeSelection *selection;
1395 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1396 gtk_tree_selection_unselect_all(selection);
1399 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1404 tpath = gtk_tree_model_get_path(store, iter);
1405 result = gtk_tree_path_prev(tpath);
1407 gtk_tree_model_get_iter(store, iter, tpath);
1409 gtk_tree_path_free(tpath);
1414 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1416 if (!gtk_tree_model_get_iter_first(store, iter))
1421 GtkTreeIter next = *iter;
1423 if (gtk_tree_model_iter_next(store, &next))
1432 void vflist_select_invert(ViewFile *vf)
1435 GtkTreeSelection *selection;
1436 GtkTreeModel *store;
1439 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1440 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1442 /* Backward iteration prevents scrolling to the end of the list,
1443 * it scrolls to the first selected row instead. */
1444 valid = tree_model_get_iter_last(store, &iter);
1448 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1451 gtk_tree_selection_unselect_iter(selection, &iter);
1453 gtk_tree_selection_select_iter(selection, &iter);
1455 valid = tree_model_iter_prev(store, &iter);
1459 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1463 if (vflist_find_row(vf, fd, &iter) < 0) return;
1465 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1467 if (!vflist_row_is_selected(vf, fd))
1469 GtkTreeSelection *selection;
1470 GtkTreeModel *store;
1473 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1474 gtk_tree_selection_unselect_all(selection);
1475 gtk_tree_selection_select_iter(selection, &iter);
1476 vflist_move_cursor(vf, &iter);
1478 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1479 tpath = gtk_tree_model_get_path(store, &iter);
1480 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1481 gtk_tree_path_free(tpath);
1485 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1488 FileData *fd = NULL;
1490 if (sel_fd->parent) sel_fd = sel_fd->parent;
1499 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1501 if (match >= 0) break;
1504 if (fd) vflist_select_by_fd(vf, fd);
1508 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1510 GtkTreeModel *store;
1512 GtkTreeSelection *selection;
1516 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1518 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1519 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1521 valid = gtk_tree_model_get_iter_first(store, &iter);
1525 gboolean mark_val, selected;
1526 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1528 mark_val = file_data_get_mark(fd, n);
1529 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1533 case MTS_MODE_SET: selected = mark_val;
1535 case MTS_MODE_OR: selected = mark_val | selected;
1537 case MTS_MODE_AND: selected = mark_val & selected;
1539 case MTS_MODE_MINUS: selected = !mark_val & selected;
1544 gtk_tree_selection_select_iter(selection, &iter);
1546 gtk_tree_selection_unselect_iter(selection, &iter);
1548 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1552 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1554 GtkTreeModel *store;
1555 GtkTreeSelection *selection;
1560 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1562 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1563 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1567 GtkTreePath *tpath = work->data;
1571 gtk_tree_model_get_iter(store, &iter, tpath);
1572 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1574 /* the change has a very limited range and the standard notification would trigger
1575 complete re-read of the directory - try to do only minimal update instead */
1576 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1580 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1582 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1584 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1588 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1590 vf_refresh_idle(vf);
1594 /* mark functions can have various side effects - update all columns to be sure */
1595 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1596 /* mark functions can change sidecars too */
1597 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL);
1601 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1605 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1610 *-----------------------------------------------------------------------------
1612 *-----------------------------------------------------------------------------
1615 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
1617 GtkTreeViewColumn *column;
1618 GtkCellRenderer *cell;
1621 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1622 if (!column) return;
1624 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1626 list = gtk_tree_view_column_get_cell_renderers(column);
1631 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1632 gtk_tree_view_column_set_visible(column, thumb);
1634 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_NAME);
1635 if (!column) return;
1636 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1638 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1639 if (!column) return;
1640 gtk_tree_view_column_set_visible(column, !multiline);
1642 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1643 if (!column) return;
1644 gtk_tree_view_column_set_visible(column, !multiline);
1646 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1649 static gboolean vflist_is_multiline(ViewFile *vf)
1651 return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
1655 static void vflist_populate_view(ViewFile *vf)
1657 GtkTreeStore *store;
1660 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1666 vflist_store_clear(vf);
1671 vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
1673 selected = vflist_selection_get_list(vf);
1675 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected);
1677 if (selected && vflist_selection_count(vf, NULL) == 0)
1679 /* all selected files disappeared */
1680 vflist_select_closest(vf, selected->data);
1683 filelist_free(selected);
1686 vf_thumb_update(vf);
1689 gboolean vflist_refresh(ViewFile *vf)
1692 gboolean ret = TRUE;
1694 old_list = vf->list;
1697 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1700 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1702 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1703 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1704 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1706 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1707 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1710 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1712 vflist_populate_view(vf);
1714 filelist_free(old_list);
1715 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1722 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1724 #define CELL_HEIGHT_OVERRIDE 512
1726 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1730 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1731 if (spec && G_IS_PARAM_SPEC_INT(spec))
1733 GParamSpecInt *spec_int;
1735 spec_int = G_PARAM_SPEC_INT(spec);
1736 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1740 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1742 static GdkColor color;
1743 static GtkWidget *done = NULL;
1749 style = gtk_widget_get_style(widget);
1750 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1751 shift_color(&color, -1, 0);
1758 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1759 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1761 ViewFile *vf = data;
1764 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1765 g_object_set(G_OBJECT(cell),
1766 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1767 "cell-background-set", set, NULL);
1770 static void vflist_name_cell_data_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1771 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1773 ViewFile *vf = data;
1774 gboolean multiline = vflist_is_multiline(vf);
1779 tpath = gtk_tree_model_get_path(tree_model, iter);
1780 expanded = gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath);
1781 gtk_tree_path_free(tpath);
1785 gchar *name, *sidecars, *size, *time;
1786 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_SIDECARS, &sidecars,
1787 FILE_COLUMN_NAME, &name,
1788 FILE_COLUMN_SIZE, &size,
1789 FILE_COLUMN_DATE, &time,
1791 text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time);
1799 gchar *name, *sidecars;
1800 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_SIDECARS, &sidecars,
1801 FILE_COLUMN_NAME, &name,
1803 text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars);
1808 g_object_set(cell, "text", text, NULL);
1811 /* now call the common cb */
1812 vflist_listview_color_cb(tree_column, cell, tree_model, iter, data);
1816 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify,
1817 gboolean expand, GtkTreeCellDataFunc cell_data_func)
1819 GtkTreeViewColumn *column;
1820 GtkCellRenderer *renderer;
1822 column = gtk_tree_view_column_new();
1823 gtk_tree_view_column_set_title(column, title);
1824 gtk_tree_view_column_set_min_width(column, 4);
1828 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1829 renderer = gtk_cell_renderer_text_new();
1832 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1834 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1835 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1837 gtk_tree_view_column_set_expand(column, TRUE);
1841 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1842 renderer = gtk_cell_renderer_pixbuf_new();
1843 cell_renderer_height_override(renderer);
1844 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1845 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1849 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func, vf, NULL);
1851 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1852 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1853 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1855 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1858 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1860 ViewFile *vf = data;
1861 GtkTreeStore *store;
1862 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1868 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1869 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1872 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1874 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1876 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1879 /* the change has a very limited range and the standard notification would trigger
1880 complete re-read of the directory - try to do only minimal update instead */
1881 file_data_unregister_notify_func(vf_notify_cb, vf);
1882 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1883 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1885 vf_refresh_idle(vf);
1889 /* mark functions can have various side effects - update all columns to be sure */
1890 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1891 /* mark functions can change sidecars too */
1892 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL);
1894 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1896 gtk_tree_path_free(path);
1899 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1901 GtkTreeViewColumn *column;
1902 GtkCellRenderer *renderer;
1903 GtkTreeStore *store;
1906 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1908 renderer = gtk_cell_renderer_toggle_new();
1909 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1911 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1912 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1913 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1915 index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1916 gtk_tree_view_column_set_fixed_width(column, 22);
1917 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1920 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1924 *-----------------------------------------------------------------------------
1926 *-----------------------------------------------------------------------------
1929 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1931 if (!dir_fd) return FALSE;
1932 if (vf->dir_fd == dir_fd) return TRUE;
1934 file_data_unref(vf->dir_fd);
1935 vf->dir_fd = file_data_ref(dir_fd);
1937 /* force complete reload */
1938 vflist_store_clear(vf);
1940 filelist_free(vf->list);
1943 return vf_refresh(vf);
1946 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1948 ViewFile *vf = data;
1950 file_data_unregister_notify_func(vf_notify_cb, vf);
1952 vflist_select_idle_cancel(vf);
1953 vf_refresh_idle_cancel(vf);
1956 filelist_free(vf->list);
1959 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1961 GtkTreeStore *store;
1962 GtkTreeSelection *selection;
1963 GType flist_types[FILE_COLUMN_COUNT];
1967 vf->info = g_new0(ViewFileInfoList, 1);
1969 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1970 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1971 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1972 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1973 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
1974 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1975 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1976 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1977 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1978 flist_types[i] = G_TYPE_BOOLEAN;
1980 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
1982 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1983 g_object_unref(store);
1985 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1986 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1987 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
1989 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
1990 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
1994 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
1996 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
1997 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
2001 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE, NULL);
2002 g_assert(column == FILE_VIEW_COLUMN_THUMB);
2005 vflist_listview_add_column(vf, FILE_COLUMN_NAME, _("Name"), FALSE, FALSE, TRUE, vflist_name_cell_data_cb);
2006 g_assert(column == FILE_VIEW_COLUMN_NAME);
2009 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE, NULL);
2010 g_assert(column == FILE_VIEW_COLUMN_SIZE);
2013 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE, NULL);
2014 g_assert(column == FILE_VIEW_COLUMN_DATE);
2017 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2021 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2023 if (VFLIST(vf)->thumbs_enabled == enable) return;
2025 VFLIST(vf)->thumbs_enabled = enable;
2026 if (vf->layout) vf_refresh(vf);
2029 void vflist_marks_set(ViewFile *vf, gboolean enable)
2031 GList *columns, *work;
2033 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2038 GtkTreeViewColumn *column = work->data;
2039 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2042 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2043 gtk_tree_view_column_set_visible(column, enable);
2046 g_list_free(columns);
2050 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */