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, 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;
146 void vflist_color_set(ViewFile *vf, FileData *fd, gint color_set)
151 if (vflist_find_row(vf, fd, &iter) < 0) return;
152 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
153 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
156 static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
161 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
163 tpath = gtk_tree_model_get_path(store, iter);
164 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
165 gtk_tree_path_free(tpath);
169 static gint vflist_column_idx(ViewFile *vf, gint store_idx)
171 GList *columns, *work;
174 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
178 GtkTreeViewColumn *column = work->data;
179 if (store_idx == GPOINTER_TO_INT(g_object_get_data (G_OBJECT(column), "column_store_idx")))
185 g_list_free(columns);
191 *-----------------------------------------------------------------------------
193 *-----------------------------------------------------------------------------
196 static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
197 GtkSelectionData *selection_data, guint info,
198 guint time, gpointer data)
202 gchar *uri_text = NULL;
205 if (!VFLIST_INFO(vf, click_fd)) return;
207 if (vflist_row_is_selected(vf, VFLIST_INFO(vf, click_fd)))
209 list = vf_selection_get_list(vf);
213 list = g_list_append(NULL, file_data_ref(VFLIST_INFO(vf, click_fd)));
218 uri_text = uri_text_from_filelist(list, &total, (info == TARGET_TEXT_PLAIN));
223 gtk_selection_data_set(selection_data, selection_data->target,
224 8, (guchar *)uri_text, total);
228 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
232 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), TRUE);
234 if (VFLIST_INFO(vf, thumbs_enabled) &&
235 VFLIST_INFO(vf, click_fd) && VFLIST_INFO(vf, click_fd)->pixbuf)
239 if (vflist_row_is_selected(vf, VFLIST_INFO(vf, click_fd)))
240 items = vf_selection_count(vf, NULL);
244 dnd_set_drag_icon(widget, context, VFLIST_INFO(vf, click_fd)->pixbuf, items);
248 static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
252 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
254 if (context->action == GDK_ACTION_MOVE)
260 void vflist_dnd_init(ViewFile *vf)
262 gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
263 dnd_file_drag_types, dnd_file_drag_types_count,
264 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
265 g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
266 G_CALLBACK(vflist_dnd_get), vf);
267 g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
268 G_CALLBACK(vflist_dnd_begin), vf);
269 g_signal_connect(G_OBJECT(vf->listview), "drag_end",
270 G_CALLBACK(vflist_dnd_end), vf);
274 *-----------------------------------------------------------------------------
276 *-----------------------------------------------------------------------------
279 GList *vflist_pop_menu_file_list(ViewFile *vf)
281 if (!VFLIST_INFO(vf, click_fd)) return NULL;
283 if (vflist_row_is_selected(vf, VFLIST_INFO(vf, click_fd)))
285 return vf_selection_get_list(vf);
288 return g_list_append(NULL, file_data_ref(VFLIST_INFO(vf, click_fd)));
291 void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
295 if (vflist_row_is_selected(vf, VFLIST_INFO(vf, click_fd)))
299 list = vf_selection_get_list(vf);
300 view_window_new_from_list(list);
305 view_window_new(VFLIST_INFO(vf, click_fd));
309 void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
314 list = vf_pop_menu_file_list(vf);
315 if (options->file_ops.enable_in_place_rename &&
316 list && !list->next && VFLIST_INFO(vf, click_fd))
323 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
324 if (vflist_find_row(vf, VFLIST_INFO(vf, click_fd), &iter) >= 0)
328 tpath = gtk_tree_model_get_path(store, &iter);
329 tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
330 vflist_column_idx(vf, FILE_COLUMN_NAME), VFLIST_INFO(vf, click_fd)->name,
331 vflist_row_rename_cb, vf);
332 gtk_tree_path_free(tpath);
337 file_util_rename(NULL, list, vf->listview);
340 void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
344 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
347 layout_thumb_set(vf->layout, !VFLIST_INFO(vf, thumbs_enabled));
351 vflist_thumb_set(vf, !VFLIST_INFO(vf, thumbs_enabled));
355 void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
359 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
363 void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
366 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
367 VFLIST_INFO(vf, click_fd) = NULL;
373 *-----------------------------------------------------------------------------
375 *-----------------------------------------------------------------------------
378 static gint vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
384 if (strlen(new) == 0) return FALSE;
386 old_path = g_build_filename(vf->dir_fd->path, old, NULL);
387 new_path = g_build_filename(vf->dir_fd->path, new, NULL);
389 if (strchr(new, G_DIR_SEPARATOR) != NULL)
391 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
392 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
395 else if (isfile(new_path))
397 gchar *text = g_strdup_printf(_("A file with name %s already exists."), new);
398 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
403 gint row = vf_index_by_path(vf, old_path);
406 GList *work = g_list_nth(vf->list, row);
407 FileData *fd = work->data;
409 file_util_rename_simple(fd, new_path, vf->listview);
420 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
428 if (vflist_find_row(vf, VFLIST_INFO(vf, click_fd), &iter) < 0) return;
429 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
430 tpath = gtk_tree_model_get_path(store, &iter);
431 tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
432 gtk_tree_path_free(tpath);
434 popup_menu_position_clamp(menu, x, y, 0);
437 gint vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
442 if (event->keyval != GDK_Menu) return FALSE;
444 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
450 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
451 gtk_tree_model_get_iter(store, &iter, tpath);
452 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST_INFO(vf, click_fd), -1);
453 gtk_tree_path_free(tpath);
457 VFLIST_INFO(vf, click_fd) = NULL;
460 vf->popup = vf_pop_menu(vf);
461 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
466 gint vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
472 GtkTreeViewColumn *column;
474 vf->clicked_mark = 0;
476 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
477 &tpath, &column, NULL, NULL))
480 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
482 if (bevent->button == MOUSE_BUTTON_LEFT &&
483 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
486 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
487 vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
489 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
491 gtk_tree_model_get_iter(store, &iter, tpath);
492 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
494 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE);
496 gtk_tree_path_free(tpath);
499 VFLIST_INFO(vf, click_fd) = fd;
501 if (bevent->button == MOUSE_BUTTON_RIGHT)
503 vf->popup = vf_pop_menu(vf);
504 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
505 bevent->button, bevent->time);
509 if (!fd) return FALSE;
511 if (bevent->button == MOUSE_BUTTON_MIDDLE)
513 if (!vflist_row_is_selected(vf, fd))
515 vflist_color_set(vf, fd, TRUE);
521 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
522 !(bevent->state & GDK_SHIFT_MASK ) &&
523 !(bevent->state & GDK_CONTROL_MASK ) &&
524 vflist_row_is_selected(vf, fd))
526 GtkTreeSelection *selection;
528 gtk_widget_grab_focus(widget);
531 /* returning FALSE and further processing of the event is needed for
532 correct operation of the expander, to show the sidecar files.
533 It however resets the selection of multiple files. With this condition
534 it should work for both cases */
535 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
536 return (gtk_tree_selection_count_selected_rows(selection) > 1);
540 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
542 if (vf->layout) layout_image_full_screen_start(vf->layout);
549 gint vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
556 if (bevent->button == MOUSE_BUTTON_MIDDLE)
558 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
561 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
566 if ((bevent->x != 0 || bevent->y != 0) &&
567 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
568 &tpath, NULL, NULL, NULL))
572 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
573 gtk_tree_model_get_iter(store, &iter, tpath);
574 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
575 gtk_tree_path_free(tpath);
578 if (bevent->button == MOUSE_BUTTON_MIDDLE)
580 if (fd && VFLIST_INFO(vf, click_fd) == fd)
582 GtkTreeSelection *selection;
584 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
585 if (vflist_row_is_selected(vf, fd))
587 gtk_tree_selection_unselect_iter(selection, &iter);
591 gtk_tree_selection_select_iter(selection, &iter);
597 if (fd && VFLIST_INFO(vf, click_fd) == fd &&
598 !(bevent->state & GDK_SHIFT_MASK ) &&
599 !(bevent->state & GDK_CONTROL_MASK ) &&
600 vflist_row_is_selected(vf, fd))
602 GtkTreeSelection *selection;
604 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
605 gtk_tree_selection_unselect_all(selection);
606 gtk_tree_selection_select_iter(selection, &iter);
607 vflist_move_cursor(vf, &iter);
608 // return TRUE;// FIXME - expand
614 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
616 FileData *read_ahead_fd = NULL;
621 cur_fd = layout_image_get_fd(vf->layout);
622 if (sel_fd == cur_fd) return; /* no change */
624 row = g_list_index(vf->list, sel_fd);
625 // FIXME sidecar data
627 if (sel_fd && options->image.enable_read_ahead && row >= 0)
629 if (row > g_list_index(vf->list, cur_fd) &&
630 (guint) (row + 1) < vf_count(vf, NULL))
632 read_ahead_fd = vf_index_get_data(vf, row + 1);
636 read_ahead_fd = vf_index_get_data(vf, row - 1);
640 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
643 static gint vflist_select_idle_cb(gpointer data)
649 VFLIST_INFO(vf, select_idle_id) = -1;
655 if (VFLIST_INFO(vf, select_fd))
657 vflist_select_image(vf, VFLIST_INFO(vf, select_fd));
658 VFLIST_INFO(vf, select_fd) = NULL;
661 VFLIST_INFO(vf, select_idle_id) = -1;
665 static void vflist_select_idle_cancel(ViewFile *vf)
667 if (VFLIST_INFO(vf, select_idle_id) != -1) g_source_remove(VFLIST_INFO(vf, select_idle_id));
668 VFLIST_INFO(vf, select_idle_id) = -1;
671 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
672 gboolean path_currently_selected, gpointer data)
677 if (!path_currently_selected &&
678 gtk_tree_model_get_iter(store, &iter, tpath))
680 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST_INFO(vf, select_fd), -1);
684 VFLIST_INFO(vf, select_fd) = NULL;
688 VFLIST_INFO(vf, select_idle_id) == -1)
690 VFLIST_INFO(vf, select_idle_id) = g_idle_add(vflist_select_idle_cb, vf);
697 *-----------------------------------------------------------------------------
699 *-----------------------------------------------------------------------------
703 static gboolean vflist_dummy_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
704 gboolean path_currently_selected, gpointer data)
710 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
714 gchar *sidecars = NULL;
716 if (fd->sidecar_files)
717 sidecars = file_data_sc_list_to_string(fd);
718 size = text_from_size(fd->size);
720 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
721 FILE_COLUMN_VERSION, fd->version,
722 FILE_COLUMN_THUMB, (VFLIST_INFO(vf, thumbs_enabled)) ? fd->pixbuf : NULL,
723 FILE_COLUMN_NAME, fd->name,
724 FILE_COLUMN_SIDECARS, sidecars,
725 FILE_COLUMN_SIZE, size,
726 FILE_COLUMN_DATE, text_from_time(fd->date),
727 FILE_COLUMN_COLOR, FALSE, -1);
728 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
729 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, fd->marks[i], -1);
736 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected)
742 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
748 FileData *fd = work->data;
753 FileData *old_fd = NULL;
754 gint old_version = 0;
758 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
759 FILE_COLUMN_POINTER, &old_fd,
760 FILE_COLUMN_VERSION, &old_version,
770 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
772 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
774 if (match == 0) g_warning("multiple fd for the same path");
789 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
793 gtk_tree_store_append(store, &new, parent_iter);
796 vflist_setup_iter(vf, store, &new, fd);
797 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected);
799 if (g_list_find(selected, fd))
801 /* renamed files - the same fd appears at different position - select it again*/
802 GtkTreeSelection *selection;
803 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
804 gtk_tree_selection_select_iter(selection, &new);
811 valid = gtk_tree_store_remove(store, &iter);
815 if (fd->version != old_version)
817 vflist_setup_iter(vf, store, &iter, fd);
818 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected);
821 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
831 valid = gtk_tree_store_remove(store, &iter);
835 void vflist_sort_set(ViewFile *vf, SortType type, gint ascend)
838 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
843 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
844 if (!vf->list) return;
850 FileData *fd = work->data;
851 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
856 vf->sort_method = type;
857 vf->sort_ascend = ascend;
859 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
861 new_order = g_malloc(i * sizeof(gint));
867 FileData *fd = work->data;
868 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
873 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
874 gtk_tree_store_reorder(store, NULL, new_order);
877 g_hash_table_destroy(fd_idx_hash);
881 *-----------------------------------------------------------------------------
883 *-----------------------------------------------------------------------------
886 static gint vflist_thumb_next(ViewFile *vf);
888 static void vflist_thumb_status(ViewFile *vf, gdouble val, const gchar *text)
890 if (vf->func_thumb_status)
892 vf->func_thumb_status(vf, val, text, vf->data_thumb_status);
896 static void vflist_thumb_cleanup(ViewFile *vf)
898 vflist_thumb_status(vf, 0.0, NULL);
900 vf->thumbs_count = 0;
901 vf->thumbs_running = FALSE;
903 thumb_loader_free(vf->thumbs_loader);
904 vf->thumbs_loader = NULL;
906 vf->thumbs_filedata = NULL;
909 static void vflist_thumb_stop(ViewFile *vf)
911 if (vf->thumbs_running) vflist_thumb_cleanup(vf);
914 static void vflist_thumb_do(ViewFile *vf, ThumbLoader *tl, FileData *fd)
919 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
921 if (fd->pixbuf) g_object_unref(fd->pixbuf);
922 fd->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
924 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
925 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->pixbuf, -1);
927 vflist_thumb_status(vf, (gdouble)(vf->thumbs_count) / vflist_sidecar_list_count(vf->list), _("Loading thumbs..."));
930 static void vflist_thumb_error_cb(ThumbLoader *tl, gpointer data)
934 if (vf->thumbs_filedata && vf->thumbs_loader == tl)
936 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
939 while (vflist_thumb_next(vf));
942 static void vflist_thumb_done_cb(ThumbLoader *tl, gpointer data)
946 if (vf->thumbs_filedata && vf->thumbs_loader == tl)
948 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
951 while (vflist_thumb_next(vf));
954 static gint vflist_thumb_next(ViewFile *vf)
959 /* first check the visible files */
961 if (GTK_WIDGET_REALIZED(vf->listview) &&
962 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
968 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
969 gtk_tree_model_get_iter(store, &iter, tpath);
970 gtk_tree_path_free(tpath);
972 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
974 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
975 if (fd->pixbuf) fd = NULL;
977 valid = gtk_tree_model_iter_next(store, &iter);
981 /* then find first undone */
985 GList *work = vf->list;
988 FileData *fd_p = work->data;
993 GList *work2 = fd_p->sidecar_files;
998 if (!fd_p->pixbuf) fd = fd_p;
1009 vflist_thumb_cleanup(vf);
1015 vf->thumbs_filedata = fd;
1017 thumb_loader_free(vf->thumbs_loader);
1019 vf->thumbs_loader = thumb_loader_new(options->thumbnails.max_width, options->thumbnails.max_height);
1020 thumb_loader_set_callbacks(vf->thumbs_loader,
1021 vflist_thumb_done_cb,
1022 vflist_thumb_error_cb,
1026 if (!thumb_loader_start(vf->thumbs_loader, fd->path))
1028 /* set icon to unknown, continue */
1029 DEBUG_1("thumb loader start failed %s", vf->thumbs_loader->path);
1030 vflist_thumb_do(vf, vf->thumbs_loader, fd);
1038 static void vflist_thumb_update(ViewFile *vf)
1040 vflist_thumb_stop(vf);
1041 if (!VFLIST_INFO(vf, thumbs_enabled)) return;
1043 vflist_thumb_status(vf, 0.0, _("Loading thumbs..."));
1044 vf->thumbs_running = TRUE;
1046 while (vflist_thumb_next(vf));
1050 *-----------------------------------------------------------------------------
1052 *-----------------------------------------------------------------------------
1055 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1057 return g_list_nth_data(vf->list, row);
1060 gint vflist_index_by_path(ViewFile *vf, const gchar *path)
1065 if (!path) return -1;
1070 FileData *fd = work->data;
1071 if (strcmp(path, fd->path) == 0) return p;
1080 guint vflist_count(ViewFile *vf, gint64 *bytes)
1090 FileData *fd = work->data;
1098 return g_list_length(vf->list);
1101 GList *vflist_get_list(ViewFile *vf)
1109 FileData *fd = work->data;
1112 list = g_list_prepend(list, file_data_ref(fd));
1115 return g_list_reverse(list);
1119 *-----------------------------------------------------------------------------
1121 *-----------------------------------------------------------------------------
1124 static gint vflist_row_is_selected(ViewFile *vf, FileData *fd)
1126 GtkTreeModel *store;
1127 GtkTreeSelection *selection;
1132 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1133 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1135 while (!found && work)
1137 GtkTreePath *tpath = work->data;
1141 gtk_tree_model_get_iter(store, &iter, tpath);
1142 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1143 if (fd_n == fd) found = TRUE;
1146 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1152 gint vflist_index_is_selected(ViewFile *vf, gint row)
1156 fd = vf_index_get_data(vf, row);
1157 return vflist_row_is_selected(vf, fd);
1160 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1162 GtkTreeModel *store;
1163 GtkTreeSelection *selection;
1167 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1168 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1178 GtkTreePath *tpath = work->data;
1182 gtk_tree_model_get_iter(store, &iter, tpath);
1183 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1192 count = g_list_length(slist);
1193 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1199 GList *vflist_selection_get_list(ViewFile *vf)
1201 GtkTreeModel *store;
1202 GtkTreeSelection *selection;
1207 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1208 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1212 GtkTreePath *tpath = work->data;
1216 gtk_tree_model_get_iter(store, &iter, tpath);
1217 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1219 list = g_list_prepend(list, file_data_ref(fd));
1223 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1226 return g_list_reverse(list);
1229 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1231 GtkTreeModel *store;
1232 GtkTreeSelection *selection;
1237 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1238 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1242 GtkTreePath *tpath = work->data;
1246 gtk_tree_model_get_iter(store, &iter, tpath);
1247 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1249 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1253 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1256 return g_list_reverse(list);
1259 void vflist_select_all(ViewFile *vf)
1261 GtkTreeSelection *selection;
1263 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1264 gtk_tree_selection_select_all(selection);
1266 VFLIST_INFO(vf, select_fd) = NULL;
1269 void vflist_select_none(ViewFile *vf)
1271 GtkTreeSelection *selection;
1273 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1274 gtk_tree_selection_unselect_all(selection);
1277 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1282 tpath = gtk_tree_model_get_path(store, iter);
1283 result = gtk_tree_path_prev(tpath);
1285 gtk_tree_model_get_iter(store, iter, tpath);
1287 gtk_tree_path_free(tpath);
1292 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1294 if (!gtk_tree_model_get_iter_first(store, iter))
1299 GtkTreeIter next = *iter;
1301 if (gtk_tree_model_iter_next(store, &next))
1310 void vflist_select_invert(ViewFile *vf)
1313 GtkTreeSelection *selection;
1314 GtkTreeModel *store;
1317 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1318 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1320 /* Backward iteration prevents scrolling to the end of the list,
1321 * it scrolls to the first selected row instead. */
1322 valid = tree_model_get_iter_last(store, &iter);
1326 gint selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1329 gtk_tree_selection_unselect_iter(selection, &iter);
1331 gtk_tree_selection_select_iter(selection, &iter);
1333 valid = tree_model_iter_prev(store, &iter);
1337 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1341 if (vflist_find_row(vf, fd, &iter) < 0) return;
1343 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1345 if (!vflist_row_is_selected(vf, fd))
1347 GtkTreeSelection *selection;
1348 GtkTreeModel *store;
1351 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1352 gtk_tree_selection_unselect_all(selection);
1353 gtk_tree_selection_select_iter(selection, &iter);
1354 vflist_move_cursor(vf, &iter);
1356 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1357 tpath = gtk_tree_model_get_path(store, &iter);
1358 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1359 gtk_tree_path_free(tpath);
1363 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1367 if (sel_fd->parent) sel_fd = sel_fd->parent;
1373 FileData *fd = work->data;
1377 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1381 vflist_select_by_fd(vf, fd);
1388 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1390 GtkTreeModel *store;
1392 GtkTreeSelection *selection;
1396 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1398 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1399 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1401 valid = gtk_tree_model_get_iter_first(store, &iter);
1405 gboolean mark_val, selected;
1406 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1408 mark_val = fd->marks[n];
1409 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1413 case MTS_MODE_SET: selected = mark_val;
1415 case MTS_MODE_OR: selected = mark_val | selected;
1417 case MTS_MODE_AND: selected = mark_val & selected;
1419 case MTS_MODE_MINUS: selected = !mark_val & selected;
1424 gtk_tree_selection_select_iter(selection, &iter);
1426 gtk_tree_selection_unselect_iter(selection, &iter);
1428 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1432 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1434 GtkTreeModel *store;
1435 GtkTreeSelection *selection;
1440 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1442 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1443 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1447 GtkTreePath *tpath = work->data;
1451 gtk_tree_model_get_iter(store, &iter, tpath);
1452 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1456 case STM_MODE_SET: fd->marks[n] = 1;
1458 case STM_MODE_RESET: fd->marks[n] = 0;
1460 case STM_MODE_TOGGLE: fd->marks[n] = !fd->marks[n];
1464 file_data_unregister_notify_func(vflist_notify_cb, vf); /* we don't need the notification */
1465 file_data_increment_version(fd);
1466 file_data_register_notify_func(vflist_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1468 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_MARKS + n, fd->marks[n], -1);
1472 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1477 *-----------------------------------------------------------------------------
1479 *-----------------------------------------------------------------------------
1482 static void vflist_listview_set_height(GtkWidget *listview, gint thumb)
1484 GtkTreeViewColumn *column;
1485 GtkCellRenderer *cell;
1488 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), 0); /* first column is thumbnail */
1489 if (!column) return;
1491 gtk_tree_view_column_set_fixed_width(column, ((thumb) ? options->thumbnails.max_width : 4) + 10);
1493 list = gtk_tree_view_column_get_cell_renderers(column);
1498 g_object_set(G_OBJECT(cell), "height", (thumb) ? options->thumbnails.max_height : -1, NULL);
1499 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1502 static void vflist_populate_view(ViewFile *vf)
1504 GtkTreeStore *store;
1506 GtkTreeRowReference *visible_row = NULL;
1510 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1511 thumbs = VFLIST_INFO(vf, thumbs_enabled);
1513 vflist_thumb_stop(vf);
1517 gtk_tree_store_clear(store);
1522 if (GTK_WIDGET_REALIZED(vf->listview) &&
1523 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1525 visible_row = gtk_tree_row_reference_new(GTK_TREE_MODEL(store), tpath);
1526 gtk_tree_path_free(tpath);
1529 vflist_listview_set_height(vf->listview, thumbs);
1531 selected = vflist_selection_get_list(vf);
1533 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected);
1535 if (selected && vflist_selection_count(vf, NULL) == 0)
1537 /* all selected files disappeared */
1538 vflist_select_closest(vf, selected->data);
1541 filelist_free(selected);
1545 if (gtk_tree_row_reference_valid(visible_row))
1547 tpath = gtk_tree_row_reference_get_path(visible_row);
1548 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(vf->listview), tpath, NULL, TRUE, 0.0, 0.0);
1549 gtk_tree_path_free(tpath);
1551 gtk_tree_row_reference_free(visible_row);
1555 vflist_thumb_update(vf);
1558 gint vflist_refresh(ViewFile *vf)
1563 old_list = vf->list;
1566 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1569 file_data_unregister_notify_func(vflist_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1571 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1573 file_data_register_notify_func(vflist_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1575 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1576 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1579 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1581 vflist_populate_view(vf);
1583 filelist_free(old_list);
1584 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1589 static gint vflist_refresh_idle_cb(gpointer data)
1591 ViewFile *vf = data;
1594 vf->refresh_idle_id = -1;
1598 static void vflist_refresh_idle_cancel(ViewFile *vf)
1600 if (vf->refresh_idle_id != -1) g_source_remove(vf->refresh_idle_id);
1601 vf->refresh_idle_id = -1;
1605 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1607 #define CELL_HEIGHT_OVERRIDE 512
1609 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1613 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1614 if (spec && G_IS_PARAM_SPEC_INT(spec))
1616 GParamSpecInt *spec_int;
1618 spec_int = G_PARAM_SPEC_INT(spec);
1619 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1623 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1625 static GdkColor color;
1626 static GtkWidget *done = NULL;
1632 style = gtk_widget_get_style(widget);
1633 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1634 shift_color(&color, -1, 0);
1641 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1642 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1644 ViewFile *vf = data;
1647 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1648 g_object_set(G_OBJECT(cell),
1649 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1650 "cell-background-set", set, NULL);
1653 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gint image, gint right_justify, gint expand)
1655 GtkTreeViewColumn *column;
1656 GtkCellRenderer *renderer;
1658 column = gtk_tree_view_column_new();
1659 gtk_tree_view_column_set_title(column, title);
1660 gtk_tree_view_column_set_min_width(column, 4);
1664 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1665 renderer = gtk_cell_renderer_text_new();
1668 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1670 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1671 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1673 gtk_tree_view_column_set_expand(column, TRUE);
1677 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1678 renderer = gtk_cell_renderer_pixbuf_new();
1679 cell_renderer_height_override(renderer);
1680 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1681 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1684 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1685 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1686 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1688 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1691 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1693 ViewFile *vf = data;
1694 GtkTreeStore *store;
1695 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1701 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1702 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1705 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1707 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1709 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &mark, -1);
1711 fd->marks[col_idx - FILE_COLUMN_MARKS] = mark;
1712 file_data_unregister_notify_func(vflist_notify_cb, vf); /* we don't need the notification */
1713 file_data_increment_version(fd);
1714 file_data_register_notify_func(vflist_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1716 gtk_tree_store_set(store, &iter, col_idx, mark, -1);
1717 gtk_tree_path_free(path);
1720 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1722 GtkTreeViewColumn *column;
1723 GtkCellRenderer *renderer;
1724 GtkTreeStore *store;
1727 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1729 renderer = gtk_cell_renderer_toggle_new();
1730 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1732 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1733 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1734 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1736 index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1737 gtk_tree_view_column_set_fixed_width(column, 16);
1738 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1741 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1745 *-----------------------------------------------------------------------------
1747 *-----------------------------------------------------------------------------
1750 gint vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1752 GtkTreeStore *store;
1754 if (!dir_fd) return FALSE;
1755 if (vf->dir_fd == dir_fd) return TRUE;
1757 file_data_unref(vf->dir_fd);
1758 vf->dir_fd = file_data_ref(dir_fd);
1760 /* force complete reload */
1761 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1762 gtk_tree_store_clear(store);
1764 filelist_free(vf->list);
1767 return vf_refresh(vf);
1770 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1772 ViewFile *vf = data;
1774 file_data_unregister_notify_func(vflist_notify_cb, vf);
1776 vflist_select_idle_cancel(vf);
1777 vflist_refresh_idle_cancel(vf);
1778 vflist_thumb_stop(vf);
1780 filelist_free(vf->list);
1783 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1785 GtkTreeStore *store;
1786 GtkTreeSelection *selection;
1788 GType flist_types[FILE_COLUMN_COUNT];
1791 vf->info = g_new0(ViewFileInfoList, 1);
1793 VFLIST_INFO(vf, click_fd) = NULL;
1794 VFLIST_INFO(vf, select_fd) = NULL;
1795 VFLIST_INFO(vf, thumbs_enabled) = FALSE;
1797 VFLIST_INFO(vf, select_idle_id) = -1;
1798 vf->refresh_idle_id = -1;
1801 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1802 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1803 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1804 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1805 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
1806 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1807 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1808 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1809 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1810 flist_types[i] = G_TYPE_BOOLEAN;
1812 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
1814 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1815 g_object_unref(store);
1817 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1818 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1819 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
1821 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
1822 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
1824 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
1826 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
1827 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
1829 vflist_listview_add_column(vf, FILE_COLUMN_NAME, _("Name"), FALSE, FALSE, FALSE);
1830 vflist_listview_add_column(vf, FILE_COLUMN_SIDECARS, _("SC"), FALSE, FALSE, FALSE);
1832 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
1833 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
1835 file_data_register_notify_func(vflist_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1839 void vflist_thumb_set(ViewFile *vf, gint enable)
1841 if (VFLIST_INFO(vf, thumbs_enabled) == enable) return;
1843 VFLIST_INFO(vf, thumbs_enabled) = enable;
1844 if (vf->layout) vf_refresh(vf);
1847 void vflist_marks_set(ViewFile *vf, gint enable)
1849 GList *columns, *work;
1851 if (vf->marks_enabled == enable) return;
1853 vf->marks_enabled = enable;
1855 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
1860 GtkTreeViewColumn *column = work->data;
1861 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
1864 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
1865 gtk_tree_view_column_set_visible(column, enable);
1868 g_list_free(columns);
1873 *-----------------------------------------------------------------------------
1874 * maintenance (for rename, move, remove)
1875 *-----------------------------------------------------------------------------
1878 static void vflist_notify_cb(FileData *fd, gpointer data)
1880 ViewFile *vf = data;
1883 if (vf->refresh_idle_id != -1) return;
1885 refresh = (fd == vf->dir_fd);
1889 gchar *base = remove_level_from_path(fd->path);
1890 refresh = (strcmp(base, vf->dir_fd->path) == 0);
1894 if (!refresh && fd->change && fd->change->dest)
1896 gchar *dest_base = remove_level_from_path(fd->change->dest);
1897 refresh = (strcmp(dest_base, vf->dir_fd->path) == 0);
1901 if (!refresh && fd->change && fd->change->source)
1903 gchar *source_base = remove_level_from_path(fd->change->source);
1904 refresh = (strcmp(source_base, vf->dir_fd->path) == 0);
1905 g_free(source_base);
1910 vf->refresh_idle_id = g_idle_add(vflist_refresh_idle_cb, vf);
1916 gint vflist_maint_renamed(ViewFile *vf, FileData *fd)
1918 vflist_maint(vf, fd);
1921 gint vflist_maint_removed(ViewFile *vf, FileData *fd, GList *ignore_list)
1923 vflist_maint(vf, fd);
1926 gint vflist_maint_moved(ViewFile *vf, FileData *fd, GList *ignore_list)
1928 vflist_maint(vf, fd);
1933 static gint vflist_maint_find_closest(ViewFile *vf, gint row, gint count, GList *ignore_list)
1943 gint f = vf_index_by_path(vf, work->data);
1944 if (f >= 0) list = g_list_prepend(list, GINT_TO_POINTER(f));
1954 gpointer p = work->data;
1956 if (row == GPOINTER_TO_INT(p))
1961 if (rev == GPOINTER_TO_INT(p))
1966 if (!c) list = g_list_remove(list, p);
1974 if (row > count - 1)
1987 gint vflist_maint_renamed(ViewFile *vf, FileData *fd)
1993 DEBUG_1("%s vflist_maint_renamed: start", get_exec_time());
1995 if (g_list_index(vf->list, fd) < 0) return FALSE;
1997 source_base = remove_level_from_path(fd->change->source);
1998 dest_base = remove_level_from_path(fd->change->dest);
2001 if (strcmp(source_base, dest_base) == 0)
2003 GtkTreeStore *store;
2005 GtkTreeIter position;
2009 old_row = g_list_index(vf->list, fd);
2011 vf->list = g_list_remove(vf->list, fd);
2013 vf->list = filelist_insert_sort(vf->list, fd, vf->sort_method, vf->sort_ascend);
2014 n = g_list_index(vf->list, fd);
2016 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2017 if (vflist_find_row(vf, fd, &iter) >= 0 &&
2018 gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &position, NULL, n))
2022 gtk_tree_store_move_before(store, &iter, &position);
2026 gtk_tree_store_move_after(store, &iter, &position);
2029 gtk_tree_store_set(store, &iter, FILE_COLUMN_NAME, fd->name, -1);
2035 ret = vflist_maint_removed(vf, fd, NULL);
2038 g_free(source_base);
2041 DEBUG_1("%s vflist_maint_renamed: done", get_exec_time());
2046 gint vflist_maint_removed(ViewFile *vf, FileData *fd, GList *ignore_list)
2053 DEBUG_1("%s vflist_maint_removed: start", get_exec_time());
2055 row = g_list_index(vf->list, fd);
2056 if (row < 0) return FALSE;
2058 if (vflist_index_is_selected(vf, row) &&
2059 layout_image_get_collection(vf->layout, NULL) == NULL)
2063 n = vf_count(vf, NULL);
2066 new_row = vflist_maint_find_closest(vf, row, n, ignore_list);
2067 DEBUG_1("row = %d, closest is %d", row, new_row);
2083 fd = vf_index_get_data(vf, new_row);
2084 if (vflist_find_row(vf, fd, &iter) >= 0)
2086 GtkTreeSelection *selection;
2088 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2089 gtk_tree_selection_select_iter(selection, &iter);
2090 vflist_move_cursor(vf, &iter);
2095 fd = vf_index_get_data(vf, row);
2096 if (vflist_find_row(vf, fd, &iter) >= 0)
2098 GtkTreeStore *store;
2099 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2100 gtk_tree_store_remove(store, &iter);
2102 list = g_list_nth(vf->list, row);
2105 /* thumbnail loader check */
2106 if (fd == vf->thumbs_filedata) vf->thumbs_filedata = NULL;
2107 if (vf->thumbs_count > 0) vf->thumbs_count--;
2109 vf->list = g_list_remove(vf->list, fd);
2110 file_data_unref(fd);
2114 DEBUG_1("%s vflist_maint_removed: done", get_exec_time());
2119 gint vflist_maint_moved(ViewFile *vf, FileData *fd, GList *ignore_list)
2124 if (!fd->change->source || !vf->path) return FALSE;
2126 buf = remove_level_from_path(fd->change->source);
2128 if (strcmp(buf, vf->path) == 0)
2130 ret = vflist_maint_removed(vf, fd, ignore_list);