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 */
35 #define VFLIST_INFO_POINTER(_vf_) ((ViewFileInfoList *)(_vf_->info))
36 #define VFLIST_INFO(_vf_, _part_) (VFLIST_INFO_POINTER(_vf_)->_part_)
39 FILE_COLUMN_POINTER = 0,
47 FILE_COLUMN_MARKS_LAST = FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
52 static gint vflist_row_is_selected(ViewFile *vf, FileData *fd);
53 static gint vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data);
54 static void vflist_populate_view(ViewFile *vf);
58 *-----------------------------------------------------------------------------
60 *-----------------------------------------------------------------------------
67 } ViewFileFindRowData;
69 static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
71 ViewFileFindRowData *find = data;
73 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
84 static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter)
87 ViewFileFindRowData data = {fd, iter, 0, 0};
89 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
90 gtk_tree_model_foreach(store, vflist_find_row_cb, &data);
102 static gint vflist_find_sidecar_list_idx(GList *work, FileData *fd)
107 FileData *fd_p = work->data;
108 if (fd == fd_p) return i;
112 GList *work2 = fd_p->sidecar_files;
116 if (fd == fd_p) return i;
127 static gint vflist_sidecar_list_count(GList *work)
132 FileData *fd = work->data;
135 GList *work2 = fd->sidecar_files;
147 static void vflist_color_set(ViewFile *vf, FileData *fd, gint color_set)
152 if (vflist_find_row(vf, fd, &iter) < 0) return;
153 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
154 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
157 static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
162 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
164 tpath = gtk_tree_model_get_path(store, iter);
165 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
166 gtk_tree_path_free(tpath);
170 static gint vflist_column_idx(ViewFile *vf, gint store_idx)
172 GList *columns, *work;
175 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
179 GtkTreeViewColumn *column = work->data;
180 if (store_idx == GPOINTER_TO_INT(g_object_get_data (G_OBJECT(column), "column_store_idx")))
186 g_list_free(columns);
192 *-----------------------------------------------------------------------------
194 *-----------------------------------------------------------------------------
197 static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
198 GtkSelectionData *selection_data, guint info,
199 guint time, gpointer data)
203 gchar *uri_text = NULL;
206 if (!VFLIST_INFO(vf, click_fd)) return;
208 if (vflist_row_is_selected(vf, VFLIST_INFO(vf, click_fd)))
210 list = vf_selection_get_list(vf);
214 list = g_list_append(NULL, file_data_ref(VFLIST_INFO(vf, click_fd)));
219 uri_text = uri_text_from_filelist(list, &total, (info == TARGET_TEXT_PLAIN));
224 gtk_selection_data_set(selection_data, selection_data->target,
225 8, (guchar *)uri_text, total);
229 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
233 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), TRUE);
235 if (VFLIST_INFO(vf, thumbs_enabled) &&
236 VFLIST_INFO(vf, click_fd) && VFLIST_INFO(vf, click_fd)->pixbuf)
240 if (vflist_row_is_selected(vf, VFLIST_INFO(vf, click_fd)))
241 items = vf_selection_count(vf, NULL);
245 dnd_set_drag_icon(widget, context, VFLIST_INFO(vf, click_fd)->pixbuf, items);
249 static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
253 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
255 if (context->action == GDK_ACTION_MOVE)
261 void vflist_dnd_init(ViewFile *vf)
263 gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
264 dnd_file_drag_types, dnd_file_drag_types_count,
265 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
266 g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
267 G_CALLBACK(vflist_dnd_get), vf);
268 g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
269 G_CALLBACK(vflist_dnd_begin), vf);
270 g_signal_connect(G_OBJECT(vf->listview), "drag_end",
271 G_CALLBACK(vflist_dnd_end), vf);
275 *-----------------------------------------------------------------------------
277 *-----------------------------------------------------------------------------
280 static GList *vflist_pop_menu_file_list(ViewFile *vf)
282 if (!VFLIST_INFO(vf, click_fd)) return NULL;
284 if (vflist_row_is_selected(vf, VFLIST_INFO(vf, click_fd)))
286 return vf_selection_get_list(vf);
289 return g_list_append(NULL, file_data_ref(VFLIST_INFO(vf, click_fd)));
292 static void vflist_pop_menu_edit_cb(GtkWidget *widget, gpointer data)
298 vf = submenu_item_get_data(widget);
299 n = GPOINTER_TO_INT(data);
303 list = vflist_pop_menu_file_list(vf);
304 start_editor_from_filelist(n, list);
308 static void vflist_pop_menu_info_cb(GtkWidget *widget, gpointer data)
312 info_window_new(NULL, vflist_pop_menu_file_list(vf), NULL);
315 static void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
319 if (vflist_row_is_selected(vf, VFLIST_INFO(vf, click_fd)))
323 list = vf_selection_get_list(vf);
324 view_window_new_from_list(list);
329 view_window_new(VFLIST_INFO(vf, click_fd));
333 static void vflist_pop_menu_copy_cb(GtkWidget *widget, gpointer data)
337 file_util_copy(NULL, vflist_pop_menu_file_list(vf), NULL, vf->listview);
340 static void vflist_pop_menu_copy_path_cb(GtkWidget *widget, gpointer data)
344 file_util_copy_path_list_to_clipboard(vflist_pop_menu_file_list(vf));
347 static void vflist_pop_menu_move_cb(GtkWidget *widget, gpointer data)
351 file_util_move(NULL, vflist_pop_menu_file_list(vf), NULL, vf->listview);
354 static void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
359 list = vflist_pop_menu_file_list(vf);
360 if (options->file_ops.enable_in_place_rename &&
361 list && !list->next && VFLIST_INFO(vf, click_fd))
368 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
369 if (vflist_find_row(vf, VFLIST_INFO(vf, click_fd), &iter) >= 0)
373 tpath = gtk_tree_model_get_path(store, &iter);
374 tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
375 vflist_column_idx(vf, FILE_COLUMN_NAME), VFLIST_INFO(vf, click_fd)->name,
376 vflist_row_rename_cb, vf);
377 gtk_tree_path_free(tpath);
382 file_util_rename(NULL, list, vf->listview);
385 static void vflist_pop_menu_delete_cb(GtkWidget *widget, gpointer data)
389 file_util_delete(NULL, vflist_pop_menu_file_list(vf), vf->listview);
392 static void vflist_pop_menu_sort_cb(GtkWidget *widget, gpointer data)
397 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) return;
399 vf = submenu_item_get_data(widget);
402 type = (SortType)GPOINTER_TO_INT(data);
406 layout_sort_set(vf->layout, type, vf->sort_ascend);
410 vf_sort_set(vf, type, vf->sort_ascend);
414 static void vflist_pop_menu_sort_ascend_cb(GtkWidget *widget, gpointer data)
420 layout_sort_set(vf->layout, vf->sort_method, !vf->sort_ascend);
424 vf_sort_set(vf, vf->sort_method, !vf->sort_ascend);
428 static void vflist_pop_menu_icons_cb(GtkWidget *widget, gpointer data)
432 if (vf->layout) layout_views_set(vf->layout, vf->layout->dir_view_type, TRUE);
435 static void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
439 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
442 layout_thumb_set(vf->layout, !VFLIST_INFO(vf, thumbs_enabled));
446 vflist_thumb_set(vf, !VFLIST_INFO(vf, thumbs_enabled));
450 static void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
454 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
458 static void vflist_pop_menu_sel_mark_cb(GtkWidget *widget, gpointer data)
461 vf_mark_to_selection(vf, vf->active_mark, MTS_MODE_SET);
464 static void vflist_pop_menu_sel_mark_and_cb(GtkWidget *widget, gpointer data)
467 vf_mark_to_selection(vf, vf->active_mark, MTS_MODE_AND);
470 static void vflist_pop_menu_sel_mark_or_cb(GtkWidget *widget, gpointer data)
473 vf_mark_to_selection(vf, vf->active_mark, MTS_MODE_OR);
476 static void vflist_pop_menu_sel_mark_minus_cb(GtkWidget *widget, gpointer data)
479 vf_mark_to_selection(vf, vf->active_mark, MTS_MODE_MINUS);
482 static void vflist_pop_menu_set_mark_sel_cb(GtkWidget *widget, gpointer data)
485 vf_selection_to_mark(vf, vf->active_mark, STM_MODE_SET);
488 static void vflist_pop_menu_res_mark_sel_cb(GtkWidget *widget, gpointer data)
491 vf_selection_to_mark(vf, vf->active_mark, STM_MODE_RESET);
494 static void vflist_pop_menu_toggle_mark_sel_cb(GtkWidget *widget, gpointer data)
497 vf_selection_to_mark(vf, vf->active_mark, STM_MODE_TOGGLE);
501 static void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
504 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
505 VFLIST_INFO(vf, click_fd) = NULL;
510 static GtkWidget *vflist_pop_menu(ViewFile *vf, FileData *fd, gint col_idx)
517 vflist_color_set(vf, fd, TRUE);
518 active = (fd != NULL);
520 menu = popup_menu_short_lived();
521 g_signal_connect(G_OBJECT(menu), "destroy",
522 G_CALLBACK(vflist_popup_destroy_cb), vf);
524 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
526 gint mark = col_idx - FILE_COLUMN_MARKS;
527 gchar *str_set_mark = g_strdup_printf(_("_Set mark %d"), mark + 1);
528 gchar *str_res_mark = g_strdup_printf(_("_Reset mark %d"), mark + 1);
529 gchar *str_toggle_mark = g_strdup_printf(_("_Toggle mark %d"), mark + 1);
530 gchar *str_sel_mark = g_strdup_printf(_("_Select mark %d"), mark + 1);
531 gchar *str_sel_mark_or = g_strdup_printf(_("_Add mark %d"), mark + 1);
532 gchar *str_sel_mark_and = g_strdup_printf(_("_Intersection with mark %d"), mark + 1);
533 gchar *str_sel_mark_minus = g_strdup_printf(_("_Unselect mark %d"), mark + 1);
536 vf->active_mark = mark;
537 menu_item_add_sensitive(menu, str_set_mark, active,
538 G_CALLBACK(vflist_pop_menu_set_mark_sel_cb), vf);
540 menu_item_add_sensitive(menu, str_res_mark, active,
541 G_CALLBACK(vflist_pop_menu_res_mark_sel_cb), vf);
543 menu_item_add_sensitive(menu, str_toggle_mark, active,
544 G_CALLBACK(vflist_pop_menu_toggle_mark_sel_cb), vf);
546 menu_item_add_divider(menu);
548 menu_item_add_sensitive(menu, str_sel_mark, active,
549 G_CALLBACK(vflist_pop_menu_sel_mark_cb), vf);
550 menu_item_add_sensitive(menu, str_sel_mark_or, active,
551 G_CALLBACK(vflist_pop_menu_sel_mark_or_cb), vf);
552 menu_item_add_sensitive(menu, str_sel_mark_and, active,
553 G_CALLBACK(vflist_pop_menu_sel_mark_and_cb), vf);
554 menu_item_add_sensitive(menu, str_sel_mark_minus, active,
555 G_CALLBACK(vflist_pop_menu_sel_mark_minus_cb), vf);
557 menu_item_add_divider(menu);
559 g_free(str_set_mark);
560 g_free(str_res_mark);
561 g_free(str_toggle_mark);
562 g_free(str_sel_mark);
563 g_free(str_sel_mark_and);
564 g_free(str_sel_mark_or);
565 g_free(str_sel_mark_minus);
568 submenu_add_edit(menu, &item, G_CALLBACK(vflist_pop_menu_edit_cb), vf);
569 gtk_widget_set_sensitive(item, active);
571 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
572 G_CALLBACK(vflist_pop_menu_info_cb), vf);
573 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
574 G_CALLBACK(vflist_pop_menu_view_cb), vf);
576 menu_item_add_divider(menu);
577 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
578 G_CALLBACK(vflist_pop_menu_copy_cb), vf);
579 menu_item_add_sensitive(menu, _("_Move..."), active,
580 G_CALLBACK(vflist_pop_menu_move_cb), vf);
581 menu_item_add_sensitive(menu, _("_Rename..."), active,
582 G_CALLBACK(vflist_pop_menu_rename_cb), vf);
583 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
584 G_CALLBACK(vflist_pop_menu_delete_cb), vf);
585 if (options->show_copy_path)
586 menu_item_add_sensitive(menu, _("_Copy path"), active,
587 G_CALLBACK(vflist_pop_menu_copy_path_cb), vf);
589 menu_item_add_divider(menu);
591 submenu = submenu_add_sort(NULL, G_CALLBACK(vflist_pop_menu_sort_cb), vf,
592 FALSE, FALSE, TRUE, vf->sort_method);
593 menu_item_add_divider(submenu);
594 menu_item_add_check(submenu, _("Ascending"), vf->sort_ascend,
595 G_CALLBACK(vflist_pop_menu_sort_ascend_cb), vf);
597 item = menu_item_add(menu, _("_Sort"), NULL, NULL);
598 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
600 menu_item_add_check(menu, _("View as _icons"), FALSE,
601 G_CALLBACK(vflist_pop_menu_icons_cb), vf);
602 menu_item_add_check(menu, _("Show _thumbnails"), VFLIST_INFO(vf, thumbs_enabled),
603 G_CALLBACK(vflist_pop_menu_thumbs_cb), vf);
604 menu_item_add_stock(menu, _("Re_fresh"), GTK_STOCK_REFRESH, G_CALLBACK(vflist_pop_menu_refresh_cb), vf);
610 *-----------------------------------------------------------------------------
612 *-----------------------------------------------------------------------------
615 static gint vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
621 if (strlen(new) == 0) return FALSE;
623 old_path = concat_dir_and_file(vf->path, old);
624 new_path = concat_dir_and_file(vf->path, new);
626 if (strchr(new, '/') != NULL)
628 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
629 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
632 else if (isfile(new_path))
634 gchar *text = g_strdup_printf(_("A file with name %s already exists."), new);
635 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
640 gint row = vf_index_by_path(vf, old_path);
643 GList *work = g_list_nth(vf->list, row);
644 FileData *fd = work->data;
646 if (!file_data_add_change_info(fd, FILEDATA_CHANGE_RENAME, old_path, new_path) || !rename_file_ext(fd))
648 gchar *text = g_strdup_printf(_("Unable to rename file:\n%s\nto:\n%s"), old, new);
649 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
661 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
669 if (vflist_find_row(vf, VFLIST_INFO(vf, click_fd), &iter) < 0) return;
670 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
671 tpath = gtk_tree_model_get_path(store, &iter);
672 tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
673 gtk_tree_path_free(tpath);
675 popup_menu_position_clamp(menu, x, y, 0);
678 gint vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
683 if (event->keyval != GDK_Menu) return FALSE;
685 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
691 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
692 gtk_tree_model_get_iter(store, &iter, tpath);
693 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST_INFO(vf, click_fd), -1);
694 gtk_tree_path_free(tpath);
698 VFLIST_INFO(vf, click_fd) = NULL;
701 vf->popup = vflist_pop_menu(vf, VFLIST_INFO(vf, click_fd), 0);
702 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
707 gint vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
713 GtkTreeViewColumn *column;
716 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
717 &tpath, &column, NULL, NULL))
720 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
722 if (bevent->button == MOUSE_BUTTON_LEFT &&
723 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
726 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
728 gtk_tree_model_get_iter(store, &iter, tpath);
729 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
731 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE);
733 gtk_tree_path_free(tpath);
736 VFLIST_INFO(vf, click_fd) = fd;
738 if (bevent->button == MOUSE_BUTTON_RIGHT)
740 vf->popup = vflist_pop_menu(vf, VFLIST_INFO(vf, click_fd), col_idx);
741 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
742 bevent->button, bevent->time);
746 if (!fd) return FALSE;
748 if (bevent->button == MOUSE_BUTTON_MIDDLE)
750 if (!vflist_row_is_selected(vf, fd))
752 vflist_color_set(vf, fd, TRUE);
758 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
759 !(bevent->state & GDK_SHIFT_MASK ) &&
760 !(bevent->state & GDK_CONTROL_MASK ) &&
761 vflist_row_is_selected(vf, fd))
763 GtkTreeSelection *selection;
765 gtk_widget_grab_focus(widget);
768 /* returning FALSE and further processing of the event is needed for
769 correct operation of the expander, to show the sidecar files.
770 It however resets the selection of multiple files. With this condition
771 it should work for both cases */
772 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
773 return (gtk_tree_selection_count_selected_rows(selection) > 1);
777 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
779 if (vf->layout) layout_image_full_screen_start(vf->layout);
786 gint vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
793 if (bevent->button == MOUSE_BUTTON_MIDDLE)
795 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
798 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
803 if ((bevent->x != 0 || bevent->y != 0) &&
804 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
805 &tpath, NULL, NULL, NULL))
809 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
810 gtk_tree_model_get_iter(store, &iter, tpath);
811 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
812 gtk_tree_path_free(tpath);
815 if (bevent->button == MOUSE_BUTTON_MIDDLE)
817 if (fd && VFLIST_INFO(vf, click_fd) == fd)
819 GtkTreeSelection *selection;
821 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
822 if (vflist_row_is_selected(vf, fd))
824 gtk_tree_selection_unselect_iter(selection, &iter);
828 gtk_tree_selection_select_iter(selection, &iter);
834 if (fd && VFLIST_INFO(vf, click_fd) == fd &&
835 !(bevent->state & GDK_SHIFT_MASK ) &&
836 !(bevent->state & GDK_CONTROL_MASK ) &&
837 vflist_row_is_selected(vf, fd))
839 GtkTreeSelection *selection;
841 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
842 gtk_tree_selection_unselect_all(selection);
843 gtk_tree_selection_select_iter(selection, &iter);
844 vflist_move_cursor(vf, &iter);
845 // return TRUE;// FIXME - expand
851 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
853 FileData *read_ahead_fd = NULL;
858 cur_fd = layout_image_get_fd(vf->layout);
859 if (sel_fd == cur_fd) return; /* no change */
861 row = g_list_index(vf->list, sel_fd);
862 // FIXME sidecar data
864 if (sel_fd && options->image.enable_read_ahead && row >= 0)
866 if (row > g_list_index(vf->list, cur_fd) &&
867 row + 1 < vf_count(vf, NULL))
869 read_ahead_fd = vf_index_get_data(vf, row + 1);
873 read_ahead_fd = vf_index_get_data(vf, row - 1);
877 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
880 static gint vflist_select_idle_cb(gpointer data)
886 VFLIST_INFO(vf, select_idle_id) = -1;
892 if (VFLIST_INFO(vf, select_fd))
894 vflist_select_image(vf, VFLIST_INFO(vf, select_fd));
895 VFLIST_INFO(vf, select_fd) = NULL;
898 VFLIST_INFO(vf, select_idle_id) = -1;
902 static void vflist_select_idle_cancel(ViewFile *vf)
904 if (VFLIST_INFO(vf, select_idle_id) != -1) g_source_remove(VFLIST_INFO(vf, select_idle_id));
905 VFLIST_INFO(vf, select_idle_id) = -1;
908 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
909 gboolean path_currently_selected, gpointer data)
914 if (!path_currently_selected &&
915 gtk_tree_model_get_iter(store, &iter, tpath))
917 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST_INFO(vf, select_fd), -1);
921 VFLIST_INFO(vf, select_fd) = NULL;
925 VFLIST_INFO(vf, select_idle_id) == -1)
927 VFLIST_INFO(vf, select_idle_id) = g_idle_add(vflist_select_idle_cb, vf);
934 *-----------------------------------------------------------------------------
936 *-----------------------------------------------------------------------------
940 static gboolean vflist_dummy_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
941 gboolean path_currently_selected, gpointer data)
948 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
952 gchar *sidecars = NULL;
954 if (fd->sidecar_files)
955 sidecars = file_data_sc_list_to_string(fd);
956 size = text_from_size(fd->size);
958 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
959 FILE_COLUMN_THUMB, (VFLIST_INFO(vf, thumbs_enabled)) ? fd->pixbuf : NULL,
960 FILE_COLUMN_NAME, fd->name,
961 FILE_COLUMN_SIDECARS, sidecars,
962 FILE_COLUMN_SIZE, size,
963 FILE_COLUMN_DATE, text_from_time(fd->date),
964 FILE_COLUMN_COLOR, FALSE, -1);
965 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
966 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, fd->marks[i], -1);
973 static void vflist_setup_iter_with_sidecars(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
979 vflist_setup_iter(vf, store, iter, fd);
982 /* this is almost the same code as in vflist_populate_view
983 maybe it should be made more generic and used in both places */
986 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &s_iter, iter);
988 work = fd->sidecar_files;
992 FileData *sfd = work->data;
997 FileData *old_sfd = NULL;
1001 gtk_tree_model_get(GTK_TREE_MODEL(store), &s_iter, FILE_COLUMN_POINTER, &old_sfd, -1);
1009 match = filelist_sort_compare_filedata_full(sfd, old_sfd, SORT_NAME, TRUE);
1024 gtk_tree_store_insert_before(store, &new, iter, &s_iter);
1028 gtk_tree_store_append(store, &new, iter);
1031 vflist_setup_iter(vf, store, &new, sfd);
1037 valid = gtk_tree_store_remove(store, &s_iter);
1041 vflist_setup_iter(vf, store, &s_iter, sfd);
1043 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &s_iter);
1053 valid = gtk_tree_store_remove(store, &s_iter);
1057 void vflist_sort_set(ViewFile *vf, SortType type, gint ascend)
1060 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
1062 GtkTreeStore *store;
1065 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1066 if (!vf->list) return;
1072 FileData *fd = work->data;
1073 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1078 vf->sort_method = type;
1079 vf->sort_ascend = ascend;
1081 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1083 new_order = g_malloc(i * sizeof(gint));
1089 FileData *fd = work->data;
1090 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1095 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1096 gtk_tree_store_reorder(store, NULL, new_order);
1099 g_hash_table_destroy(fd_idx_hash);
1103 *-----------------------------------------------------------------------------
1105 *-----------------------------------------------------------------------------
1108 static gint vflist_thumb_next(ViewFile *vf);
1110 static void vflist_thumb_status(ViewFile *vf, gdouble val, const gchar *text)
1112 if (vf->func_thumb_status)
1114 vf->func_thumb_status(vf, val, text, vf->data_thumb_status);
1118 static void vflist_thumb_cleanup(ViewFile *vf)
1120 vflist_thumb_status(vf, 0.0, NULL);
1122 vf->thumbs_count = 0;
1123 vf->thumbs_running = FALSE;
1125 thumb_loader_free(vf->thumbs_loader);
1126 vf->thumbs_loader = NULL;
1128 vf->thumbs_filedata = NULL;
1131 static void vflist_thumb_stop(ViewFile *vf)
1133 if (vf->thumbs_running) vflist_thumb_cleanup(vf);
1136 static void vflist_thumb_do(ViewFile *vf, ThumbLoader *tl, FileData *fd)
1138 GtkTreeStore *store;
1141 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1143 if (fd->pixbuf) g_object_unref(fd->pixbuf);
1144 fd->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
1146 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1147 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->pixbuf, -1);
1149 vflist_thumb_status(vf, (gdouble)(vf->thumbs_count) / vflist_sidecar_list_count(vf->list), _("Loading thumbs..."));
1152 static void vflist_thumb_error_cb(ThumbLoader *tl, gpointer data)
1154 ViewFile *vf = data;
1156 if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1158 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
1161 while (vflist_thumb_next(vf));
1164 static void vflist_thumb_done_cb(ThumbLoader *tl, gpointer data)
1166 ViewFile *vf = data;
1168 if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1170 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
1173 while (vflist_thumb_next(vf));
1176 static gint vflist_thumb_next(ViewFile *vf)
1179 FileData *fd = NULL;
1181 /* first check the visible files */
1183 if (GTK_WIDGET_REALIZED(vf->listview) &&
1184 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1186 GtkTreeModel *store;
1190 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1191 gtk_tree_model_get_iter(store, &iter, tpath);
1192 gtk_tree_path_free(tpath);
1194 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1196 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1197 if (fd->pixbuf) fd = NULL;
1199 valid = gtk_tree_model_iter_next(store, &iter);
1203 /* then find first undone */
1207 GList *work = vf->list;
1210 FileData *fd_p = work->data;
1215 GList *work2 = fd_p->sidecar_files;
1217 while (work2 && !fd)
1220 if (!fd_p->pixbuf) fd = fd_p;
1221 work2 = work2->next;
1231 vflist_thumb_cleanup(vf);
1237 vf->thumbs_filedata = fd;
1239 thumb_loader_free(vf->thumbs_loader);
1241 vf->thumbs_loader = thumb_loader_new(options->thumbnails.max_width, options->thumbnails.max_height);
1242 thumb_loader_set_callbacks(vf->thumbs_loader,
1243 vflist_thumb_done_cb,
1244 vflist_thumb_error_cb,
1248 if (!thumb_loader_start(vf->thumbs_loader, fd->path))
1250 /* set icon to unknown, continue */
1251 DEBUG_1("thumb loader start failed %s", vf->thumbs_loader->path);
1252 vflist_thumb_do(vf, vf->thumbs_loader, fd);
1260 static void vflist_thumb_update(ViewFile *vf)
1262 vflist_thumb_stop(vf);
1263 if (!VFLIST_INFO(vf, thumbs_enabled)) return;
1265 vflist_thumb_status(vf, 0.0, _("Loading thumbs..."));
1266 vf->thumbs_running = TRUE;
1268 while (vflist_thumb_next(vf));
1272 *-----------------------------------------------------------------------------
1274 *-----------------------------------------------------------------------------
1277 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1279 return g_list_nth_data(vf->list, row);
1282 static gint vflist_row_by_path(ViewFile *vf, const gchar *path, FileData **fd)
1287 if (!path) return -1;
1292 FileData *fd_n = work->data;
1293 if (strcmp(path, fd_n->path) == 0)
1306 gint vflist_index_by_path(ViewFile *vf, const gchar *path)
1308 return vflist_row_by_path(vf, path, NULL);
1311 gint vflist_count(ViewFile *vf, gint64 *bytes)
1321 FileData *fd = work->data;
1329 return g_list_length(vf->list);
1332 GList *vflist_get_list(ViewFile *vf)
1340 FileData *fd = work->data;
1343 list = g_list_prepend(list, file_data_ref(fd));
1346 return g_list_reverse(list);
1350 *-----------------------------------------------------------------------------
1352 *-----------------------------------------------------------------------------
1355 static gint vflist_row_is_selected(ViewFile *vf, FileData *fd)
1357 GtkTreeModel *store;
1358 GtkTreeSelection *selection;
1363 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1364 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1366 while (!found && work)
1368 GtkTreePath *tpath = work->data;
1372 gtk_tree_model_get_iter(store, &iter, tpath);
1373 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1374 if (fd_n == fd) found = TRUE;
1377 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1383 gint vflist_index_is_selected(ViewFile *vf, gint row)
1387 fd = vf_index_get_data(vf, row);
1388 return vflist_row_is_selected(vf, fd);
1391 gint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1393 GtkTreeModel *store;
1394 GtkTreeSelection *selection;
1398 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1399 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1409 GtkTreePath *tpath = work->data;
1413 gtk_tree_model_get_iter(store, &iter, tpath);
1414 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1423 count = g_list_length(slist);
1424 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1430 GList *vflist_selection_get_list(ViewFile *vf)
1432 GtkTreeModel *store;
1433 GtkTreeSelection *selection;
1438 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1439 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1443 GtkTreePath *tpath = work->data;
1447 gtk_tree_model_get_iter(store, &iter, tpath);
1448 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1450 list = g_list_prepend(list, file_data_ref(fd));
1454 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1457 return g_list_reverse(list);
1460 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1462 GtkTreeModel *store;
1463 GtkTreeSelection *selection;
1468 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1469 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1473 GtkTreePath *tpath = work->data;
1477 gtk_tree_model_get_iter(store, &iter, tpath);
1478 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1480 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1484 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1487 return g_list_reverse(list);
1490 void vflist_select_all(ViewFile *vf)
1492 GtkTreeSelection *selection;
1494 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1495 gtk_tree_selection_select_all(selection);
1497 VFLIST_INFO(vf, select_fd) = NULL;
1500 void vflist_select_none(ViewFile *vf)
1502 GtkTreeSelection *selection;
1504 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1505 gtk_tree_selection_unselect_all(selection);
1508 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1513 tpath = gtk_tree_model_get_path(store, iter);
1514 result = gtk_tree_path_prev(tpath);
1516 gtk_tree_model_get_iter(store, iter, tpath);
1518 gtk_tree_path_free(tpath);
1523 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1525 if (!gtk_tree_model_get_iter_first(store, iter))
1530 GtkTreeIter next = *iter;
1532 if (gtk_tree_model_iter_next(store, &next))
1541 void vflist_select_invert(ViewFile *vf)
1544 GtkTreeSelection *selection;
1545 GtkTreeModel *store;
1548 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1549 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1551 /* Backward iteration prevents scrolling to the end of the list,
1552 * it scrolls to the first selected row instead. */
1553 valid = tree_model_get_iter_last(store, &iter);
1557 gint selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1560 gtk_tree_selection_unselect_iter(selection, &iter);
1562 gtk_tree_selection_select_iter(selection, &iter);
1564 valid = tree_model_iter_prev(store, &iter);
1568 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1572 if (vflist_find_row(vf, fd, &iter) < 0) return;
1574 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1576 if (!vflist_row_is_selected(vf, fd))
1578 GtkTreeSelection *selection;
1579 GtkTreeModel *store;
1582 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1583 gtk_tree_selection_unselect_all(selection);
1584 gtk_tree_selection_select_iter(selection, &iter);
1585 vflist_move_cursor(vf, &iter);
1587 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1588 tpath = gtk_tree_model_get_path(store, &iter);
1589 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1590 gtk_tree_path_free(tpath);
1594 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1596 GtkTreeModel *store;
1598 GtkTreeSelection *selection;
1601 g_assert(mark >= 0 && mark < FILEDATA_MARKS_SIZE);
1603 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1604 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1606 valid = gtk_tree_model_get_iter_first(store, &iter);
1610 gboolean mark_val, selected;
1611 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1613 mark_val = fd->marks[mark];
1614 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1618 case MTS_MODE_SET: selected = mark_val;
1620 case MTS_MODE_OR: selected = mark_val | selected;
1622 case MTS_MODE_AND: selected = mark_val & selected;
1624 case MTS_MODE_MINUS: selected = !mark_val & selected;
1629 gtk_tree_selection_select_iter(selection, &iter);
1631 gtk_tree_selection_unselect_iter(selection, &iter);
1633 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1637 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1639 GtkTreeModel *store;
1640 GtkTreeSelection *selection;
1644 g_assert(mark >= 0 && mark < FILEDATA_MARKS_SIZE);
1646 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1647 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1651 GtkTreePath *tpath = work->data;
1655 gtk_tree_model_get_iter(store, &iter, tpath);
1656 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1660 case STM_MODE_SET: fd->marks[mark] = 1;
1662 case STM_MODE_RESET: fd->marks[mark] = 0;
1664 case STM_MODE_TOGGLE: fd->marks[mark] = !fd->marks[mark];
1668 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_MARKS + mark, fd->marks[mark], -1);
1672 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1677 *-----------------------------------------------------------------------------
1679 *-----------------------------------------------------------------------------
1682 static void vflist_listview_set_height(GtkWidget *listview, gint thumb)
1684 GtkTreeViewColumn *column;
1685 GtkCellRenderer *cell;
1688 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_COLUMN_THUMB - 1);
1689 if (!column) return;
1691 gtk_tree_view_column_set_fixed_width(column, ((thumb) ? options->thumbnails.max_width : 4) + 10);
1693 list = gtk_tree_view_column_get_cell_renderers(column);
1698 g_object_set(G_OBJECT(cell), "height", (thumb) ? options->thumbnails.max_height : -1, NULL);
1699 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1702 static void vflist_populate_view(ViewFile *vf)
1704 GtkTreeStore *store;
1708 GtkTreeRowReference *visible_row = NULL;
1712 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1713 thumbs = VFLIST_INFO(vf, thumbs_enabled);
1715 vflist_thumb_stop(vf);
1719 gtk_tree_store_clear(store);
1724 if (GTK_WIDGET_REALIZED(vf->listview) &&
1725 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1727 visible_row = gtk_tree_row_reference_new(GTK_TREE_MODEL(store), tpath);
1728 gtk_tree_path_free(tpath);
1731 vflist_listview_set_height(vf->listview, thumbs);
1733 valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
1739 FileData *fd = work->data;
1744 FileData *old_fd = NULL;
1748 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1756 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
1758 match = -1; /* probably should not happen*/
1773 gtk_tree_store_insert_before(store, &new, NULL, &iter);
1777 gtk_tree_store_append(store, &new, NULL);
1779 vflist_setup_iter_with_sidecars(vf, store, &new, fd);
1785 valid = gtk_tree_store_remove(store, &iter);
1789 vflist_setup_iter_with_sidecars(vf, store, &iter, fd);
1791 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1801 valid = gtk_tree_store_remove(store, &iter);
1806 if (gtk_tree_row_reference_valid(visible_row))
1808 tpath = gtk_tree_row_reference_get_path(visible_row);
1809 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(vf->listview), tpath, NULL, TRUE, 0.0, 0.0);
1810 gtk_tree_path_free(tpath);
1812 gtk_tree_row_reference_free(visible_row);
1816 vflist_thumb_update(vf);
1819 gint vflist_refresh(ViewFile *vf)
1824 old_list = vf->list;
1829 ret = filelist_read(vf->path, &vf->list, NULL);
1832 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1833 vflist_populate_view(vf);
1835 filelist_free(old_list);
1840 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1842 #define CELL_HEIGHT_OVERRIDE 512
1844 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1848 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1849 if (spec && G_IS_PARAM_SPEC_INT(spec))
1851 GParamSpecInt *spec_int;
1853 spec_int = G_PARAM_SPEC_INT(spec);
1854 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1858 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1860 static GdkColor color;
1861 static GtkWidget *done = NULL;
1867 style = gtk_widget_get_style(widget);
1868 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1869 shift_color(&color, -1, 0);
1876 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1877 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1879 ViewFile *vf = data;
1882 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1883 g_object_set(G_OBJECT(cell),
1884 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1885 "cell-background-set", set, NULL);
1888 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gint image, gint right_justify, gint expand)
1890 GtkTreeViewColumn *column;
1891 GtkCellRenderer *renderer;
1893 column = gtk_tree_view_column_new();
1894 gtk_tree_view_column_set_title(column, title);
1895 gtk_tree_view_column_set_min_width(column, 4);
1899 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1900 renderer = gtk_cell_renderer_text_new();
1903 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1905 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1906 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1908 gtk_tree_view_column_set_expand(column, TRUE);
1912 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1913 renderer = gtk_cell_renderer_pixbuf_new();
1914 cell_renderer_height_override(renderer);
1915 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1916 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1919 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1920 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1921 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1923 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1926 static void vflist_listview_mark_toggled(GtkCellRendererToggle *cell, gchar *path_str, GtkTreeStore *store)
1928 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1934 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1937 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1939 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1941 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &mark, -1);
1943 fd->marks[col_idx - FILE_COLUMN_MARKS] = mark;
1945 gtk_tree_store_set(store, &iter, col_idx, mark, -1);
1946 gtk_tree_path_free(path);
1949 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1951 GtkTreeViewColumn *column;
1952 GtkCellRenderer *renderer;
1953 GtkTreeStore *store;
1956 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1958 renderer = gtk_cell_renderer_toggle_new();
1959 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1961 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1962 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1963 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1965 index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1966 gtk_tree_view_column_set_fixed_width(column, 16);
1967 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1970 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled), store);
1974 *-----------------------------------------------------------------------------
1976 *-----------------------------------------------------------------------------
1979 gint vflist_set_path(ViewFile *vf, const gchar *path)
1981 GtkTreeStore *store;
1983 if (!path) return FALSE;
1984 if (vf->path && strcmp(path, vf->path) == 0) return TRUE;
1987 vf->path = g_strdup(path);
1989 /* force complete reload */
1990 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1991 gtk_tree_store_clear(store);
1993 filelist_free(vf->list);
1996 return vf_refresh(vf);
1999 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
2001 ViewFile *vf = data;
2003 vflist_select_idle_cancel(vf);
2004 vflist_thumb_stop(vf);
2006 filelist_free(vf->list);
2009 ViewFile *vflist_new(ViewFile *vf, const gchar *path)
2011 GtkTreeStore *store;
2012 GtkTreeSelection *selection;
2014 GType flist_types[FILE_COLUMN_COUNT];
2017 vf->info = g_new0(ViewFileInfoList, 1);
2019 VFLIST_INFO(vf, click_fd) = NULL;
2020 VFLIST_INFO(vf, select_fd) = NULL;
2021 VFLIST_INFO(vf, thumbs_enabled) = FALSE;
2023 VFLIST_INFO(vf, select_idle_id) = -1;
2025 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
2026 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
2027 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
2028 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
2029 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
2030 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
2031 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
2032 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
2033 flist_types[i] = G_TYPE_BOOLEAN;
2035 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
2037 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2038 g_object_unref(store);
2040 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2041 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
2042 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
2044 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2045 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2047 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2049 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2050 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2052 vflist_listview_add_column(vf, FILE_COLUMN_NAME, _("Name"), FALSE, FALSE, FALSE);
2053 vflist_listview_add_column(vf, FILE_COLUMN_SIDECARS, _("SC"), FALSE, FALSE, FALSE);
2055 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2056 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2061 void vflist_thumb_set(ViewFile *vf, gint enable)
2063 if (VFLIST_INFO(vf, thumbs_enabled) == enable) return;
2065 VFLIST_INFO(vf, thumbs_enabled) = enable;
2066 if (vf->layout) vf_refresh(vf);
2069 void vflist_marks_set(ViewFile *vf, gint enable)
2071 GList *columns, *work;
2073 if (vf->marks_enabled == enable) return;
2075 vf->marks_enabled = enable;
2077 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2082 GtkTreeViewColumn *column = work->data;
2083 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2086 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2087 gtk_tree_view_column_set_visible(column, enable);
2090 g_list_free(columns);
2095 *-----------------------------------------------------------------------------
2096 * maintenance (for rename, move, remove)
2097 *-----------------------------------------------------------------------------
2100 static gint vflist_maint_find_closest(ViewFile *vf, gint row, gint count, GList *ignore_list)
2110 gint f = vf_index_by_path(vf, work->data);
2111 if (f >= 0) list = g_list_prepend(list, GINT_TO_POINTER(f));
2121 gpointer p = work->data;
2123 if (row == GPOINTER_TO_INT(p))
2128 if (rev == GPOINTER_TO_INT(p))
2133 if (!c) list = g_list_remove(list, p);
2141 if (row > count - 1)
2154 gint vflist_maint_renamed(ViewFile *vf, FileData *fd)
2160 if (g_list_index(vf->list, fd) < 0) return FALSE;
2162 source_base = remove_level_from_path(fd->change->source);
2163 dest_base = remove_level_from_path(fd->change->dest);
2166 if (strcmp(source_base, dest_base) == 0)
2168 GtkTreeStore *store;
2170 GtkTreeIter position;
2174 old_row = g_list_index(vf->list, fd);
2176 vf->list = g_list_remove(vf->list, fd);
2178 vf->list = filelist_insert_sort(vf->list, fd, vf->sort_method, vf->sort_ascend);
2179 n = g_list_index(vf->list, fd);
2181 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2182 if (vflist_find_row(vf, fd, &iter) >= 0 &&
2183 gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &position, NULL, n))
2187 gtk_tree_store_move_before(store, &iter, &position);
2191 gtk_tree_store_move_after(store, &iter, &position);
2194 gtk_tree_store_set(store, &iter, FILE_COLUMN_NAME, fd->name, -1);
2200 ret = vflist_maint_removed(vf, fd, NULL);
2203 g_free(source_base);
2209 gint vflist_maint_removed(ViewFile *vf, FileData *fd, GList *ignore_list)
2216 row = g_list_index(vf->list, fd);
2217 if (row < 0) return FALSE;
2219 if (vflist_index_is_selected(vf, row) &&
2220 layout_image_get_collection(vf->layout, NULL) == NULL)
2224 n = vf_count(vf, NULL);
2227 new_row = vflist_maint_find_closest(vf, row, n, ignore_list);
2228 DEBUG_1("row = %d, closest is %d", row, new_row);
2244 fd = vf_index_get_data(vf, new_row);
2245 if (vflist_find_row(vf, fd, &iter) >= 0)
2247 GtkTreeSelection *selection;
2249 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2250 gtk_tree_selection_select_iter(selection, &iter);
2251 vflist_move_cursor(vf, &iter);
2256 fd = vf_index_get_data(vf, row);
2257 if (vflist_find_row(vf, fd, &iter) >= 0)
2259 GtkTreeStore *store;
2260 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2261 gtk_tree_store_remove(store, &iter);
2263 list = g_list_nth(vf->list, row);
2266 /* thumbnail loader check */
2267 if (fd == vf->thumbs_filedata) vf->thumbs_filedata = NULL;
2268 if (vf->thumbs_count > 0) vf->thumbs_count--;
2270 vf->list = g_list_remove(vf->list, fd);
2271 file_data_unref(fd);
2278 gint vflist_maint_moved(ViewFile *vf, FileData *fd, GList *ignore_list)
2283 if (!fd->change->source || !vf->path) return FALSE;
2285 buf = remove_level_from_path(fd->change->source);
2287 if (strcmp(buf, vf->path) == 0)
2289 ret = vflist_maint_removed(vf, fd, ignore_list);