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"
23 #include "layout_image.h"
27 #include "ui_bookmark.h"
28 #include "ui_fileops.h"
30 #include "ui_tree_edit.h"
31 #include "view_file.h"
33 #include <gdk/gdkkeysyms.h> /* for keyboard values */
37 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);
56 *-----------------------------------------------------------------------------
58 *-----------------------------------------------------------------------------
65 } ViewFileFindRowData;
67 static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
69 ViewFileFindRowData *find = data;
71 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
82 static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter)
85 ViewFileFindRowData data = {fd, iter, 0, 0};
87 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
88 gtk_tree_model_foreach(store, vflist_find_row_cb, &data);
100 static gint vflist_find_sidecar_list_idx(GList *work, FileData *fd)
105 FileData *fd_p = work->data;
106 if (fd == fd_p) return i;
110 GList *work2 = fd_p->sidecar_files;
114 if (fd == fd_p) return i;
125 static gint vflist_sidecar_list_count(GList *work)
130 FileData *fd = work->data;
133 GList *work2 = fd->sidecar_files;
145 static void vflist_color_set(ViewFile *vf, FileData *fd, gint color_set)
150 if (vflist_find_row(vf, fd, &iter) < 0) return;
151 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
152 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
155 static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
160 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
162 tpath = gtk_tree_model_get_path(store, iter);
163 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
164 gtk_tree_path_free(tpath);
168 static gint vflist_column_idx(ViewFile *vf, gint store_idx)
170 GList *columns, *work;
173 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
177 GtkTreeViewColumn *column = work->data;
178 if (store_idx == GPOINTER_TO_INT(g_object_get_data (G_OBJECT(column), "column_store_idx")))
184 g_list_free(columns);
190 *-----------------------------------------------------------------------------
192 *-----------------------------------------------------------------------------
195 static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
196 GtkSelectionData *selection_data, guint info,
197 guint time, gpointer data)
201 gchar *uri_text = NULL;
204 if (!VFLIST_INFO(vf, click_fd)) return;
206 if (vflist_row_is_selected(vf, VFLIST_INFO(vf, click_fd)))
208 list = vf_selection_get_list(vf);
212 list = g_list_append(NULL, file_data_ref(VFLIST_INFO(vf, click_fd)));
217 uri_text = uri_text_from_filelist(list, &total, (info == TARGET_TEXT_PLAIN));
222 gtk_selection_data_set(selection_data, selection_data->target,
223 8, (guchar *)uri_text, total);
227 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
231 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), TRUE);
233 if (VFLIST_INFO(vf, thumbs_enabled) &&
234 VFLIST_INFO(vf, click_fd) && VFLIST_INFO(vf, click_fd)->pixbuf)
238 if (vflist_row_is_selected(vf, VFLIST_INFO(vf, click_fd)))
239 items = vf_selection_count(vf, NULL);
243 dnd_set_drag_icon(widget, context, VFLIST_INFO(vf, click_fd)->pixbuf, items);
247 static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
251 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
253 if (context->action == GDK_ACTION_MOVE)
259 void vflist_dnd_init(ViewFile *vf)
261 gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
262 dnd_file_drag_types, dnd_file_drag_types_count,
263 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
264 g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
265 G_CALLBACK(vflist_dnd_get), vf);
266 g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
267 G_CALLBACK(vflist_dnd_begin), vf);
268 g_signal_connect(G_OBJECT(vf->listview), "drag_end",
269 G_CALLBACK(vflist_dnd_end), vf);
273 *-----------------------------------------------------------------------------
275 *-----------------------------------------------------------------------------
278 GList *vflist_pop_menu_file_list(ViewFile *vf)
280 if (!VFLIST_INFO(vf, click_fd)) return NULL;
282 if (vflist_row_is_selected(vf, VFLIST_INFO(vf, click_fd)))
284 return vf_selection_get_list(vf);
287 return g_list_append(NULL, file_data_ref(VFLIST_INFO(vf, click_fd)));
290 void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
294 if (vflist_row_is_selected(vf, VFLIST_INFO(vf, click_fd)))
298 list = vf_selection_get_list(vf);
299 view_window_new_from_list(list);
304 view_window_new(VFLIST_INFO(vf, click_fd));
308 void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
313 list = vf_pop_menu_file_list(vf);
314 if (options->file_ops.enable_in_place_rename &&
315 list && !list->next && VFLIST_INFO(vf, click_fd))
322 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
323 if (vflist_find_row(vf, VFLIST_INFO(vf, click_fd), &iter) >= 0)
327 tpath = gtk_tree_model_get_path(store, &iter);
328 tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
329 vflist_column_idx(vf, FILE_COLUMN_NAME), VFLIST_INFO(vf, click_fd)->name,
330 vflist_row_rename_cb, vf);
331 gtk_tree_path_free(tpath);
336 file_util_rename(NULL, list, vf->listview);
339 static void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
343 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
346 layout_thumb_set(vf->layout, !VFLIST_INFO(vf, thumbs_enabled));
350 vflist_thumb_set(vf, !VFLIST_INFO(vf, thumbs_enabled));
354 void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
358 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
362 void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
365 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
366 VFLIST_INFO(vf, click_fd) = NULL;
371 static GtkWidget *vflist_pop_menu(ViewFile *vf)
378 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), TRUE);
379 active = (VFLIST_INFO(vf, click_fd) != NULL);
381 menu = popup_menu_short_lived();
382 g_signal_connect(G_OBJECT(menu), "destroy",
383 G_CALLBACK(vf_popup_destroy_cb), vf);
385 if (vf->clicked_mark > 0)
387 gint mark = vf->clicked_mark;
388 gchar *str_set_mark = g_strdup_printf(_("_Set mark %d"), mark);
389 gchar *str_res_mark = g_strdup_printf(_("_Reset mark %d"), mark);
390 gchar *str_toggle_mark = g_strdup_printf(_("_Toggle mark %d"), mark);
391 gchar *str_sel_mark = g_strdup_printf(_("_Select mark %d"), mark);
392 gchar *str_sel_mark_or = g_strdup_printf(_("_Add mark %d"), mark);
393 gchar *str_sel_mark_and = g_strdup_printf(_("_Intersection with mark %d"), mark);
394 gchar *str_sel_mark_minus = g_strdup_printf(_("_Unselect mark %d"), mark);
396 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
398 vf->active_mark = mark;
399 vf->clicked_mark = 0;
401 menu_item_add_sensitive(menu, str_set_mark, active,
402 G_CALLBACK(vf_pop_menu_set_mark_sel_cb), vf);
404 menu_item_add_sensitive(menu, str_res_mark, active,
405 G_CALLBACK(vf_pop_menu_res_mark_sel_cb), vf);
407 menu_item_add_sensitive(menu, str_toggle_mark, active,
408 G_CALLBACK(vf_pop_menu_toggle_mark_sel_cb), vf);
410 menu_item_add_divider(menu);
412 menu_item_add_sensitive(menu, str_sel_mark, active,
413 G_CALLBACK(vf_pop_menu_sel_mark_cb), vf);
414 menu_item_add_sensitive(menu, str_sel_mark_or, active,
415 G_CALLBACK(vf_pop_menu_sel_mark_or_cb), vf);
416 menu_item_add_sensitive(menu, str_sel_mark_and, active,
417 G_CALLBACK(vf_pop_menu_sel_mark_and_cb), vf);
418 menu_item_add_sensitive(menu, str_sel_mark_minus, active,
419 G_CALLBACK(vf_pop_menu_sel_mark_minus_cb), vf);
421 menu_item_add_divider(menu);
423 g_free(str_set_mark);
424 g_free(str_res_mark);
425 g_free(str_toggle_mark);
426 g_free(str_sel_mark);
427 g_free(str_sel_mark_and);
428 g_free(str_sel_mark_or);
429 g_free(str_sel_mark_minus);
432 submenu_add_edit(menu, &item, G_CALLBACK(vf_pop_menu_edit_cb), vf);
433 gtk_widget_set_sensitive(item, active);
435 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
436 G_CALLBACK(vf_pop_menu_info_cb), vf);
437 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
438 G_CALLBACK(vf_pop_menu_view_cb), vf);
440 menu_item_add_divider(menu);
441 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
442 G_CALLBACK(vf_pop_menu_copy_cb), vf);
443 menu_item_add_sensitive(menu, _("_Move..."), active,
444 G_CALLBACK(vf_pop_menu_move_cb), vf);
445 menu_item_add_sensitive(menu, _("_Rename..."), active,
446 G_CALLBACK(vf_pop_menu_rename_cb), vf);
447 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
448 G_CALLBACK(vf_pop_menu_delete_cb), vf);
449 if (options->show_copy_path)
450 menu_item_add_sensitive(menu, _("_Copy path"), active,
451 G_CALLBACK(vf_pop_menu_copy_path_cb), vf);
453 menu_item_add_divider(menu);
455 submenu = submenu_add_sort(NULL, G_CALLBACK(vf_pop_menu_sort_cb), vf,
456 FALSE, FALSE, TRUE, vf->sort_method);
457 menu_item_add_divider(submenu);
458 menu_item_add_check(submenu, _("Ascending"), vf->sort_ascend,
459 G_CALLBACK(vf_pop_menu_sort_ascend_cb), vf);
461 item = menu_item_add(menu, _("_Sort"), NULL, NULL);
462 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
464 menu_item_add_check(menu, _("View as _icons"), FALSE,
465 G_CALLBACK(vf_pop_menu_toggle_view_type_cb), vf);
466 menu_item_add_check(menu, _("Show _thumbnails"), VFLIST_INFO(vf, thumbs_enabled),
467 G_CALLBACK(vflist_pop_menu_thumbs_cb), vf);
468 menu_item_add_stock(menu, _("Re_fresh"), GTK_STOCK_REFRESH, G_CALLBACK(vf_pop_menu_refresh_cb), vf);
474 *-----------------------------------------------------------------------------
476 *-----------------------------------------------------------------------------
479 static gint vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
485 if (strlen(new) == 0) return FALSE;
487 old_path = concat_dir_and_file(vf->path, old);
488 new_path = concat_dir_and_file(vf->path, new);
490 if (strchr(new, '/') != NULL)
492 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
493 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
496 else if (isfile(new_path))
498 gchar *text = g_strdup_printf(_("A file with name %s already exists."), new);
499 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
504 gint row = vf_index_by_path(vf, old_path);
507 GList *work = g_list_nth(vf->list, row);
508 FileData *fd = work->data;
510 if (!file_data_add_change_info(fd, FILEDATA_CHANGE_RENAME, old_path, new_path) || !rename_file_ext(fd))
512 gchar *text = g_strdup_printf(_("Unable to rename file:\n%s\nto:\n%s"), old, new);
513 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
525 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
533 if (vflist_find_row(vf, VFLIST_INFO(vf, click_fd), &iter) < 0) return;
534 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
535 tpath = gtk_tree_model_get_path(store, &iter);
536 tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
537 gtk_tree_path_free(tpath);
539 popup_menu_position_clamp(menu, x, y, 0);
542 gint vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
547 if (event->keyval != GDK_Menu) return FALSE;
549 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
555 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
556 gtk_tree_model_get_iter(store, &iter, tpath);
557 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST_INFO(vf, click_fd), -1);
558 gtk_tree_path_free(tpath);
562 VFLIST_INFO(vf, click_fd) = NULL;
565 vf->popup = vflist_pop_menu(vf);
566 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
571 gint vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
577 GtkTreeViewColumn *column;
579 vf->clicked_mark = 0;
581 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
582 &tpath, &column, NULL, NULL))
585 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
587 if (bevent->button == MOUSE_BUTTON_LEFT &&
588 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
591 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
592 vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
594 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
596 gtk_tree_model_get_iter(store, &iter, tpath);
597 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
599 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE);
601 gtk_tree_path_free(tpath);
604 VFLIST_INFO(vf, click_fd) = fd;
606 if (bevent->button == MOUSE_BUTTON_RIGHT)
608 vf->popup = vflist_pop_menu(vf);
609 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
610 bevent->button, bevent->time);
614 if (!fd) return FALSE;
616 if (bevent->button == MOUSE_BUTTON_MIDDLE)
618 if (!vflist_row_is_selected(vf, fd))
620 vflist_color_set(vf, fd, TRUE);
626 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
627 !(bevent->state & GDK_SHIFT_MASK ) &&
628 !(bevent->state & GDK_CONTROL_MASK ) &&
629 vflist_row_is_selected(vf, fd))
631 GtkTreeSelection *selection;
633 gtk_widget_grab_focus(widget);
636 /* returning FALSE and further processing of the event is needed for
637 correct operation of the expander, to show the sidecar files.
638 It however resets the selection of multiple files. With this condition
639 it should work for both cases */
640 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
641 return (gtk_tree_selection_count_selected_rows(selection) > 1);
645 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
647 if (vf->layout) layout_image_full_screen_start(vf->layout);
654 gint vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
661 if (bevent->button == MOUSE_BUTTON_MIDDLE)
663 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
666 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
671 if ((bevent->x != 0 || bevent->y != 0) &&
672 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
673 &tpath, NULL, NULL, NULL))
677 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
678 gtk_tree_model_get_iter(store, &iter, tpath);
679 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
680 gtk_tree_path_free(tpath);
683 if (bevent->button == MOUSE_BUTTON_MIDDLE)
685 if (fd && VFLIST_INFO(vf, click_fd) == fd)
687 GtkTreeSelection *selection;
689 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
690 if (vflist_row_is_selected(vf, fd))
692 gtk_tree_selection_unselect_iter(selection, &iter);
696 gtk_tree_selection_select_iter(selection, &iter);
702 if (fd && VFLIST_INFO(vf, click_fd) == fd &&
703 !(bevent->state & GDK_SHIFT_MASK ) &&
704 !(bevent->state & GDK_CONTROL_MASK ) &&
705 vflist_row_is_selected(vf, fd))
707 GtkTreeSelection *selection;
709 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
710 gtk_tree_selection_unselect_all(selection);
711 gtk_tree_selection_select_iter(selection, &iter);
712 vflist_move_cursor(vf, &iter);
713 // return TRUE;// FIXME - expand
719 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
721 FileData *read_ahead_fd = NULL;
726 cur_fd = layout_image_get_fd(vf->layout);
727 if (sel_fd == cur_fd) return; /* no change */
729 row = g_list_index(vf->list, sel_fd);
730 // FIXME sidecar data
732 if (sel_fd && options->image.enable_read_ahead && row >= 0)
734 if (row > g_list_index(vf->list, cur_fd) &&
735 row + 1 < vf_count(vf, NULL))
737 read_ahead_fd = vf_index_get_data(vf, row + 1);
741 read_ahead_fd = vf_index_get_data(vf, row - 1);
745 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
748 static gint vflist_select_idle_cb(gpointer data)
754 VFLIST_INFO(vf, select_idle_id) = -1;
760 if (VFLIST_INFO(vf, select_fd))
762 vflist_select_image(vf, VFLIST_INFO(vf, select_fd));
763 VFLIST_INFO(vf, select_fd) = NULL;
766 VFLIST_INFO(vf, select_idle_id) = -1;
770 static void vflist_select_idle_cancel(ViewFile *vf)
772 if (VFLIST_INFO(vf, select_idle_id) != -1) g_source_remove(VFLIST_INFO(vf, select_idle_id));
773 VFLIST_INFO(vf, select_idle_id) = -1;
776 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
777 gboolean path_currently_selected, gpointer data)
782 if (!path_currently_selected &&
783 gtk_tree_model_get_iter(store, &iter, tpath))
785 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST_INFO(vf, select_fd), -1);
789 VFLIST_INFO(vf, select_fd) = NULL;
793 VFLIST_INFO(vf, select_idle_id) == -1)
795 VFLIST_INFO(vf, select_idle_id) = g_idle_add(vflist_select_idle_cb, vf);
802 *-----------------------------------------------------------------------------
804 *-----------------------------------------------------------------------------
808 static gboolean vflist_dummy_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
809 gboolean path_currently_selected, gpointer data)
816 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
820 gchar *sidecars = NULL;
822 if (fd->sidecar_files)
823 sidecars = file_data_sc_list_to_string(fd);
824 size = text_from_size(fd->size);
826 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
827 FILE_COLUMN_THUMB, (VFLIST_INFO(vf, thumbs_enabled)) ? fd->pixbuf : NULL,
828 FILE_COLUMN_NAME, fd->name,
829 FILE_COLUMN_SIDECARS, sidecars,
830 FILE_COLUMN_SIZE, size,
831 FILE_COLUMN_DATE, text_from_time(fd->date),
832 FILE_COLUMN_COLOR, FALSE, -1);
833 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
834 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, fd->marks[i], -1);
841 static void vflist_setup_iter_with_sidecars(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
847 vflist_setup_iter(vf, store, iter, fd);
850 /* this is almost the same code as in vflist_populate_view
851 maybe it should be made more generic and used in both places */
854 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &s_iter, iter);
856 work = fd->sidecar_files;
860 FileData *sfd = work->data;
865 FileData *old_sfd = NULL;
869 gtk_tree_model_get(GTK_TREE_MODEL(store), &s_iter, FILE_COLUMN_POINTER, &old_sfd, -1);
877 match = filelist_sort_compare_filedata_full(sfd, old_sfd, SORT_NAME, TRUE);
892 gtk_tree_store_insert_before(store, &new, iter, &s_iter);
896 gtk_tree_store_append(store, &new, iter);
899 vflist_setup_iter(vf, store, &new, sfd);
905 valid = gtk_tree_store_remove(store, &s_iter);
909 vflist_setup_iter(vf, store, &s_iter, sfd);
911 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &s_iter);
921 valid = gtk_tree_store_remove(store, &s_iter);
925 void vflist_sort_set(ViewFile *vf, SortType type, gint ascend)
928 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
933 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
934 if (!vf->list) return;
940 FileData *fd = work->data;
941 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
946 vf->sort_method = type;
947 vf->sort_ascend = ascend;
949 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
951 new_order = g_malloc(i * sizeof(gint));
957 FileData *fd = work->data;
958 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
963 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
964 gtk_tree_store_reorder(store, NULL, new_order);
967 g_hash_table_destroy(fd_idx_hash);
971 *-----------------------------------------------------------------------------
973 *-----------------------------------------------------------------------------
976 static gint vflist_thumb_next(ViewFile *vf);
978 static void vflist_thumb_status(ViewFile *vf, gdouble val, const gchar *text)
980 if (vf->func_thumb_status)
982 vf->func_thumb_status(vf, val, text, vf->data_thumb_status);
986 static void vflist_thumb_cleanup(ViewFile *vf)
988 vflist_thumb_status(vf, 0.0, NULL);
990 vf->thumbs_count = 0;
991 vf->thumbs_running = FALSE;
993 thumb_loader_free(vf->thumbs_loader);
994 vf->thumbs_loader = NULL;
996 vf->thumbs_filedata = NULL;
999 static void vflist_thumb_stop(ViewFile *vf)
1001 if (vf->thumbs_running) vflist_thumb_cleanup(vf);
1004 static void vflist_thumb_do(ViewFile *vf, ThumbLoader *tl, FileData *fd)
1006 GtkTreeStore *store;
1009 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1011 if (fd->pixbuf) g_object_unref(fd->pixbuf);
1012 fd->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
1014 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1015 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->pixbuf, -1);
1017 vflist_thumb_status(vf, (gdouble)(vf->thumbs_count) / vflist_sidecar_list_count(vf->list), _("Loading thumbs..."));
1020 static void vflist_thumb_error_cb(ThumbLoader *tl, gpointer data)
1022 ViewFile *vf = data;
1024 if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1026 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
1029 while (vflist_thumb_next(vf));
1032 static void vflist_thumb_done_cb(ThumbLoader *tl, gpointer data)
1034 ViewFile *vf = data;
1036 if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1038 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
1041 while (vflist_thumb_next(vf));
1044 static gint vflist_thumb_next(ViewFile *vf)
1047 FileData *fd = NULL;
1049 /* first check the visible files */
1051 if (GTK_WIDGET_REALIZED(vf->listview) &&
1052 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1054 GtkTreeModel *store;
1058 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1059 gtk_tree_model_get_iter(store, &iter, tpath);
1060 gtk_tree_path_free(tpath);
1062 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1064 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1065 if (fd->pixbuf) fd = NULL;
1067 valid = gtk_tree_model_iter_next(store, &iter);
1071 /* then find first undone */
1075 GList *work = vf->list;
1078 FileData *fd_p = work->data;
1083 GList *work2 = fd_p->sidecar_files;
1085 while (work2 && !fd)
1088 if (!fd_p->pixbuf) fd = fd_p;
1089 work2 = work2->next;
1099 vflist_thumb_cleanup(vf);
1105 vf->thumbs_filedata = fd;
1107 thumb_loader_free(vf->thumbs_loader);
1109 vf->thumbs_loader = thumb_loader_new(options->thumbnails.max_width, options->thumbnails.max_height);
1110 thumb_loader_set_callbacks(vf->thumbs_loader,
1111 vflist_thumb_done_cb,
1112 vflist_thumb_error_cb,
1116 if (!thumb_loader_start(vf->thumbs_loader, fd->path))
1118 /* set icon to unknown, continue */
1119 DEBUG_1("thumb loader start failed %s", vf->thumbs_loader->path);
1120 vflist_thumb_do(vf, vf->thumbs_loader, fd);
1128 static void vflist_thumb_update(ViewFile *vf)
1130 vflist_thumb_stop(vf);
1131 if (!VFLIST_INFO(vf, thumbs_enabled)) return;
1133 vflist_thumb_status(vf, 0.0, _("Loading thumbs..."));
1134 vf->thumbs_running = TRUE;
1136 while (vflist_thumb_next(vf));
1140 *-----------------------------------------------------------------------------
1142 *-----------------------------------------------------------------------------
1145 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1147 return g_list_nth_data(vf->list, row);
1150 static gint vflist_row_by_path(ViewFile *vf, const gchar *path, FileData **fd)
1155 if (!path) return -1;
1160 FileData *fd_n = work->data;
1161 if (strcmp(path, fd_n->path) == 0)
1174 gint vflist_index_by_path(ViewFile *vf, const gchar *path)
1176 return vflist_row_by_path(vf, path, NULL);
1179 gint vflist_count(ViewFile *vf, gint64 *bytes)
1189 FileData *fd = work->data;
1197 return g_list_length(vf->list);
1200 GList *vflist_get_list(ViewFile *vf)
1208 FileData *fd = work->data;
1211 list = g_list_prepend(list, file_data_ref(fd));
1214 return g_list_reverse(list);
1218 *-----------------------------------------------------------------------------
1220 *-----------------------------------------------------------------------------
1223 static gint vflist_row_is_selected(ViewFile *vf, FileData *fd)
1225 GtkTreeModel *store;
1226 GtkTreeSelection *selection;
1231 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1232 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1234 while (!found && work)
1236 GtkTreePath *tpath = work->data;
1240 gtk_tree_model_get_iter(store, &iter, tpath);
1241 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1242 if (fd_n == fd) found = TRUE;
1245 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1251 gint vflist_index_is_selected(ViewFile *vf, gint row)
1255 fd = vf_index_get_data(vf, row);
1256 return vflist_row_is_selected(vf, fd);
1259 gint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1261 GtkTreeModel *store;
1262 GtkTreeSelection *selection;
1266 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1267 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1277 GtkTreePath *tpath = work->data;
1281 gtk_tree_model_get_iter(store, &iter, tpath);
1282 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1291 count = g_list_length(slist);
1292 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1298 GList *vflist_selection_get_list(ViewFile *vf)
1300 GtkTreeModel *store;
1301 GtkTreeSelection *selection;
1306 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1307 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1311 GtkTreePath *tpath = work->data;
1315 gtk_tree_model_get_iter(store, &iter, tpath);
1316 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1318 list = g_list_prepend(list, file_data_ref(fd));
1322 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1325 return g_list_reverse(list);
1328 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1330 GtkTreeModel *store;
1331 GtkTreeSelection *selection;
1336 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1337 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1341 GtkTreePath *tpath = work->data;
1345 gtk_tree_model_get_iter(store, &iter, tpath);
1346 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1348 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1352 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1355 return g_list_reverse(list);
1358 void vflist_select_all(ViewFile *vf)
1360 GtkTreeSelection *selection;
1362 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1363 gtk_tree_selection_select_all(selection);
1365 VFLIST_INFO(vf, select_fd) = NULL;
1368 void vflist_select_none(ViewFile *vf)
1370 GtkTreeSelection *selection;
1372 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1373 gtk_tree_selection_unselect_all(selection);
1376 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1381 tpath = gtk_tree_model_get_path(store, iter);
1382 result = gtk_tree_path_prev(tpath);
1384 gtk_tree_model_get_iter(store, iter, tpath);
1386 gtk_tree_path_free(tpath);
1391 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1393 if (!gtk_tree_model_get_iter_first(store, iter))
1398 GtkTreeIter next = *iter;
1400 if (gtk_tree_model_iter_next(store, &next))
1409 void vflist_select_invert(ViewFile *vf)
1412 GtkTreeSelection *selection;
1413 GtkTreeModel *store;
1416 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1417 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1419 /* Backward iteration prevents scrolling to the end of the list,
1420 * it scrolls to the first selected row instead. */
1421 valid = tree_model_get_iter_last(store, &iter);
1425 gint selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1428 gtk_tree_selection_unselect_iter(selection, &iter);
1430 gtk_tree_selection_select_iter(selection, &iter);
1432 valid = tree_model_iter_prev(store, &iter);
1436 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1440 if (vflist_find_row(vf, fd, &iter) < 0) return;
1442 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1444 if (!vflist_row_is_selected(vf, fd))
1446 GtkTreeSelection *selection;
1447 GtkTreeModel *store;
1450 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1451 gtk_tree_selection_unselect_all(selection);
1452 gtk_tree_selection_select_iter(selection, &iter);
1453 vflist_move_cursor(vf, &iter);
1455 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1456 tpath = gtk_tree_model_get_path(store, &iter);
1457 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1458 gtk_tree_path_free(tpath);
1462 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1464 GtkTreeModel *store;
1466 GtkTreeSelection *selection;
1470 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1472 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1473 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1475 valid = gtk_tree_model_get_iter_first(store, &iter);
1479 gboolean mark_val, selected;
1480 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1482 mark_val = fd->marks[n];
1483 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1487 case MTS_MODE_SET: selected = mark_val;
1489 case MTS_MODE_OR: selected = mark_val | selected;
1491 case MTS_MODE_AND: selected = mark_val & selected;
1493 case MTS_MODE_MINUS: selected = !mark_val & selected;
1498 gtk_tree_selection_select_iter(selection, &iter);
1500 gtk_tree_selection_unselect_iter(selection, &iter);
1502 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1506 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1508 GtkTreeModel *store;
1509 GtkTreeSelection *selection;
1514 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1516 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1517 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1521 GtkTreePath *tpath = work->data;
1525 gtk_tree_model_get_iter(store, &iter, tpath);
1526 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1530 case STM_MODE_SET: fd->marks[n] = 1;
1532 case STM_MODE_RESET: fd->marks[n] = 0;
1534 case STM_MODE_TOGGLE: fd->marks[n] = !fd->marks[n];
1538 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_MARKS + n, fd->marks[n], -1);
1542 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1547 *-----------------------------------------------------------------------------
1549 *-----------------------------------------------------------------------------
1552 static void vflist_listview_set_height(GtkWidget *listview, gint thumb)
1554 GtkTreeViewColumn *column;
1555 GtkCellRenderer *cell;
1558 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_COLUMN_THUMB - 1);
1559 if (!column) return;
1561 gtk_tree_view_column_set_fixed_width(column, ((thumb) ? options->thumbnails.max_width : 4) + 10);
1563 list = gtk_tree_view_column_get_cell_renderers(column);
1568 g_object_set(G_OBJECT(cell), "height", (thumb) ? options->thumbnails.max_height : -1, NULL);
1569 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1572 static void vflist_populate_view(ViewFile *vf)
1574 GtkTreeStore *store;
1578 GtkTreeRowReference *visible_row = NULL;
1582 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1583 thumbs = VFLIST_INFO(vf, thumbs_enabled);
1585 vflist_thumb_stop(vf);
1589 gtk_tree_store_clear(store);
1594 if (GTK_WIDGET_REALIZED(vf->listview) &&
1595 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1597 visible_row = gtk_tree_row_reference_new(GTK_TREE_MODEL(store), tpath);
1598 gtk_tree_path_free(tpath);
1601 vflist_listview_set_height(vf->listview, thumbs);
1603 valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
1609 FileData *fd = work->data;
1614 FileData *old_fd = NULL;
1618 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1626 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
1628 match = -1; /* probably should not happen*/
1643 gtk_tree_store_insert_before(store, &new, NULL, &iter);
1647 gtk_tree_store_append(store, &new, NULL);
1649 vflist_setup_iter_with_sidecars(vf, store, &new, fd);
1655 valid = gtk_tree_store_remove(store, &iter);
1659 vflist_setup_iter_with_sidecars(vf, store, &iter, fd);
1661 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1671 valid = gtk_tree_store_remove(store, &iter);
1676 if (gtk_tree_row_reference_valid(visible_row))
1678 tpath = gtk_tree_row_reference_get_path(visible_row);
1679 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(vf->listview), tpath, NULL, TRUE, 0.0, 0.0);
1680 gtk_tree_path_free(tpath);
1682 gtk_tree_row_reference_free(visible_row);
1686 vflist_thumb_update(vf);
1689 gint vflist_refresh(ViewFile *vf)
1694 old_list = vf->list;
1699 ret = filelist_read(vf->path, &vf->list, NULL);
1702 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1703 vflist_populate_view(vf);
1705 filelist_free(old_list);
1710 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1712 #define CELL_HEIGHT_OVERRIDE 512
1714 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1718 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1719 if (spec && G_IS_PARAM_SPEC_INT(spec))
1721 GParamSpecInt *spec_int;
1723 spec_int = G_PARAM_SPEC_INT(spec);
1724 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1728 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1730 static GdkColor color;
1731 static GtkWidget *done = NULL;
1737 style = gtk_widget_get_style(widget);
1738 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1739 shift_color(&color, -1, 0);
1746 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1747 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1749 ViewFile *vf = data;
1752 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1753 g_object_set(G_OBJECT(cell),
1754 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1755 "cell-background-set", set, NULL);
1758 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gint image, gint right_justify, gint expand)
1760 GtkTreeViewColumn *column;
1761 GtkCellRenderer *renderer;
1763 column = gtk_tree_view_column_new();
1764 gtk_tree_view_column_set_title(column, title);
1765 gtk_tree_view_column_set_min_width(column, 4);
1769 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1770 renderer = gtk_cell_renderer_text_new();
1773 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1775 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1776 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1778 gtk_tree_view_column_set_expand(column, TRUE);
1782 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1783 renderer = gtk_cell_renderer_pixbuf_new();
1784 cell_renderer_height_override(renderer);
1785 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1786 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1789 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1790 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1791 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1793 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1796 static void vflist_listview_mark_toggled(GtkCellRendererToggle *cell, gchar *path_str, GtkTreeStore *store)
1798 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1804 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1807 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1809 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1811 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &mark, -1);
1813 fd->marks[col_idx - FILE_COLUMN_MARKS] = mark;
1815 gtk_tree_store_set(store, &iter, col_idx, mark, -1);
1816 gtk_tree_path_free(path);
1819 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1821 GtkTreeViewColumn *column;
1822 GtkCellRenderer *renderer;
1823 GtkTreeStore *store;
1826 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1828 renderer = gtk_cell_renderer_toggle_new();
1829 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1831 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1832 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1833 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1835 index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1836 gtk_tree_view_column_set_fixed_width(column, 16);
1837 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1840 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled), store);
1844 *-----------------------------------------------------------------------------
1846 *-----------------------------------------------------------------------------
1849 gint vflist_set_path(ViewFile *vf, const gchar *path)
1851 GtkTreeStore *store;
1853 if (!path) return FALSE;
1854 if (vf->path && strcmp(path, vf->path) == 0) return TRUE;
1857 vf->path = g_strdup(path);
1859 /* force complete reload */
1860 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1861 gtk_tree_store_clear(store);
1863 filelist_free(vf->list);
1866 return vf_refresh(vf);
1869 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1871 ViewFile *vf = data;
1873 vflist_select_idle_cancel(vf);
1874 vflist_thumb_stop(vf);
1876 filelist_free(vf->list);
1879 ViewFile *vflist_new(ViewFile *vf, const gchar *path)
1881 GtkTreeStore *store;
1882 GtkTreeSelection *selection;
1884 GType flist_types[FILE_COLUMN_COUNT];
1887 vf->info = g_new0(ViewFileInfoList, 1);
1889 VFLIST_INFO(vf, click_fd) = NULL;
1890 VFLIST_INFO(vf, select_fd) = NULL;
1891 VFLIST_INFO(vf, thumbs_enabled) = FALSE;
1893 VFLIST_INFO(vf, select_idle_id) = -1;
1895 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1896 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1897 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1898 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
1899 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1900 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1901 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1902 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1903 flist_types[i] = G_TYPE_BOOLEAN;
1905 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
1907 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1908 g_object_unref(store);
1910 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1911 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1912 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
1914 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
1915 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
1917 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
1919 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
1920 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
1922 vflist_listview_add_column(vf, FILE_COLUMN_NAME, _("Name"), FALSE, FALSE, FALSE);
1923 vflist_listview_add_column(vf, FILE_COLUMN_SIDECARS, _("SC"), FALSE, FALSE, FALSE);
1925 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
1926 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
1931 void vflist_thumb_set(ViewFile *vf, gint enable)
1933 if (VFLIST_INFO(vf, thumbs_enabled) == enable) return;
1935 VFLIST_INFO(vf, thumbs_enabled) = enable;
1936 if (vf->layout) vf_refresh(vf);
1939 void vflist_marks_set(ViewFile *vf, gint enable)
1941 GList *columns, *work;
1943 if (vf->marks_enabled == enable) return;
1945 vf->marks_enabled = enable;
1947 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
1952 GtkTreeViewColumn *column = work->data;
1953 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
1956 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
1957 gtk_tree_view_column_set_visible(column, enable);
1960 g_list_free(columns);
1965 *-----------------------------------------------------------------------------
1966 * maintenance (for rename, move, remove)
1967 *-----------------------------------------------------------------------------
1970 static gint vflist_maint_find_closest(ViewFile *vf, gint row, gint count, GList *ignore_list)
1980 gint f = vf_index_by_path(vf, work->data);
1981 if (f >= 0) list = g_list_prepend(list, GINT_TO_POINTER(f));
1991 gpointer p = work->data;
1993 if (row == GPOINTER_TO_INT(p))
1998 if (rev == GPOINTER_TO_INT(p))
2003 if (!c) list = g_list_remove(list, p);
2011 if (row > count - 1)
2024 gint vflist_maint_renamed(ViewFile *vf, FileData *fd)
2030 if (g_list_index(vf->list, fd) < 0) return FALSE;
2032 source_base = remove_level_from_path(fd->change->source);
2033 dest_base = remove_level_from_path(fd->change->dest);
2036 if (strcmp(source_base, dest_base) == 0)
2038 GtkTreeStore *store;
2040 GtkTreeIter position;
2044 old_row = g_list_index(vf->list, fd);
2046 vf->list = g_list_remove(vf->list, fd);
2048 vf->list = filelist_insert_sort(vf->list, fd, vf->sort_method, vf->sort_ascend);
2049 n = g_list_index(vf->list, fd);
2051 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2052 if (vflist_find_row(vf, fd, &iter) >= 0 &&
2053 gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &position, NULL, n))
2057 gtk_tree_store_move_before(store, &iter, &position);
2061 gtk_tree_store_move_after(store, &iter, &position);
2064 gtk_tree_store_set(store, &iter, FILE_COLUMN_NAME, fd->name, -1);
2070 ret = vflist_maint_removed(vf, fd, NULL);
2073 g_free(source_base);
2079 gint vflist_maint_removed(ViewFile *vf, FileData *fd, GList *ignore_list)
2086 row = g_list_index(vf->list, fd);
2087 if (row < 0) return FALSE;
2089 if (vflist_index_is_selected(vf, row) &&
2090 layout_image_get_collection(vf->layout, NULL) == NULL)
2094 n = vf_count(vf, NULL);
2097 new_row = vflist_maint_find_closest(vf, row, n, ignore_list);
2098 DEBUG_1("row = %d, closest is %d", row, new_row);
2114 fd = vf_index_get_data(vf, new_row);
2115 if (vflist_find_row(vf, fd, &iter) >= 0)
2117 GtkTreeSelection *selection;
2119 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2120 gtk_tree_selection_select_iter(selection, &iter);
2121 vflist_move_cursor(vf, &iter);
2126 fd = vf_index_get_data(vf, row);
2127 if (vflist_find_row(vf, fd, &iter) >= 0)
2129 GtkTreeStore *store;
2130 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2131 gtk_tree_store_remove(store, &iter);
2133 list = g_list_nth(vf->list, row);
2136 /* thumbnail loader check */
2137 if (fd == vf->thumbs_filedata) vf->thumbs_filedata = NULL;
2138 if (vf->thumbs_count > 0) vf->thumbs_count--;
2140 vf->list = g_list_remove(vf->list, fd);
2141 file_data_unref(fd);
2148 gint vflist_maint_moved(ViewFile *vf, FileData *fd, GList *ignore_list)
2153 if (!fd->change->source || !vf->path) return FALSE;
2155 buf = remove_level_from_path(fd->change->source);
2157 if (strcmp(buf, vf->path) == 0)
2159 ret = vflist_maint_removed(vf, fd, ignore_list);