4 * Copyright (C) 2008 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"
16 #include "cache_maint.h"
22 #include "layout_image.h"
26 #include "ui_bookmark.h"
27 #include "ui_fileops.h"
29 #include "ui_tree_edit.h"
30 #include "view_file.h"
32 #include <gdk/gdkkeysyms.h> /* for keyboard values */
36 FILE_COLUMN_POINTER = 0,
45 FILE_COLUMN_MARKS_LAST = FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
50 static gint vflist_row_is_selected(ViewFile *vf, FileData *fd);
51 static gint vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data);
52 static void vflist_populate_view(ViewFile *vf);
53 static void vflist_notify_cb(FileData *fd, NotifyType type, gpointer data);
57 *-----------------------------------------------------------------------------
59 *-----------------------------------------------------------------------------
66 } ViewFileFindRowData;
68 static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
70 ViewFileFindRowData *find = data;
72 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
83 static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter)
86 ViewFileFindRowData data = {fd, iter, 0, 0};
88 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
89 gtk_tree_model_foreach(store, vflist_find_row_cb, &data);
101 static gint vflist_find_sidecar_list_idx(GList *work, FileData *fd)
106 FileData *fd_p = work->data;
107 if (fd == fd_p) return i;
111 GList *work2 = fd_p->sidecar_files;
115 if (fd == fd_p) return i;
126 static gint vflist_sidecar_list_count(GList *work)
131 FileData *fd = work->data;
134 GList *work2 = fd->sidecar_files;
145 static gboolean vflist_store_clear_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
148 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
153 static void vflist_store_clear(ViewFile *vf)
156 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
157 gtk_tree_model_foreach(store, vflist_store_clear_cb, NULL);
158 gtk_tree_store_clear(GTK_TREE_STORE(store));
161 void vflist_color_set(ViewFile *vf, FileData *fd, gint color_set)
166 if (vflist_find_row(vf, fd, &iter) < 0) return;
167 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
168 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
171 static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
176 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
178 tpath = gtk_tree_model_get_path(store, iter);
179 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
180 gtk_tree_path_free(tpath);
184 static gint vflist_column_idx(ViewFile *vf, gint store_idx)
186 GList *columns, *work;
189 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
193 GtkTreeViewColumn *column = work->data;
194 if (store_idx == GPOINTER_TO_INT(g_object_get_data (G_OBJECT(column), "column_store_idx")))
200 g_list_free(columns);
206 *-----------------------------------------------------------------------------
208 *-----------------------------------------------------------------------------
211 static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
212 GtkSelectionData *selection_data, guint info,
213 guint time, gpointer data)
217 gchar *uri_text = NULL;
220 if (!VFLIST_INFO(vf, click_fd)) return;
222 if (vflist_row_is_selected(vf, VFLIST_INFO(vf, click_fd)))
224 list = vf_selection_get_list(vf);
228 list = g_list_append(NULL, file_data_ref(VFLIST_INFO(vf, click_fd)));
233 uri_text = uri_text_from_filelist(list, &total, (info == TARGET_TEXT_PLAIN));
238 gtk_selection_data_set(selection_data, selection_data->target,
239 8, (guchar *)uri_text, total);
243 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
247 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), TRUE);
249 if (VFLIST_INFO(vf, thumbs_enabled) &&
250 VFLIST_INFO(vf, click_fd) && VFLIST_INFO(vf, click_fd)->pixbuf)
254 if (vflist_row_is_selected(vf, VFLIST_INFO(vf, click_fd)))
255 items = vf_selection_count(vf, NULL);
259 dnd_set_drag_icon(widget, context, VFLIST_INFO(vf, click_fd)->pixbuf, items);
263 static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
267 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
269 if (context->action == GDK_ACTION_MOVE)
275 void vflist_dnd_init(ViewFile *vf)
277 gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
278 dnd_file_drag_types, dnd_file_drag_types_count,
279 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
280 g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
281 G_CALLBACK(vflist_dnd_get), vf);
282 g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
283 G_CALLBACK(vflist_dnd_begin), vf);
284 g_signal_connect(G_OBJECT(vf->listview), "drag_end",
285 G_CALLBACK(vflist_dnd_end), vf);
289 *-----------------------------------------------------------------------------
291 *-----------------------------------------------------------------------------
294 GList *vflist_pop_menu_file_list(ViewFile *vf)
296 if (!VFLIST_INFO(vf, click_fd)) return NULL;
298 if (vflist_row_is_selected(vf, VFLIST_INFO(vf, click_fd)))
300 return vf_selection_get_list(vf);
303 return g_list_append(NULL, file_data_ref(VFLIST_INFO(vf, click_fd)));
306 void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
310 if (vflist_row_is_selected(vf, VFLIST_INFO(vf, click_fd)))
314 list = vf_selection_get_list(vf);
315 view_window_new_from_list(list);
320 view_window_new(VFLIST_INFO(vf, click_fd));
324 void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
329 list = vf_pop_menu_file_list(vf);
330 if (options->file_ops.enable_in_place_rename &&
331 list && !list->next && VFLIST_INFO(vf, click_fd))
338 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
339 if (vflist_find_row(vf, VFLIST_INFO(vf, click_fd), &iter) >= 0)
343 tpath = gtk_tree_model_get_path(store, &iter);
344 tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
345 vflist_column_idx(vf, FILE_COLUMN_NAME), VFLIST_INFO(vf, click_fd)->name,
346 vflist_row_rename_cb, vf);
347 gtk_tree_path_free(tpath);
352 file_util_rename(NULL, list, vf->listview);
355 void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
359 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
362 layout_thumb_set(vf->layout, !VFLIST_INFO(vf, thumbs_enabled));
366 vflist_thumb_set(vf, !VFLIST_INFO(vf, thumbs_enabled));
370 void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
374 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
378 void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
381 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
382 VFLIST_INFO(vf, click_fd) = NULL;
388 *-----------------------------------------------------------------------------
390 *-----------------------------------------------------------------------------
393 static gint vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
399 if (strlen(new) == 0) return FALSE;
401 old_path = g_build_filename(vf->dir_fd->path, old, NULL);
402 new_path = g_build_filename(vf->dir_fd->path, new, NULL);
404 if (strchr(new, G_DIR_SEPARATOR) != NULL)
406 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
407 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
410 else if (isfile(new_path))
412 gchar *text = g_strdup_printf(_("A file with name %s already exists."), new);
413 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
418 gint row = vf_index_by_path(vf, old_path);
421 GList *work = g_list_nth(vf->list, row);
422 FileData *fd = work->data;
424 file_util_rename_simple(fd, new_path, vf->listview);
435 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
443 if (vflist_find_row(vf, VFLIST_INFO(vf, click_fd), &iter) < 0) return;
444 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
445 tpath = gtk_tree_model_get_path(store, &iter);
446 tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
447 gtk_tree_path_free(tpath);
449 popup_menu_position_clamp(menu, x, y, 0);
452 gint vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
457 if (event->keyval != GDK_Menu) return FALSE;
459 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
465 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
466 gtk_tree_model_get_iter(store, &iter, tpath);
467 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST_INFO(vf, click_fd), -1);
468 gtk_tree_path_free(tpath);
472 VFLIST_INFO(vf, click_fd) = NULL;
475 vf->popup = vf_pop_menu(vf);
476 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
481 gint vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
487 GtkTreeViewColumn *column;
489 vf->clicked_mark = 0;
491 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
492 &tpath, &column, NULL, NULL))
495 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
497 if (bevent->button == MOUSE_BUTTON_LEFT &&
498 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
501 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
502 vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
504 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
506 gtk_tree_model_get_iter(store, &iter, tpath);
507 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
509 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE);
511 gtk_tree_path_free(tpath);
514 VFLIST_INFO(vf, click_fd) = fd;
516 if (bevent->button == MOUSE_BUTTON_RIGHT)
518 vf->popup = vf_pop_menu(vf);
519 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
520 bevent->button, bevent->time);
524 if (!fd) return FALSE;
526 if (bevent->button == MOUSE_BUTTON_MIDDLE)
528 if (!vflist_row_is_selected(vf, fd))
530 vflist_color_set(vf, fd, TRUE);
536 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
537 !(bevent->state & GDK_SHIFT_MASK ) &&
538 !(bevent->state & GDK_CONTROL_MASK ) &&
539 vflist_row_is_selected(vf, fd))
541 GtkTreeSelection *selection;
543 gtk_widget_grab_focus(widget);
546 /* returning FALSE and further processing of the event is needed for
547 correct operation of the expander, to show the sidecar files.
548 It however resets the selection of multiple files. With this condition
549 it should work for both cases */
550 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
551 return (gtk_tree_selection_count_selected_rows(selection) > 1);
555 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
557 if (vf->layout) layout_image_full_screen_start(vf->layout);
564 gint vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
571 if (bevent->button == MOUSE_BUTTON_MIDDLE)
573 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
576 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
581 if ((bevent->x != 0 || bevent->y != 0) &&
582 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
583 &tpath, NULL, NULL, NULL))
587 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
588 gtk_tree_model_get_iter(store, &iter, tpath);
589 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
590 gtk_tree_path_free(tpath);
593 if (bevent->button == MOUSE_BUTTON_MIDDLE)
595 if (fd && VFLIST_INFO(vf, click_fd) == fd)
597 GtkTreeSelection *selection;
599 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
600 if (vflist_row_is_selected(vf, fd))
602 gtk_tree_selection_unselect_iter(selection, &iter);
606 gtk_tree_selection_select_iter(selection, &iter);
612 if (fd && VFLIST_INFO(vf, click_fd) == fd &&
613 !(bevent->state & GDK_SHIFT_MASK ) &&
614 !(bevent->state & GDK_CONTROL_MASK ) &&
615 vflist_row_is_selected(vf, fd))
617 GtkTreeSelection *selection;
619 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
620 gtk_tree_selection_unselect_all(selection);
621 gtk_tree_selection_select_iter(selection, &iter);
622 vflist_move_cursor(vf, &iter);
623 // return TRUE;// FIXME - expand
629 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
631 FileData *read_ahead_fd = NULL;
636 cur_fd = layout_image_get_fd(vf->layout);
637 if (sel_fd == cur_fd) return; /* no change */
639 row = g_list_index(vf->list, sel_fd);
640 // FIXME sidecar data
642 if (sel_fd && options->image.enable_read_ahead && row >= 0)
644 if (row > g_list_index(vf->list, cur_fd) &&
645 (guint) (row + 1) < vf_count(vf, NULL))
647 read_ahead_fd = vf_index_get_data(vf, row + 1);
651 read_ahead_fd = vf_index_get_data(vf, row - 1);
655 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
658 static gint vflist_select_idle_cb(gpointer data)
664 VFLIST_INFO(vf, select_idle_id) = -1;
670 if (VFLIST_INFO(vf, select_fd))
672 vflist_select_image(vf, VFLIST_INFO(vf, select_fd));
673 VFLIST_INFO(vf, select_fd) = NULL;
676 VFLIST_INFO(vf, select_idle_id) = -1;
680 static void vflist_select_idle_cancel(ViewFile *vf)
682 if (VFLIST_INFO(vf, select_idle_id) != -1) g_source_remove(VFLIST_INFO(vf, select_idle_id));
683 VFLIST_INFO(vf, select_idle_id) = -1;
686 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
687 gboolean path_currently_selected, gpointer data)
692 if (!path_currently_selected &&
693 gtk_tree_model_get_iter(store, &iter, tpath))
695 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST_INFO(vf, select_fd), -1);
699 VFLIST_INFO(vf, select_fd) = NULL;
703 VFLIST_INFO(vf, select_idle_id) == -1)
705 VFLIST_INFO(vf, select_idle_id) = g_idle_add(vflist_select_idle_cb, vf);
712 *-----------------------------------------------------------------------------
714 *-----------------------------------------------------------------------------
718 static gboolean vflist_dummy_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
719 gboolean path_currently_selected, gpointer data)
725 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
729 gchar *sidecars = NULL;
731 if (fd->sidecar_files)
732 sidecars = file_data_sc_list_to_string(fd);
733 size = text_from_size(fd->size);
735 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
736 FILE_COLUMN_VERSION, fd->version,
737 FILE_COLUMN_THUMB, fd->pixbuf,
738 FILE_COLUMN_NAME, fd->name,
739 FILE_COLUMN_SIDECARS, sidecars,
740 FILE_COLUMN_SIZE, size,
741 FILE_COLUMN_DATE, text_from_time(fd->date),
742 FILE_COLUMN_COLOR, FALSE, -1);
743 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
744 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
751 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected)
757 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
763 FileData *fd = work->data;
768 FileData *old_fd = NULL;
769 gint old_version = 0;
773 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
774 FILE_COLUMN_POINTER, &old_fd,
775 FILE_COLUMN_VERSION, &old_version,
785 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
787 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
789 if (match == 0) g_warning("multiple fd for the same path");
804 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
808 gtk_tree_store_append(store, &new, parent_iter);
811 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
812 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected);
814 if (g_list_find(selected, fd))
816 /* renamed files - the same fd appears at different position - select it again*/
817 GtkTreeSelection *selection;
818 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
819 gtk_tree_selection_select_iter(selection, &new);
826 file_data_unref(old_fd);
827 valid = gtk_tree_store_remove(store, &iter);
831 if (fd->version != old_version)
833 vflist_setup_iter(vf, store, &iter, fd);
834 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected);
837 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
848 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
849 file_data_unref(old_fd);
851 valid = gtk_tree_store_remove(store, &iter);
855 void vflist_sort_set(ViewFile *vf, SortType type, gint ascend)
858 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
863 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
864 if (!vf->list) return;
870 FileData *fd = work->data;
871 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
876 vf->sort_method = type;
877 vf->sort_ascend = ascend;
879 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
881 new_order = g_malloc(i * sizeof(gint));
887 FileData *fd = work->data;
888 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
893 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
894 gtk_tree_store_reorder(store, NULL, new_order);
897 g_hash_table_destroy(fd_idx_hash);
901 *-----------------------------------------------------------------------------
903 *-----------------------------------------------------------------------------
906 static gint vflist_thumb_next(ViewFile *vf);
908 static void vflist_thumb_status(ViewFile *vf, gdouble val, const gchar *text)
910 if (vf->func_thumb_status)
912 vf->func_thumb_status(vf, val, text, vf->data_thumb_status);
916 static void vflist_thumb_cleanup(ViewFile *vf)
918 vflist_thumb_status(vf, 0.0, NULL);
920 vf->thumbs_count = 0;
921 vf->thumbs_running = FALSE;
923 thumb_loader_free(vf->thumbs_loader);
924 vf->thumbs_loader = NULL;
926 vf->thumbs_filedata = NULL;
929 static void vflist_thumb_stop(ViewFile *vf)
931 if (vf->thumbs_running) vflist_thumb_cleanup(vf);
934 static void vflist_thumb_do(ViewFile *vf, ThumbLoader *tl, FileData *fd)
939 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
941 if (fd->pixbuf) g_object_unref(fd->pixbuf);
942 fd->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
944 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
945 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->pixbuf, -1);
947 vflist_thumb_status(vf, (gdouble)(vf->thumbs_count) / vflist_sidecar_list_count(vf->list), _("Loading thumbs..."));
950 static void vflist_thumb_error_cb(ThumbLoader *tl, gpointer data)
954 if (vf->thumbs_filedata && vf->thumbs_loader == tl)
956 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
959 while (vflist_thumb_next(vf));
962 static void vflist_thumb_done_cb(ThumbLoader *tl, gpointer data)
966 if (vf->thumbs_filedata && vf->thumbs_loader == tl)
968 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
971 while (vflist_thumb_next(vf));
974 static gint vflist_thumb_next(ViewFile *vf)
979 /* first check the visible files */
981 if (GTK_WIDGET_REALIZED(vf->listview) &&
982 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
988 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
989 gtk_tree_model_get_iter(store, &iter, tpath);
990 gtk_tree_path_free(tpath);
992 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
994 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
995 if (fd->pixbuf) fd = NULL;
997 valid = gtk_tree_model_iter_next(store, &iter);
1001 /* then find first undone */
1005 GList *work = vf->list;
1008 FileData *fd_p = work->data;
1013 GList *work2 = fd_p->sidecar_files;
1015 while (work2 && !fd)
1018 if (!fd_p->pixbuf) fd = fd_p;
1019 work2 = work2->next;
1029 vflist_thumb_cleanup(vf);
1035 vf->thumbs_filedata = fd;
1037 thumb_loader_free(vf->thumbs_loader);
1039 vf->thumbs_loader = thumb_loader_new(options->thumbnails.max_width, options->thumbnails.max_height);
1040 thumb_loader_set_callbacks(vf->thumbs_loader,
1041 vflist_thumb_done_cb,
1042 vflist_thumb_error_cb,
1046 if (!thumb_loader_start(vf->thumbs_loader, fd->path))
1048 /* set icon to unknown, continue */
1049 DEBUG_1("thumb loader start failed %s", vf->thumbs_loader->path);
1050 vflist_thumb_do(vf, vf->thumbs_loader, fd);
1058 static void vflist_thumb_update(ViewFile *vf)
1060 vflist_thumb_stop(vf);
1061 if (!VFLIST_INFO(vf, thumbs_enabled)) return;
1063 vflist_thumb_status(vf, 0.0, _("Loading thumbs..."));
1064 vf->thumbs_running = TRUE;
1066 while (vflist_thumb_next(vf));
1070 *-----------------------------------------------------------------------------
1072 *-----------------------------------------------------------------------------
1075 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1077 return g_list_nth_data(vf->list, row);
1080 gint vflist_index_by_path(ViewFile *vf, const gchar *path)
1085 if (!path) return -1;
1090 FileData *fd = work->data;
1091 if (strcmp(path, fd->path) == 0) return p;
1100 guint vflist_count(ViewFile *vf, gint64 *bytes)
1110 FileData *fd = work->data;
1118 return g_list_length(vf->list);
1121 GList *vflist_get_list(ViewFile *vf)
1129 FileData *fd = work->data;
1132 list = g_list_prepend(list, file_data_ref(fd));
1135 return g_list_reverse(list);
1139 *-----------------------------------------------------------------------------
1141 *-----------------------------------------------------------------------------
1144 static gint vflist_row_is_selected(ViewFile *vf, FileData *fd)
1146 GtkTreeModel *store;
1147 GtkTreeSelection *selection;
1152 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1153 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1155 while (!found && work)
1157 GtkTreePath *tpath = work->data;
1161 gtk_tree_model_get_iter(store, &iter, tpath);
1162 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1163 if (fd_n == fd) found = TRUE;
1166 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1172 gint vflist_index_is_selected(ViewFile *vf, gint row)
1176 fd = vf_index_get_data(vf, row);
1177 return vflist_row_is_selected(vf, fd);
1180 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1182 GtkTreeModel *store;
1183 GtkTreeSelection *selection;
1187 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1188 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1198 GtkTreePath *tpath = work->data;
1202 gtk_tree_model_get_iter(store, &iter, tpath);
1203 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1212 count = g_list_length(slist);
1213 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1219 GList *vflist_selection_get_list(ViewFile *vf)
1221 GtkTreeModel *store;
1222 GtkTreeSelection *selection;
1227 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1228 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1232 GtkTreePath *tpath = work->data;
1236 gtk_tree_model_get_iter(store, &iter, tpath);
1237 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1239 list = g_list_prepend(list, file_data_ref(fd));
1243 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1246 return g_list_reverse(list);
1249 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1251 GtkTreeModel *store;
1252 GtkTreeSelection *selection;
1257 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1258 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1262 GtkTreePath *tpath = work->data;
1266 gtk_tree_model_get_iter(store, &iter, tpath);
1267 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1269 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1273 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1276 return g_list_reverse(list);
1279 void vflist_select_all(ViewFile *vf)
1281 GtkTreeSelection *selection;
1283 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1284 gtk_tree_selection_select_all(selection);
1286 VFLIST_INFO(vf, select_fd) = NULL;
1289 void vflist_select_none(ViewFile *vf)
1291 GtkTreeSelection *selection;
1293 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1294 gtk_tree_selection_unselect_all(selection);
1297 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1302 tpath = gtk_tree_model_get_path(store, iter);
1303 result = gtk_tree_path_prev(tpath);
1305 gtk_tree_model_get_iter(store, iter, tpath);
1307 gtk_tree_path_free(tpath);
1312 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1314 if (!gtk_tree_model_get_iter_first(store, iter))
1319 GtkTreeIter next = *iter;
1321 if (gtk_tree_model_iter_next(store, &next))
1330 void vflist_select_invert(ViewFile *vf)
1333 GtkTreeSelection *selection;
1334 GtkTreeModel *store;
1337 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1338 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1340 /* Backward iteration prevents scrolling to the end of the list,
1341 * it scrolls to the first selected row instead. */
1342 valid = tree_model_get_iter_last(store, &iter);
1346 gint selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1349 gtk_tree_selection_unselect_iter(selection, &iter);
1351 gtk_tree_selection_select_iter(selection, &iter);
1353 valid = tree_model_iter_prev(store, &iter);
1357 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1361 if (vflist_find_row(vf, fd, &iter) < 0) return;
1363 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1365 if (!vflist_row_is_selected(vf, fd))
1367 GtkTreeSelection *selection;
1368 GtkTreeModel *store;
1371 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1372 gtk_tree_selection_unselect_all(selection);
1373 gtk_tree_selection_select_iter(selection, &iter);
1374 vflist_move_cursor(vf, &iter);
1376 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1377 tpath = gtk_tree_model_get_path(store, &iter);
1378 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1379 gtk_tree_path_free(tpath);
1383 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1387 if (sel_fd->parent) sel_fd = sel_fd->parent;
1393 FileData *fd = work->data;
1397 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1401 vflist_select_by_fd(vf, fd);
1408 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1410 GtkTreeModel *store;
1412 GtkTreeSelection *selection;
1416 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1418 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1419 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1421 valid = gtk_tree_model_get_iter_first(store, &iter);
1425 gboolean mark_val, selected;
1426 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1428 mark_val = file_data_get_mark(fd, n);
1429 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1433 case MTS_MODE_SET: selected = mark_val;
1435 case MTS_MODE_OR: selected = mark_val | selected;
1437 case MTS_MODE_AND: selected = mark_val & selected;
1439 case MTS_MODE_MINUS: selected = !mark_val & selected;
1444 gtk_tree_selection_select_iter(selection, &iter);
1446 gtk_tree_selection_unselect_iter(selection, &iter);
1448 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1452 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1454 GtkTreeModel *store;
1455 GtkTreeSelection *selection;
1460 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1462 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1463 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1467 GtkTreePath *tpath = work->data;
1471 gtk_tree_model_get_iter(store, &iter, tpath);
1472 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1474 file_data_unregister_notify_func(vflist_notify_cb, vf); /* we don't need the notification */
1478 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1480 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1482 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1486 file_data_register_notify_func(vflist_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1488 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_MARKS + n, file_data_get_mark(fd, n), -1);
1492 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1497 *-----------------------------------------------------------------------------
1499 *-----------------------------------------------------------------------------
1502 static void vflist_listview_set_height(GtkWidget *listview, gint thumb)
1504 GtkTreeViewColumn *column;
1505 GtkCellRenderer *cell;
1508 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), 0); /* first column is thumbnail */
1509 if (!column) return;
1511 gtk_tree_view_column_set_visible(column, thumb);
1513 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 40);
1515 list = gtk_tree_view_column_get_cell_renderers(column);
1520 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1521 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1524 static void vflist_populate_view(ViewFile *vf)
1526 GtkTreeStore *store;
1528 GtkTreeRowReference *visible_row = NULL;
1532 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1533 thumbs = VFLIST_INFO(vf, thumbs_enabled);
1535 vflist_thumb_stop(vf);
1539 vflist_store_clear(vf);
1544 if (GTK_WIDGET_REALIZED(vf->listview) &&
1545 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1547 visible_row = gtk_tree_row_reference_new(GTK_TREE_MODEL(store), tpath);
1548 gtk_tree_path_free(tpath);
1551 vflist_listview_set_height(vf->listview, thumbs);
1553 selected = vflist_selection_get_list(vf);
1555 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected);
1557 if (selected && vflist_selection_count(vf, NULL) == 0)
1559 /* all selected files disappeared */
1560 vflist_select_closest(vf, selected->data);
1563 filelist_free(selected);
1567 if (gtk_tree_row_reference_valid(visible_row))
1569 tpath = gtk_tree_row_reference_get_path(visible_row);
1570 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(vf->listview), tpath, NULL, TRUE, 0.0, 0.0);
1571 gtk_tree_path_free(tpath);
1573 gtk_tree_row_reference_free(visible_row);
1577 vflist_thumb_update(vf);
1580 gint vflist_refresh(ViewFile *vf)
1585 old_list = vf->list;
1588 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1591 file_data_unregister_notify_func(vflist_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1593 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1595 file_data_register_notify_func(vflist_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1597 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1598 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1601 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1603 vflist_populate_view(vf);
1605 filelist_free(old_list);
1606 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1611 static gint vflist_refresh_idle_cb(gpointer data)
1613 ViewFile *vf = data;
1616 vf->refresh_idle_id = -1;
1620 static void vflist_refresh_idle_cancel(ViewFile *vf)
1622 if (vf->refresh_idle_id != -1) g_source_remove(vf->refresh_idle_id);
1623 vf->refresh_idle_id = -1;
1627 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1629 #define CELL_HEIGHT_OVERRIDE 512
1631 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1635 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1636 if (spec && G_IS_PARAM_SPEC_INT(spec))
1638 GParamSpecInt *spec_int;
1640 spec_int = G_PARAM_SPEC_INT(spec);
1641 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1645 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1647 static GdkColor color;
1648 static GtkWidget *done = NULL;
1654 style = gtk_widget_get_style(widget);
1655 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1656 shift_color(&color, -1, 0);
1663 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1664 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1666 ViewFile *vf = data;
1669 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1670 g_object_set(G_OBJECT(cell),
1671 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1672 "cell-background-set", set, NULL);
1675 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gint image, gint right_justify, gint expand)
1677 GtkTreeViewColumn *column;
1678 GtkCellRenderer *renderer;
1680 column = gtk_tree_view_column_new();
1681 gtk_tree_view_column_set_title(column, title);
1682 gtk_tree_view_column_set_min_width(column, 4);
1686 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1687 renderer = gtk_cell_renderer_text_new();
1690 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1692 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1693 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1695 gtk_tree_view_column_set_expand(column, TRUE);
1699 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1700 renderer = gtk_cell_renderer_pixbuf_new();
1701 cell_renderer_height_override(renderer);
1702 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1703 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1706 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1707 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1708 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1710 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1713 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1715 ViewFile *vf = data;
1716 GtkTreeStore *store;
1717 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1723 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1724 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1727 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1729 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1731 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &mark, -1);
1733 file_data_unregister_notify_func(vflist_notify_cb, vf); /* we don't need the notification */
1734 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, mark);
1735 file_data_register_notify_func(vflist_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1737 gtk_tree_store_set(store, &iter, col_idx, mark, -1);
1738 gtk_tree_path_free(path);
1741 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1743 GtkTreeViewColumn *column;
1744 GtkCellRenderer *renderer;
1745 GtkTreeStore *store;
1748 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1750 renderer = gtk_cell_renderer_toggle_new();
1751 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1753 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1754 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1755 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1757 index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1758 gtk_tree_view_column_set_fixed_width(column, 16);
1759 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1762 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1766 *-----------------------------------------------------------------------------
1768 *-----------------------------------------------------------------------------
1771 gint vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1773 if (!dir_fd) return FALSE;
1774 if (vf->dir_fd == dir_fd) return TRUE;
1776 file_data_unref(vf->dir_fd);
1777 vf->dir_fd = file_data_ref(dir_fd);
1779 /* force complete reload */
1780 vflist_store_clear(vf);
1782 filelist_free(vf->list);
1785 return vf_refresh(vf);
1788 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1790 ViewFile *vf = data;
1792 file_data_unregister_notify_func(vflist_notify_cb, vf);
1794 vflist_select_idle_cancel(vf);
1795 vflist_refresh_idle_cancel(vf);
1796 vflist_thumb_stop(vf);
1798 filelist_free(vf->list);
1801 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1803 GtkTreeStore *store;
1804 GtkTreeSelection *selection;
1806 GType flist_types[FILE_COLUMN_COUNT];
1809 vf->info = g_new0(ViewFileInfoList, 1);
1811 VFLIST_INFO(vf, click_fd) = NULL;
1812 VFLIST_INFO(vf, select_fd) = NULL;
1813 VFLIST_INFO(vf, thumbs_enabled) = FALSE;
1815 VFLIST_INFO(vf, select_idle_id) = -1;
1816 vf->refresh_idle_id = -1;
1819 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1820 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1821 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1822 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1823 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
1824 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1825 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1826 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1827 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1828 flist_types[i] = G_TYPE_BOOLEAN;
1830 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
1832 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1833 g_object_unref(store);
1835 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1836 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1837 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
1839 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
1840 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
1842 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
1844 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
1845 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
1847 vflist_listview_add_column(vf, FILE_COLUMN_NAME, _("Name"), FALSE, FALSE, FALSE);
1848 vflist_listview_add_column(vf, FILE_COLUMN_SIDECARS, _("SC"), FALSE, FALSE, FALSE);
1850 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
1851 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
1853 file_data_register_notify_func(vflist_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1857 void vflist_thumb_set(ViewFile *vf, gint enable)
1859 if (VFLIST_INFO(vf, thumbs_enabled) == enable) return;
1861 VFLIST_INFO(vf, thumbs_enabled) = enable;
1862 if (vf->layout) vf_refresh(vf);
1865 void vflist_marks_set(ViewFile *vf, gint enable)
1867 GList *columns, *work;
1869 if (vf->marks_enabled == enable) return;
1871 vf->marks_enabled = enable;
1873 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
1878 GtkTreeViewColumn *column = work->data;
1879 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
1882 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
1883 gtk_tree_view_column_set_visible(column, enable);
1886 g_list_free(columns);
1891 *-----------------------------------------------------------------------------
1892 * maintenance (for rename, move, remove)
1893 *-----------------------------------------------------------------------------
1896 static void vflist_notify_cb(FileData *fd, NotifyType type, gpointer data)
1898 ViewFile *vf = data;
1901 if (vf->refresh_idle_id != -1) return;
1903 refresh = (fd == vf->dir_fd);
1907 gchar *base = remove_level_from_path(fd->path);
1908 refresh = (strcmp(base, vf->dir_fd->path) == 0);
1912 if (!refresh && fd->change && fd->change->dest)
1914 gchar *dest_base = remove_level_from_path(fd->change->dest);
1915 refresh = (strcmp(dest_base, vf->dir_fd->path) == 0);
1919 if (!refresh && fd->change && fd->change->source)
1921 gchar *source_base = remove_level_from_path(fd->change->source);
1922 refresh = (strcmp(source_base, vf->dir_fd->path) == 0);
1923 g_free(source_base);
1928 vf->refresh_idle_id = g_idle_add(vflist_refresh_idle_cb, vf);
1934 gint vflist_maint_renamed(ViewFile *vf, FileData *fd)
1936 vflist_maint(vf, fd);
1939 gint vflist_maint_removed(ViewFile *vf, FileData *fd, GList *ignore_list)
1941 vflist_maint(vf, fd);
1944 gint vflist_maint_moved(ViewFile *vf, FileData *fd, GList *ignore_list)
1946 vflist_maint(vf, fd);
1951 static gint vflist_maint_find_closest(ViewFile *vf, gint row, gint count, GList *ignore_list)
1961 gint f = vf_index_by_path(vf, work->data);
1962 if (f >= 0) list = g_list_prepend(list, GINT_TO_POINTER(f));
1972 gpointer p = work->data;
1974 if (row == GPOINTER_TO_INT(p))
1979 if (rev == GPOINTER_TO_INT(p))
1984 if (!c) list = g_list_remove(list, p);
1992 if (row > count - 1)
2005 gint vflist_maint_renamed(ViewFile *vf, FileData *fd)
2011 DEBUG_1("%s vflist_maint_renamed: start", get_exec_time());
2013 if (g_list_index(vf->list, fd) < 0) return FALSE;
2015 source_base = remove_level_from_path(fd->change->source);
2016 dest_base = remove_level_from_path(fd->change->dest);
2019 if (strcmp(source_base, dest_base) == 0)
2021 GtkTreeStore *store;
2023 GtkTreeIter position;
2027 old_row = g_list_index(vf->list, fd);
2029 vf->list = g_list_remove(vf->list, fd);
2031 vf->list = filelist_insert_sort(vf->list, fd, vf->sort_method, vf->sort_ascend);
2032 n = g_list_index(vf->list, fd);
2034 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2035 if (vflist_find_row(vf, fd, &iter) >= 0 &&
2036 gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &position, NULL, n))
2040 gtk_tree_store_move_before(store, &iter, &position);
2044 gtk_tree_store_move_after(store, &iter, &position);
2047 gtk_tree_store_set(store, &iter, FILE_COLUMN_NAME, fd->name, -1);
2053 ret = vflist_maint_removed(vf, fd, NULL);
2056 g_free(source_base);
2059 DEBUG_1("%s vflist_maint_renamed: done", get_exec_time());
2064 gint vflist_maint_removed(ViewFile *vf, FileData *fd, GList *ignore_list)
2071 DEBUG_1("%s vflist_maint_removed: start", get_exec_time());
2073 row = g_list_index(vf->list, fd);
2074 if (row < 0) return FALSE;
2076 if (vflist_index_is_selected(vf, row) &&
2077 layout_image_get_collection(vf->layout, NULL) == NULL)
2081 n = vf_count(vf, NULL);
2084 new_row = vflist_maint_find_closest(vf, row, n, ignore_list);
2085 DEBUG_1("row = %d, closest is %d", row, new_row);
2101 fd = vf_index_get_data(vf, new_row);
2102 if (vflist_find_row(vf, fd, &iter) >= 0)
2104 GtkTreeSelection *selection;
2106 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2107 gtk_tree_selection_select_iter(selection, &iter);
2108 vflist_move_cursor(vf, &iter);
2113 fd = vf_index_get_data(vf, row);
2114 if (vflist_find_row(vf, fd, &iter) >= 0)
2116 GtkTreeStore *store;
2117 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2118 gtk_tree_store_remove(store, &iter);
2120 list = g_list_nth(vf->list, row);
2123 /* thumbnail loader check */
2124 if (fd == vf->thumbs_filedata) vf->thumbs_filedata = NULL;
2125 if (vf->thumbs_count > 0) vf->thumbs_count--;
2127 vf->list = g_list_remove(vf->list, fd);
2128 file_data_unref(fd);
2132 DEBUG_1("%s vflist_maint_removed: done", get_exec_time());
2137 gint vflist_maint_moved(ViewFile *vf, FileData *fd, GList *ignore_list)
2142 if (!fd->change->source || !vf->path) return FALSE;
2144 buf = remove_level_from_path(fd->change->source);
2146 if (strcmp(buf, vf->path) == 0)
2148 ret = vflist_maint_removed(vf, fd, ignore_list);