Mercurial > hg > forks > geeqie
changeset 2521:3a586dd0188a
Refactor: move view_file implementations to their own subdirectory.
author | Omari Stephens <xsdg@google.com> |
---|---|
date | Wed, 05 Jul 2017 21:41:18 +0000 |
parents | 8057b4b9fe34 |
children | 5dfc01705fb1 |
files | src/Makefile.am src/view_file.c src/view_file/Makefile.am src/view_file/view_file_icon.c src/view_file/view_file_icon.h src/view_file/view_file_list.c src/view_file/view_file_list.h src/view_file_icon.c src/view_file_icon.h src/view_file_list.c src/view_file_list.h |
diffstat | 11 files changed, 4513 insertions(+), 4510 deletions(-) [+] |
line wrap: on
line diff
--- a/src/Makefile.am Thu Jun 29 11:05:59 2017 +0100 +++ b/src/Makefile.am Wed Jul 05 21:41:18 2017 +0000 @@ -81,12 +81,14 @@ $(extra_ICONS) include $(srcdir)/pan-view/Makefile.am +include $(srcdir)/view_file/Makefile.am bin_PROGRAMS = geeqie geeqie_SOURCES = \ $(module_SLIK) \ $(module_pan_view) \ + $(module_view_file) \ ClayRGB1998.icc \ ClayRGB1998_icc.h \ advanced_exif.c \ @@ -251,10 +253,6 @@ view_dir_tree.h \ view_file.c \ view_file.h \ - view_file_list.c \ - view_file_list.h \ - view_file_icon.c \ - view_file_icon.h \ window.c \ window.h \ lua.c \
--- a/src/view_file.c Thu Jun 29 11:05:59 2017 +0100 +++ b/src/view_file.c Wed Jul 05 21:41:18 2017 +0000 @@ -31,8 +31,8 @@ #include "ui_menu.h" #include "ui_fileops.h" #include "utilops.h" -#include "view_file_list.h" -#include "view_file_icon.h" +#include "view_file/view_file_list.h" +#include "view_file/view_file_icon.h" /* *-----------------------------------------------------------------------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/view_file/Makefile.am Wed Jul 05 21:41:18 2017 +0000 @@ -0,0 +1,5 @@ +module_view_file = \ + %D%/view_file_icon.c \ + %D%/view_file_icon.h \ + %D%/view_file_list.c \ + %D%/view_file_list.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/view_file/view_file_icon.c Wed Jul 05 21:41:18 2017 +0000 @@ -0,0 +1,2300 @@ +/* + * Copyright (C) 2006 John Ellis + * Copyright (C) 2008 - 2016 The Geeqie Team + * + * Author: John Ellis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "main.h" +#include "view_file_icon.h" + +#include "bar.h" +#include "cellrenderericon.h" +#include "collect.h" +#include "collect-io.h" +#include "collect-table.h" +#include "dnd.h" +#include "editors.h" +#include "img-view.h" +#include "filedata.h" +#include "layout.h" +#include "layout_image.h" +#include "menu.h" +#include "metadata.h" +#include "thumb.h" +#include "utilops.h" +#include "ui_fileops.h" +#include "ui_menu.h" +#include "ui_tree_edit.h" +#include "uri_utils.h" +#include "view_file.h" + +#include <gdk/gdkkeysyms.h> /* for keyboard values */ + + +/* between these, the icon width is increased by thumb_max_width / 2 */ +#define THUMB_MIN_ICON_WIDTH 128 +#define THUMB_MAX_ICON_WIDTH 150 + +#define VFICON_MAX_COLUMNS 32 +#define THUMB_BORDER_PADDING 2 + +#define VFICON_TIP_DELAY 500 + +enum { + FILE_COLUMN_POINTER = 0, + FILE_COLUMN_COUNT +}; + +static gint vficon_index_by_id(ViewFile *vf, IconData *in_id); + +static IconData *vficon_icon_data(ViewFile *vf, FileData *fd) +{ + IconData *id = NULL; + GList *work; + + if (!fd) return NULL; + work = vf->list; + while (work && !id) + { + IconData *chk = work->data; + work = work->next; + if (chk->fd == fd) id = chk; + } + return id; +} + +static void iconlist_free(GList *list) +{ + GList *work = list; + while (work) + { + IconData *id = work->data; + file_data_unref(id->fd); + g_free(id); + work = work->next; + } + + g_list_free(list); + +} + +gint iconlist_sort_file_cb(gpointer a, gpointer b) +{ + IconData *ida = a; + IconData *idb = b; + return filelist_sort_compare_filedata(ida->fd, idb->fd); +} + +GList *iconlist_sort(GList *list, SortType method, gboolean ascend) +{ + return filelist_sort_full(list, method, ascend, (GCompareFunc) iconlist_sort_file_cb); +} + +GList *iconlist_insert_sort(GList *list, IconData *id, SortType method, gboolean ascend) +{ + return filelist_insert_sort_full(list, id, method, ascend, (GCompareFunc) iconlist_sort_file_cb); +} + + +static void vficon_toggle_filenames(ViewFile *vf); +static void vficon_selection_remove(ViewFile *vf, IconData *id, SelectionType mask, GtkTreeIter *iter); +static void vficon_move_focus(ViewFile *vf, gint row, gint col, gboolean relative); +static void vficon_set_focus(ViewFile *vf, IconData *id); +static void vficon_populate_at_new_size(ViewFile *vf, gint w, gint h, gboolean force); + + +/* + *----------------------------------------------------------------------------- + * pop-up menu + *----------------------------------------------------------------------------- + */ + +GList *vficon_selection_get_one(ViewFile *vf, FileData *fd) +{ + return g_list_prepend(filelist_copy(fd->sidecar_files), file_data_ref(fd)); +} + +GList *vficon_pop_menu_file_list(ViewFile *vf) +{ + if (!VFICON(vf)->click_id) return NULL; + + if (VFICON(vf)->click_id->selected & SELECTION_SELECTED) + { + return vf_selection_get_list(vf); + } + + return vficon_selection_get_one(vf, VFICON(vf)->click_id->fd); +} + +void vficon_pop_menu_view_cb(GtkWidget *widget, gpointer data) +{ + ViewFile *vf = data; + + if (!VFICON(vf)->click_id) return; + + if (VFICON(vf)->click_id->selected & SELECTION_SELECTED) + { + GList *list; + + list = vf_selection_get_list(vf); + view_window_new_from_list(list); + filelist_free(list); + } + else + { + view_window_new(VFICON(vf)->click_id->fd); + } +} + +void vficon_pop_menu_rename_cb(GtkWidget *widget, gpointer data) +{ + ViewFile *vf = data; + + file_util_rename(NULL, vf_pop_menu_file_list(vf), vf->listview); +} + +void vficon_pop_menu_show_names_cb(GtkWidget *widget, gpointer data) +{ + ViewFile *vf = data; + + vficon_toggle_filenames(vf); +} + +void vficon_pop_menu_refresh_cb(GtkWidget *widget, gpointer data) +{ + ViewFile *vf = data; + + vf_refresh(vf); +} + +void vficon_popup_destroy_cb(GtkWidget *widget, gpointer data) +{ + ViewFile *vf = data; + vficon_selection_remove(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, NULL); + VFICON(vf)->click_id = NULL; + vf->popup = NULL; +} + +/* + *------------------------------------------------------------------- + * signals + *------------------------------------------------------------------- + */ + +static void vficon_send_layout_select(ViewFile *vf, IconData *id) +{ + FileData *read_ahead_fd = NULL; + FileData *sel_fd; + FileData *cur_fd; + + if (!vf->layout || !id || !id->fd) return; + + sel_fd = id->fd; + + cur_fd = layout_image_get_fd(vf->layout); + if (sel_fd == cur_fd) return; /* no change */ + + if (options->image.enable_read_ahead) + { + gint row; + + row = g_list_index(vf->list, id); + if (row > vficon_index_by_fd(vf, cur_fd) && + (guint) (row + 1) < vf_count(vf, NULL)) + { + read_ahead_fd = vf_index_get_data(vf, row + 1); + } + else if (row > 0) + { + read_ahead_fd = vf_index_get_data(vf, row - 1); + } + } + + layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd); +} + +static void vficon_toggle_filenames(ViewFile *vf) +{ + GtkAllocation allocation; + VFICON(vf)->show_text = !VFICON(vf)->show_text; + options->show_icon_names = VFICON(vf)->show_text; + + gtk_widget_get_allocation(vf->listview, &allocation); + vficon_populate_at_new_size(vf, allocation.width, allocation.height, TRUE); +} + +static gint vficon_get_icon_width(ViewFile *vf) +{ + gint width; + + if (!VFICON(vf)->show_text) return options->thumbnails.max_width; + + width = options->thumbnails.max_width + options->thumbnails.max_width / 2; + if (width < THUMB_MIN_ICON_WIDTH) width = THUMB_MIN_ICON_WIDTH; + if (width > THUMB_MAX_ICON_WIDTH) width = options->thumbnails.max_width; + + return width; +} + +/* + *------------------------------------------------------------------- + * misc utils + *------------------------------------------------------------------- + */ + +static gboolean vficon_find_position(ViewFile *vf, IconData *id, gint *row, gint *col) +{ + gint n; + + n = g_list_index(vf->list, id); + + if (n < 0) return FALSE; + + *row = n / VFICON(vf)->columns; + *col = n - (*row * VFICON(vf)->columns); + + return TRUE; +} + +static gboolean vficon_find_iter(ViewFile *vf, IconData *id, GtkTreeIter *iter, gint *column) +{ + GtkTreeModel *store; + gint row, col; + + store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); + if (!vficon_find_position(vf, id, &row, &col)) return FALSE; + if (!gtk_tree_model_iter_nth_child(store, iter, NULL, row)) return FALSE; + if (column) *column = col; + + return TRUE; +} + +static IconData *vficon_find_data(ViewFile *vf, gint row, gint col, GtkTreeIter *iter) +{ + GtkTreeModel *store; + GtkTreeIter p; + + if (row < 0 || col < 0) return NULL; + + store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); + if (gtk_tree_model_iter_nth_child(store, &p, NULL, row)) + { + GList *list; + + gtk_tree_model_get(store, &p, FILE_COLUMN_POINTER, &list, -1); + if (!list) return NULL; + + if (iter) *iter = p; + + return g_list_nth_data(list, col); + } + + return NULL; +} + +static IconData *vficon_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter) +{ + GtkTreePath *tpath; + GtkTreeViewColumn *column; + + if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), x, y, + &tpath, &column, NULL, NULL)) + { + GtkTreeModel *store; + GtkTreeIter row; + GList *list; + gint n; + + store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); + gtk_tree_model_get_iter(store, &row, tpath); + gtk_tree_path_free(tpath); + + gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &list, -1); + + n = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_number")); + if (list) + { + if (iter) *iter = row; + return g_list_nth_data(list, n); + } + } + + return NULL; +} + +static void vficon_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data) +{ + ViewFile *vf = data; + GtkTreeModel *store; + GtkTreePath *path = gtk_tree_path_new_from_string(path_str); + GtkTreeIter row; + gint column; + GList *list; + guint toggled_mark; + IconData *id; + + store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); + if (!path || !gtk_tree_model_get_iter(store, &row, path)) + return; + + gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &list, -1); + + column = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_number")); + g_object_get(G_OBJECT(cell), "toggled_mark", &toggled_mark, NULL); + + id = g_list_nth_data(list, column); + if (id) + { + FileData *fd = id->fd; + file_data_set_mark(fd, toggled_mark, !file_data_get_mark(fd, toggled_mark)); + } +} + + +/* + *------------------------------------------------------------------- + * tooltip type window + *------------------------------------------------------------------- + */ + +static void tip_show(ViewFile *vf) +{ + GtkWidget *label; + gint x, y; +#if GTK_CHECK_VERSION(3,0,0) + GdkDisplay *display; + GdkDeviceManager *device_manager; + GdkDevice *device; +#endif + + if (VFICON(vf)->tip_window) return; + +#if GTK_CHECK_VERSION(3,0,0) + device_manager = gdk_display_get_device_manager(gdk_window_get_display( + gtk_tree_view_get_bin_window(GTK_TREE_VIEW(vf->listview)))); + device = gdk_device_manager_get_client_pointer(device_manager); + gdk_window_get_device_position(gtk_tree_view_get_bin_window(GTK_TREE_VIEW(vf->listview)), + device, &x, &y, NULL); +#else + gdk_window_get_pointer(gtk_tree_view_get_bin_window(GTK_TREE_VIEW(vf->listview)), &x, &y, NULL); +#endif + + VFICON(vf)->tip_id = vficon_find_data_by_coord(vf, x, y, NULL); + if (!VFICON(vf)->tip_id) return; + + VFICON(vf)->tip_window = gtk_window_new(GTK_WINDOW_POPUP); + gtk_window_set_resizable(GTK_WINDOW(VFICON(vf)->tip_window), FALSE); + gtk_container_set_border_width(GTK_CONTAINER(VFICON(vf)->tip_window), 2); + + label = gtk_label_new(VFICON(vf)->tip_id->fd->name); + + g_object_set_data(G_OBJECT(VFICON(vf)->tip_window), "tip_label", label); + gtk_container_add(GTK_CONTAINER(VFICON(vf)->tip_window), label); + gtk_widget_show(label); + +#if GTK_CHECK_VERSION(3,0,0) + display = gdk_display_get_default(); + device_manager = gdk_display_get_device_manager(display); + device = gdk_device_manager_get_client_pointer(device_manager); + gdk_device_get_position(device, NULL, &x, &y); +#else + gdk_window_get_pointer(NULL, &x, &y, NULL); +#endif + + if (!gtk_widget_get_realized(VFICON(vf)->tip_window)) gtk_widget_realize(VFICON(vf)->tip_window); + gtk_window_move(GTK_WINDOW(VFICON(vf)->tip_window), x + 16, y + 16); + gtk_widget_show(VFICON(vf)->tip_window); +} + +static void tip_hide(ViewFile *vf) +{ + if (VFICON(vf)->tip_window) gtk_widget_destroy(VFICON(vf)->tip_window); + VFICON(vf)->tip_window = NULL; +} + +static gboolean tip_schedule_cb(gpointer data) +{ + ViewFile *vf = data; + GtkWidget *window; + + if (!VFICON(vf)->tip_delay_id) return FALSE; + + window = gtk_widget_get_toplevel(vf->listview); + + if (gtk_widget_get_sensitive(window) && + gtk_window_has_toplevel_focus(GTK_WINDOW(window))) + { + tip_show(vf); + } + + VFICON(vf)->tip_delay_id = 0; + return FALSE; +} + +static void tip_schedule(ViewFile *vf) +{ + tip_hide(vf); + + if (VFICON(vf)->tip_delay_id) + { + g_source_remove(VFICON(vf)->tip_delay_id); + VFICON(vf)->tip_delay_id = 0; + } + + if (!VFICON(vf)->show_text) + { + VFICON(vf)->tip_delay_id = g_timeout_add(VFICON_TIP_DELAY, tip_schedule_cb, vf); + } +} + +static void tip_unschedule(ViewFile *vf) +{ + tip_hide(vf); + + if (VFICON(vf)->tip_delay_id) + { + g_source_remove(VFICON(vf)->tip_delay_id); + VFICON(vf)->tip_delay_id = 0; + } +} + +static void tip_update(ViewFile *vf, IconData *id) +{ +#if GTK_CHECK_VERSION(3,0,0) + GdkDisplay *display = gdk_display_get_default(); + GdkDeviceManager *device_manager = gdk_display_get_device_manager(display); + GdkDevice *device = gdk_device_manager_get_client_pointer(device_manager); +#endif + + if (VFICON(vf)->tip_window) + { + gint x, y; + +#if GTK_CHECK_VERSION(3,0,0) + gdk_device_get_position(device, NULL, &x, &y); +#else + gdk_window_get_pointer(NULL, &x, &y, NULL); +#endif + gtk_window_move(GTK_WINDOW(VFICON(vf)->tip_window), x + 16, y + 16); + + if (id != VFICON(vf)->tip_id) + { + GtkWidget *label; + + VFICON(vf)->tip_id = id; + + if (!VFICON(vf)->tip_id) + { + tip_hide(vf); + tip_schedule(vf); + return; + } + + label = g_object_get_data(G_OBJECT(VFICON(vf)->tip_window), "tip_label"); + gtk_label_set_text(GTK_LABEL(label), VFICON(vf)->tip_id->fd->name); + } + } + else + { + tip_schedule(vf); + } +} + +/* + *------------------------------------------------------------------- + * dnd + *------------------------------------------------------------------- + */ + +static void vficon_dnd_get(GtkWidget *widget, GdkDragContext *context, + GtkSelectionData *selection_data, guint info, + guint time, gpointer data) +{ + ViewFile *vf = data; + GList *list = NULL; + + if (!VFICON(vf)->click_id) return; + + if (VFICON(vf)->click_id->selected & SELECTION_SELECTED) + { + list = vf_selection_get_list(vf); + } + else + { + list = g_list_append(NULL, file_data_ref(VFICON(vf)->click_id->fd)); + } + + if (!list) return; + uri_selection_data_set_uris_from_filelist(selection_data, list); + filelist_free(list); +} + +static void vficon_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context, + int x, int y, GtkSelectionData *selection, + guint info, guint time, gpointer data) +{ + ViewFile *vf = data; + + if (info == TARGET_TEXT_PLAIN) { + IconData *id = vficon_find_data_by_coord(vf, x, y, NULL); + + if (id && id->fd) { + /* Add keywords to file */ + FileData *fd = id->fd; + gchar *str = (gchar *) gtk_selection_data_get_text(selection); + GList *kw_list = string_to_keywords_list(str); + + metadata_append_list(fd, KEYWORD_KEY, kw_list); + string_list_free(kw_list); + g_free(str); + } + } +} + +static void vficon_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data) +{ + ViewFile *vf = data; + + tip_unschedule(vf); + + if (VFICON(vf)->click_id && VFICON(vf)->click_id->fd->thumb_pixbuf) + { + gint items; + + if (VFICON(vf)->click_id->selected & SELECTION_SELECTED) + items = g_list_length(VFICON(vf)->selection); + else + items = 1; + + dnd_set_drag_icon(widget, context, VFICON(vf)->click_id->fd->thumb_pixbuf, items); + } +} + +static void vficon_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data) +{ + ViewFile *vf = data; + + vficon_selection_remove(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, NULL); + + if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE) + { + vf_refresh(vf); + } + + tip_unschedule(vf); +} + +void vficon_dnd_init(ViewFile *vf) +{ + gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK, + dnd_file_drag_types, dnd_file_drag_types_count, + GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK); + gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL, + dnd_file_drag_types, dnd_file_drag_types_count, + GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK); + + g_signal_connect(G_OBJECT(vf->listview), "drag_data_get", + G_CALLBACK(vficon_dnd_get), vf); + g_signal_connect(G_OBJECT(vf->listview), "drag_begin", + G_CALLBACK(vficon_dnd_begin), vf); + g_signal_connect(G_OBJECT(vf->listview), "drag_end", + G_CALLBACK(vficon_dnd_end), vf); + g_signal_connect(G_OBJECT(vf->listview), "drag_data_received", + G_CALLBACK(vficon_drag_data_received), vf); +} + +/* + *------------------------------------------------------------------- + * cell updates + *------------------------------------------------------------------- + */ + +static void vficon_selection_set(ViewFile *vf, IconData *id, SelectionType value, GtkTreeIter *iter) +{ + GtkTreeModel *store; + GList *list; + + if (!id) return; + + + if (id->selected == value) return; + id->selected = value; + + store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); + if (iter) + { + gtk_tree_model_get(store, iter, FILE_COLUMN_POINTER, &list, -1); + if (list) gtk_list_store_set(GTK_LIST_STORE(store), iter, FILE_COLUMN_POINTER, list, -1); + } + else + { + GtkTreeIter row; + + if (vficon_find_iter(vf, id, &row, NULL)) + { + gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &list, -1); + if (list) gtk_list_store_set(GTK_LIST_STORE(store), &row, FILE_COLUMN_POINTER, list, -1); + } + } +} + +static void vficon_selection_add(ViewFile *vf, IconData *id, SelectionType mask, GtkTreeIter *iter) +{ + if (!id) return; + + vficon_selection_set(vf, id, id->selected | mask, iter); +} + +static void vficon_selection_remove(ViewFile *vf, IconData *id, SelectionType mask, GtkTreeIter *iter) +{ + if (!id) return; + + vficon_selection_set(vf, id, id->selected & ~mask, iter); +} + +void vficon_marks_set(ViewFile *vf, gint enable) +{ + GtkAllocation allocation; + gtk_widget_get_allocation(vf->listview, &allocation); + vficon_populate_at_new_size(vf, allocation.width, allocation.height, TRUE); +} + +/* + *------------------------------------------------------------------- + * selections + *------------------------------------------------------------------- + */ + +static void vficon_verify_selections(ViewFile *vf) +{ + GList *work; + + work = VFICON(vf)->selection; + while (work) + { + IconData *id = work->data; + work = work->next; + + if (vficon_index_by_id(vf, id) >= 0) continue; + + VFICON(vf)->selection = g_list_remove(VFICON(vf)->selection, id); + } +} + +void vficon_select_all(ViewFile *vf) +{ + GList *work; + + g_list_free(VFICON(vf)->selection); + VFICON(vf)->selection = NULL; + + work = vf->list; + while (work) + { + IconData *id = work->data; + work = work->next; + + VFICON(vf)->selection = g_list_append(VFICON(vf)->selection, id); + vficon_selection_add(vf, id, SELECTION_SELECTED, NULL); + } + + vf_send_update(vf); +} + +void vficon_select_none(ViewFile *vf) +{ + GList *work; + + work = VFICON(vf)->selection; + while (work) + { + IconData *id = work->data; + work = work->next; + + vficon_selection_remove(vf, id, SELECTION_SELECTED, NULL); + } + + g_list_free(VFICON(vf)->selection); + VFICON(vf)->selection = NULL; + + vf_send_update(vf); +} + +void vficon_select_invert(ViewFile *vf) +{ + GList *work; + + work = vf->list; + while (work) + { + IconData *id = work->data; + work = work->next; + + if (id->selected & SELECTION_SELECTED) + { + VFICON(vf)->selection = g_list_remove(VFICON(vf)->selection, id); + vficon_selection_remove(vf, id, SELECTION_SELECTED, NULL); + } + else + { + VFICON(vf)->selection = g_list_append(VFICON(vf)->selection, id); + vficon_selection_add(vf, id, SELECTION_SELECTED, NULL); + } + } + + vf_send_update(vf); +} + +static void vficon_select(ViewFile *vf, IconData *id) +{ + VFICON(vf)->prev_selection = id; + + if (!id || id->selected & SELECTION_SELECTED) return; + + VFICON(vf)->selection = g_list_append(VFICON(vf)->selection, id); + vficon_selection_add(vf, id, SELECTION_SELECTED, NULL); + + vf_send_update(vf); +} + +static void vficon_unselect(ViewFile *vf, IconData *id) +{ + VFICON(vf)->prev_selection = id; + + if (!id || !(id->selected & SELECTION_SELECTED) ) return; + + VFICON(vf)->selection = g_list_remove(VFICON(vf)->selection, id); + vficon_selection_remove(vf, id, SELECTION_SELECTED, NULL); + + vf_send_update(vf); +} + +static void vficon_select_util(ViewFile *vf, IconData *id, gboolean select) +{ + if (select) + { + vficon_select(vf, id); + } + else + { + vficon_unselect(vf, id); + } +} + +static void vficon_select_region_util(ViewFile *vf, IconData *start, IconData *end, gboolean select) +{ + gint row1, col1; + gint row2, col2; + gint t; + gint i, j; + + if (!vficon_find_position(vf, start, &row1, &col1) || + !vficon_find_position(vf, end, &row2, &col2) ) return; + + VFICON(vf)->prev_selection = end; + + if (!options->collections.rectangular_selection) + { + GList *work; + IconData *id; + + if (g_list_index(vf->list, start) > g_list_index(vf->list, end)) + { + id = start; + start = end; + end = id; + } + + work = g_list_find(vf->list, start); + while (work) + { + id = work->data; + vficon_select_util(vf, id, select); + + if (work->data != end) + work = work->next; + else + work = NULL; + } + return; + } + + if (row2 < row1) + { + t = row1; + row1 = row2; + row2 = t; + } + if (col2 < col1) + { + t = col1; + col1 = col2; + col2 = t; + } + + DEBUG_1("table: %d x %d to %d x %d", row1, col1, row2, col2); + + for (i = row1; i <= row2; i++) + { + for (j = col1; j <= col2; j++) + { + IconData *id = vficon_find_data(vf, i, j, NULL); + if (id) vficon_select_util(vf, id, select); + } + } +} + +gboolean vficon_index_is_selected(ViewFile *vf, gint row) +{ + IconData *id = g_list_nth_data(vf->list, row); + + if (!id) return FALSE; + + return (id->selected & SELECTION_SELECTED); +} + +guint vficon_selection_count(ViewFile *vf, gint64 *bytes) +{ + if (bytes) + { + gint64 b = 0; + GList *work; + + work = VFICON(vf)->selection; + while (work) + { + IconData *id = work->data; + FileData *fd = id->fd; + g_assert(fd->magick == FD_MAGICK); + b += fd->size; + + work = work->next; + } + + *bytes = b; + } + + return g_list_length(VFICON(vf)->selection); +} + +GList *vficon_selection_get_list(ViewFile *vf) +{ + GList *list = NULL; + GList *work, *work2; + + work = VFICON(vf)->selection; + while (work) + { + IconData *id = work->data; + FileData *fd = id->fd; + g_assert(fd->magick == FD_MAGICK); + + list = g_list_prepend(list, file_data_ref(fd)); + + work2 = fd->sidecar_files; + while (work2) + { + fd = work2->data; + list = g_list_prepend(list, file_data_ref(fd)); + work2 = work2->next; + } + + work = work->next; + } + + list = g_list_reverse(list); + + return list; +} + +GList *vficon_selection_get_list_by_index(ViewFile *vf) +{ + GList *list = NULL; + GList *work; + + work = VFICON(vf)->selection; + while (work) + { + list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, work->data))); + work = work->next; + } + + return g_list_reverse(list); +} + +static void vficon_select_by_id(ViewFile *vf, IconData *id) +{ + if (!id) return; + + if (!(id->selected & SELECTION_SELECTED)) + { + vf_select_none(vf); + vficon_select(vf, id); + } + + vficon_set_focus(vf, id); +} + +void vficon_select_by_fd(ViewFile *vf, FileData *fd) +{ + IconData *id = NULL; + GList *work; + + if (!fd) return; + work = vf->list; + while (work && !id) + { + IconData *chk = work->data; + work = work->next; + if (chk->fd == fd) id = chk; + } + vficon_select_by_id(vf, id); +} + +void vficon_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode) +{ + GList *work; + gint n = mark - 1; + + g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE); + + work = vf->list; + while (work) + { + IconData *id = work->data; + FileData *fd = id->fd; + gboolean mark_val, selected; + + g_assert(fd->magick == FD_MAGICK); + + mark_val = file_data_get_mark(fd, n); + selected = (id->selected & SELECTION_SELECTED); + + switch (mode) + { + case MTS_MODE_SET: selected = mark_val; + break; + case MTS_MODE_OR: selected = mark_val || selected; + break; + case MTS_MODE_AND: selected = mark_val && selected; + break; + case MTS_MODE_MINUS: selected = !mark_val && selected; + break; + } + + vficon_select_util(vf, id, selected); + + work = work->next; + } +} + +void vficon_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode) +{ + GList *slist; + GList *work; + gint n = mark -1; + + g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE); + + slist = vf_selection_get_list(vf); + work = slist; + while (work) + { + FileData *fd = work->data; + + switch (mode) + { + case STM_MODE_SET: file_data_set_mark(fd, n, 1); + break; + case STM_MODE_RESET: file_data_set_mark(fd, n, 0); + break; + case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n)); + break; + } + work = work->next; + } + filelist_free(slist); +} + +static void vficon_select_closest(ViewFile *vf, FileData *sel_fd) +{ + GList *work; + IconData *id = NULL; + + if (sel_fd->parent) sel_fd = sel_fd->parent; + work = vf->list; + + while (work) + { + gint match; + FileData *fd; + + id = work->data; + fd = id->fd; + work = work->next; + + match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend); + + if (match >= 0) break; + } + + if (id) + { + vficon_select(vf, id); + vficon_send_layout_select(vf, id); + } +} + + +/* + *------------------------------------------------------------------- + * focus + *------------------------------------------------------------------- + */ + +static void vficon_move_focus(ViewFile *vf, gint row, gint col, gboolean relative) +{ + gint new_row; + gint new_col; + + if (relative) + { + new_row = VFICON(vf)->focus_row; + new_col = VFICON(vf)->focus_column; + + new_row += row; + if (new_row < 0) new_row = 0; + if (new_row >= VFICON(vf)->rows) new_row = VFICON(vf)->rows - 1; + + while (col != 0) + { + if (col < 0) + { + new_col--; + col++; + } + else + { + new_col++; + col--; + } + + if (new_col < 0) + { + if (new_row > 0) + { + new_row--; + new_col = VFICON(vf)->columns - 1; + } + else + { + new_col = 0; + } + } + if (new_col >= VFICON(vf)->columns) + { + if (new_row < VFICON(vf)->rows - 1) + { + new_row++; + new_col = 0; + } + else + { + new_col = VFICON(vf)->columns - 1; + } + } + } + } + else + { + new_row = row; + new_col = col; + + if (new_row >= VFICON(vf)->rows) + { + if (VFICON(vf)->rows > 0) + new_row = VFICON(vf)->rows - 1; + else + new_row = 0; + new_col = VFICON(vf)->columns - 1; + } + if (new_col >= VFICON(vf)->columns) new_col = VFICON(vf)->columns - 1; + } + + if (new_row == VFICON(vf)->rows - 1) + { + gint l; + + /* if we moved beyond the last image, go to the last image */ + + l = g_list_length(vf->list); + if (VFICON(vf)->rows > 1) l -= (VFICON(vf)->rows - 1) * VFICON(vf)->columns; + if (new_col >= l) new_col = l - 1; + } + + vficon_set_focus(vf, vficon_find_data(vf, new_row, new_col, NULL)); +} + +static void vficon_set_focus(ViewFile *vf, IconData *id) +{ + GtkTreeIter iter; + gint row, col; + + if (g_list_find(vf->list, VFICON(vf)->focus_id)) + { + if (id == VFICON(vf)->focus_id) + { + /* ensure focus row col are correct */ + vficon_find_position(vf, VFICON(vf)->focus_id, &VFICON(vf)->focus_row, &VFICON(vf)->focus_column); +#if GTK_CHECK_VERSION(3,0,0) +/* FIXME: Refer to issue #467 on Github. The thumbnail position is not + * preserved when the icon view is refreshed. Caused by an unknown call from + * the idle loop. This patch hides the problem. + */ + if (vficon_find_iter(vf, VFICON(vf)->focus_id, &iter, NULL)) + { + tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, FALSE); + } +#endif + return; + } + vficon_selection_remove(vf, VFICON(vf)->focus_id, SELECTION_FOCUS, NULL); + } + + if (!vficon_find_position(vf, id, &row, &col)) + { + VFICON(vf)->focus_id = NULL; + VFICON(vf)->focus_row = -1; + VFICON(vf)->focus_column = -1; + return; + } + + VFICON(vf)->focus_id = id; + VFICON(vf)->focus_row = row; + VFICON(vf)->focus_column = col; + vficon_selection_add(vf, VFICON(vf)->focus_id, SELECTION_FOCUS, NULL); + + if (vficon_find_iter(vf, VFICON(vf)->focus_id, &iter, NULL)) + { + GtkTreePath *tpath; + GtkTreeViewColumn *column; + GtkTreeModel *store; + + tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, FALSE); + + store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); + tpath = gtk_tree_model_get_path(store, &iter); + /* focus is set to an extra column with 0 width to hide focus, we draw it ourself */ + column = gtk_tree_view_get_column(GTK_TREE_VIEW(vf->listview), VFICON_MAX_COLUMNS); + gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, column, FALSE); + gtk_tree_path_free(tpath); + } +} + +/* used to figure the page up/down distances */ +static gint page_height(ViewFile *vf) +{ + GtkAdjustment *adj; + gint page_size; + gint row_height; + gint ret; + + adj = gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(vf->listview)); + page_size = (gint)gtk_adjustment_get_page_increment(adj); + + row_height = options->thumbnails.max_height + THUMB_BORDER_PADDING * 2; + if (VFICON(vf)->show_text) row_height += options->thumbnails.max_height / 3; + + ret = page_size / row_height; + if (ret < 1) ret = 1; + + return ret; +} + +/* + *------------------------------------------------------------------- + * keyboard + *------------------------------------------------------------------- + */ + +static void vfi_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data) +{ + ViewFile *vf = data; + GtkTreeModel *store; + GtkTreeIter iter; + gint column; + GtkTreePath *tpath; + gint cw, ch; + + if (!vficon_find_iter(vf, VFICON(vf)->click_id, &iter, &column)) return; + store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); + tpath = gtk_tree_model_get_path(store, &iter); + tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, column, FALSE, x, y, &cw, &ch); + gtk_tree_path_free(tpath); + *y += ch; + popup_menu_position_clamp(menu, x, y, 0); +} + +gboolean vficon_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data) +{ + ViewFile *vf = data; + gint focus_row = 0; + gint focus_col = 0; + IconData *id; + gboolean stop_signal; + + stop_signal = TRUE; + switch (event->keyval) + { + case GDK_KEY_Left: case GDK_KEY_KP_Left: + focus_col = -1; + break; + case GDK_KEY_Right: case GDK_KEY_KP_Right: + focus_col = 1; + break; + case GDK_KEY_Up: case GDK_KEY_KP_Up: + focus_row = -1; + break; + case GDK_KEY_Down: case GDK_KEY_KP_Down: + focus_row = 1; + break; + case GDK_KEY_Page_Up: case GDK_KEY_KP_Page_Up: + focus_row = -page_height(vf); + break; + case GDK_KEY_Page_Down: case GDK_KEY_KP_Page_Down: + focus_row = page_height(vf); + break; + case GDK_KEY_Home: case GDK_KEY_KP_Home: + focus_row = -VFICON(vf)->focus_row; + focus_col = -VFICON(vf)->focus_column; + break; + case GDK_KEY_End: case GDK_KEY_KP_End: + focus_row = VFICON(vf)->rows - 1 - VFICON(vf)->focus_row; + focus_col = VFICON(vf)->columns - 1 - VFICON(vf)->focus_column; + break; + case GDK_KEY_space: + id = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL); + if (id) + { + VFICON(vf)->click_id = id; + if (event->state & GDK_CONTROL_MASK) + { + gint selected; + + selected = id->selected & SELECTION_SELECTED; + if (selected) + { + vficon_unselect(vf, id); + } + else + { + vficon_select(vf, id); + vficon_send_layout_select(vf, id); + } + } + else + { + vf_select_none(vf); + vficon_select(vf, id); + vficon_send_layout_select(vf, id); + } + } + break; + case GDK_KEY_Menu: + id = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL); + VFICON(vf)->click_id = id; + + vficon_selection_add(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, NULL); + tip_unschedule(vf); + + vf->popup = vf_pop_menu(vf); + gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vfi_menu_position_cb, vf, 0, GDK_CURRENT_TIME); + break; + default: + stop_signal = FALSE; + break; + } + + if (focus_row != 0 || focus_col != 0) + { + IconData *new_id; + IconData *old_id; + + old_id = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL); + vficon_move_focus(vf, focus_row, focus_col, TRUE); + new_id = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL); + + if (new_id != old_id) + { + if (event->state & GDK_SHIFT_MASK) + { + if (!options->collections.rectangular_selection) + { + vficon_select_region_util(vf, old_id, new_id, FALSE); + } + else + { + vficon_select_region_util(vf, VFICON(vf)->click_id, old_id, FALSE); + } + vficon_select_region_util(vf, VFICON(vf)->click_id, new_id, TRUE); + vficon_send_layout_select(vf, new_id); + } + else if (event->state & GDK_CONTROL_MASK) + { + VFICON(vf)->click_id = new_id; + } + else + { + VFICON(vf)->click_id = new_id; + vf_select_none(vf); + vficon_select(vf, new_id); + vficon_send_layout_select(vf, new_id); + } + } + } + + if (stop_signal) + { + tip_unschedule(vf); + } + + return stop_signal; +} + +/* + *------------------------------------------------------------------- + * mouse + *------------------------------------------------------------------- + */ + +static gboolean vficon_motion_cb(GtkWidget *widget, GdkEventMotion *event, gpointer data) +{ + ViewFile *vf = data; + IconData *id; + + id = vficon_find_data_by_coord(vf, (gint)event->x, (gint)event->y, NULL); + tip_update(vf, id); + + return FALSE; +} + +gboolean vficon_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data) +{ + ViewFile *vf = data; + GtkTreeIter iter; + IconData *id; + + tip_unschedule(vf); + + id = vficon_find_data_by_coord(vf, (gint)bevent->x, (gint)bevent->y, &iter); + + VFICON(vf)->click_id = id; + vficon_selection_add(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, &iter); + + switch (bevent->button) + { + case MOUSE_BUTTON_LEFT: + if (!gtk_widget_has_focus(vf->listview)) + { + gtk_widget_grab_focus(vf->listview); + } + + if (bevent->type == GDK_2BUTTON_PRESS && + vf->layout) + { + vficon_selection_remove(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, &iter); + layout_image_full_screen_start(vf->layout); + } + break; + case MOUSE_BUTTON_RIGHT: + vf->popup = vf_pop_menu(vf); + gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL, bevent->button, bevent->time); + break; + default: + break; + } + + return FALSE; +} + +gboolean vficon_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data) +{ + ViewFile *vf = data; + GtkTreeIter iter; + IconData *id = NULL; + gboolean was_selected; + + tip_schedule(vf); + + if ((gint)bevent->x != 0 || (gint)bevent->y != 0) + { + id = vficon_find_data_by_coord(vf, (gint)bevent->x, (gint)bevent->y, &iter); + } + + if (VFICON(vf)->click_id) + { + vficon_selection_remove(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, NULL); + } + + if (!id || VFICON(vf)->click_id != id) return TRUE; + + was_selected = !!(id->selected & SELECTION_SELECTED); + + switch (bevent->button) + { + case MOUSE_BUTTON_LEFT: + { + vficon_set_focus(vf, id); + + if (bevent->state & GDK_CONTROL_MASK) + { + gboolean select; + + select = !(id->selected & SELECTION_SELECTED); + if ((bevent->state & GDK_SHIFT_MASK) && VFICON(vf)->prev_selection) + { + vficon_select_region_util(vf, VFICON(vf)->prev_selection, id, select); + } + else + { + vficon_select_util(vf, id, select); + } + } + else + { + vf_select_none(vf); + + if ((bevent->state & GDK_SHIFT_MASK) && VFICON(vf)->prev_selection) + { + vficon_select_region_util(vf, VFICON(vf)->prev_selection, id, TRUE); + } + else + { + vficon_select_util(vf, id, TRUE); + was_selected = FALSE; + } + } + } + break; + case MOUSE_BUTTON_MIDDLE: + { + vficon_select_util(vf, id, !(id->selected & SELECTION_SELECTED)); + } + break; + default: + break; + } + + if (!was_selected && (id->selected & SELECTION_SELECTED)) + { + vficon_send_layout_select(vf, id); + } + + return TRUE; +} + +static gboolean vficon_leave_cb(GtkWidget *widget, GdkEventCrossing *event, gpointer data) +{ + ViewFile *vf = data; + + tip_unschedule(vf); + return FALSE; +} + +/* + *------------------------------------------------------------------- + * population + *------------------------------------------------------------------- + */ + +static gboolean vficon_destroy_node_cb(GtkTreeModel *store, GtkTreePath *tpath, GtkTreeIter *iter, gpointer data) +{ + GList *list; + + gtk_tree_model_get(store, iter, FILE_COLUMN_POINTER, &list, -1); + + /* it seems that gtk_list_store_clear may call some callbacks + that use the column. Set the pointer to NULL to be safe. */ + gtk_list_store_set(GTK_LIST_STORE(store), iter, FILE_COLUMN_POINTER, NULL, -1); + g_list_free(list); + + return FALSE; +} + +static void vficon_clear_store(ViewFile *vf) +{ + GtkTreeModel *store; + + store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); + gtk_tree_model_foreach(store, vficon_destroy_node_cb, NULL); + + gtk_list_store_clear(GTK_LIST_STORE(store)); +} + +static GList *vficon_add_row(ViewFile *vf, GtkTreeIter *iter) +{ + GtkListStore *store; + GList *list = NULL; + gint i; + + for (i = 0; i < VFICON(vf)->columns; i++) list = g_list_prepend(list, NULL); + + store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview))); + gtk_list_store_append(store, iter); + gtk_list_store_set(store, iter, FILE_COLUMN_POINTER, list, -1); + + return list; +} + +static void vficon_populate(ViewFile *vf, gboolean resize, gboolean keep_position) +{ + GtkTreeModel *store; + GtkTreePath *tpath; + GList *work; + IconData *visible_id = NULL; + gint r, c; + gboolean valid; + GtkTreeIter iter; + + vficon_verify_selections(vf); + + store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); + + if (keep_position && gtk_widget_get_realized(vf->listview) && + gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL)) + { + GtkTreeIter iter; + GList *list; + + gtk_tree_model_get_iter(store, &iter, tpath); + gtk_tree_path_free(tpath); + + gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1); + if (list) visible_id = list->data; + } + + + if (resize) + { + gint i; + gint thumb_width; + + vficon_clear_store(vf); + + thumb_width = vficon_get_icon_width(vf); + + for (i = 0; i < VFICON_MAX_COLUMNS; i++) + { + GtkTreeViewColumn *column; + GtkCellRenderer *cell; + GList *list; + + column = gtk_tree_view_get_column(GTK_TREE_VIEW(vf->listview), i); + gtk_tree_view_column_set_visible(column, (i < VFICON(vf)->columns)); + gtk_tree_view_column_set_fixed_width(column, thumb_width + (THUMB_BORDER_PADDING * 6)); + + list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column)); + cell = (list) ? list->data : NULL; + g_list_free(list); + + if (cell && GQV_IS_CELL_RENDERER_ICON(cell)) + { + g_object_set(G_OBJECT(cell), "fixed_width", thumb_width, + "fixed_height", options->thumbnails.max_height, + "show_text", VFICON(vf)->show_text, + "show_marks", vf->marks_enabled, + "num_marks", FILEDATA_MARKS_SIZE, + NULL); + } + } + if (gtk_widget_get_realized(vf->listview)) gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview)); + } + + r = -1; + c = 0; + + valid = gtk_tree_model_iter_children(store, &iter, NULL); + + work = vf->list; + while (work) + { + GList *list; + r++; + c = 0; + if (valid) + { + gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1); + gtk_list_store_set(GTK_LIST_STORE(store), &iter, FILE_COLUMN_POINTER, list, -1); + } + else + { + list = vficon_add_row(vf, &iter); + } + + while (list) + { + IconData *id; + + if (work) + { + id = work->data; + work = work->next; + c++; + } + else + { + id = NULL; + } + + list->data = id; + list = list->next; + } + if (valid) valid = gtk_tree_model_iter_next(store, &iter); + } + + r++; + while (valid) + { + GList *list; + + gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1); + valid = gtk_list_store_remove(GTK_LIST_STORE(store), &iter); + g_list_free(list); + } + + VFICON(vf)->rows = r; + + if (visible_id && + gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL)) + { + GtkTreeIter iter; + GList *list; + + gtk_tree_model_get_iter(store, &iter, tpath); + gtk_tree_path_free(tpath); + + gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1); + if (g_list_find(list, visible_id) == NULL && + vficon_find_iter(vf, visible_id, &iter, NULL)) + { + tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, FALSE); + } + } + + + vf_send_update(vf); + vf_thumb_update(vf); +} + +static void vficon_populate_at_new_size(ViewFile *vf, gint w, gint h, gboolean force) +{ + gint new_cols; + gint thumb_width; + + thumb_width = vficon_get_icon_width(vf); + + new_cols = w / (thumb_width + (THUMB_BORDER_PADDING * 6)); + if (new_cols < 1) new_cols = 1; + + if (!force && new_cols == VFICON(vf)->columns) return; + + VFICON(vf)->columns = new_cols; + + vficon_populate(vf, TRUE, TRUE); + + DEBUG_1("col tab pop cols=%d rows=%d", VFICON(vf)->columns, VFICON(vf)->rows); +} + +static void vficon_sized_cb(GtkWidget *widget, GtkAllocation *allocation, gpointer data) +{ + ViewFile *vf = data; + + vficon_populate_at_new_size(vf, allocation->width, allocation->height, FALSE); +} + +/* + *----------------------------------------------------------------------------- + * misc + *----------------------------------------------------------------------------- + */ + +void vficon_sort_set(ViewFile *vf, SortType type, gboolean ascend) +{ + if (vf->sort_method == type && vf->sort_ascend == ascend) return; + + vf->sort_method = type; + vf->sort_ascend = ascend; + + if (!vf->list) return; + + vf_refresh(vf); +} + +/* + *----------------------------------------------------------------------------- + * thumb updates + *----------------------------------------------------------------------------- + */ + +void vficon_thumb_progress_count(GList *list, gint *count, gint *done) +{ + GList *work = list; + while (work) + { + IconData *id = work->data; + FileData *fd = id->fd; + work = work->next; + + if (fd->thumb_pixbuf) (*done)++; + (*count)++; + } +} + +void vficon_set_thumb_fd(ViewFile *vf, FileData *fd) +{ + GtkTreeModel *store; + GtkTreeIter iter; + GList *list; + + if (!vficon_find_iter(vf, vficon_icon_data(vf, fd), &iter, NULL)) return; + + store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); + + gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1); + gtk_list_store_set(GTK_LIST_STORE(store), &iter, FILE_COLUMN_POINTER, list, -1); +} + + +FileData *vficon_thumb_next_fd(ViewFile *vf) +{ + GtkTreePath *tpath; + FileData *fd = NULL; + + if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL)) + { + GtkTreeModel *store; + GtkTreeIter iter; + gboolean valid = TRUE; + + store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); + gtk_tree_model_get_iter(store, &iter, tpath); + gtk_tree_path_free(tpath); + + while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0) + { + GList *list; + + gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1); + + while (!fd && list) + { + IconData *id = list->data; + if (id && !id->fd->thumb_pixbuf) fd = id->fd; + list = list->next; + } + + valid = gtk_tree_model_iter_next(store, &iter); + } + } + + /* then find first undone */ + + if (!fd) + { + GList *work = vf->list; + while (work && !fd) + { + IconData *id = work->data; + FileData *fd_p = id->fd; + work = work->next; + + if (!fd_p->thumb_pixbuf) fd = fd_p; + } + } + + return fd; +} + +void vficon_thumb_reset_all(ViewFile *vf) +{ + GList *work = vf->list; + + while (work) + { + IconData *id = work->data; + FileData *fd = id->fd; + if (fd->thumb_pixbuf) + { + g_object_unref(fd->thumb_pixbuf); + fd->thumb_pixbuf = NULL; + } + work = work->next; + } +} + + +/* + *----------------------------------------------------------------------------- + * row stuff + *----------------------------------------------------------------------------- + */ + +FileData *vficon_index_get_data(ViewFile *vf, gint row) +{ + IconData *id; + + id = g_list_nth_data(vf->list, row); + return id ? id->fd : NULL; +} + + +gint vficon_index_by_fd(ViewFile *vf, FileData *in_fd) +{ + gint p = 0; + GList *work; + + if (!in_fd) return -1; + + work = vf->list; + while (work) + { + IconData *id = work->data; + FileData *fd = id->fd; + if (fd == in_fd) return p; + work = work->next; + p++; + } + + return -1; +} + +static gint vficon_index_by_id(ViewFile *vf, IconData *in_id) +{ + gint p = 0; + GList *work; + + if (!in_id) return -1; + + work = vf->list; + while (work) + { + IconData *id = work->data; + if (id == in_id) return p; + work = work->next; + p++; + } + + return -1; +} + +guint vficon_count(ViewFile *vf, gint64 *bytes) +{ + if (bytes) + { + gint64 b = 0; + GList *work; + + work = vf->list; + while (work) + { + IconData *id = work->data; + FileData *fd = id->fd; + work = work->next; + + b += fd->size; + } + + *bytes = b; + } + + return g_list_length(vf->list); +} + +GList *vficon_get_list(ViewFile *vf) +{ + GList *list = NULL; + GList *work; + + work = vf->list; + while (work) + { + IconData *id = work->data; + FileData *fd = id->fd; + work = work->next; + + list = g_list_prepend(list, file_data_ref(fd)); + } + + return g_list_reverse(list); +} + +/* + *----------------------------------------------------------------------------- + * + *----------------------------------------------------------------------------- + */ + +static gboolean vficon_refresh_real(ViewFile *vf, gboolean keep_position) +{ + gboolean ret = TRUE; + GList *work, *work_fd; + IconData *focus_id; + GList *new_filelist = NULL; + FileData *first_selected = NULL; + GList *new_iconlist = NULL; + + focus_id = VFICON(vf)->focus_id; + + if (vf->dir_fd) + { + ret = filelist_read(vf->dir_fd, &new_filelist, NULL); + new_filelist = file_data_filter_marks_list(new_filelist, vf_marks_get_filter(vf)); + } + + vf->list = iconlist_sort(vf->list, vf->sort_method, vf->sort_ascend); /* the list might not be sorted if there were renames */ + new_filelist = filelist_sort(new_filelist, vf->sort_method, vf->sort_ascend); + + if (VFICON(vf)->selection) + { + first_selected = ((IconData *)(VFICON(vf)->selection->data))->fd; + file_data_ref(first_selected); + g_list_free(VFICON(vf)->selection); + VFICON(vf)->selection = NULL; + + + } + + /* check for same files from old_list */ + work = vf->list; + work_fd = new_filelist; + while (work || work_fd) + { + IconData *id = NULL; + FileData *fd = NULL; + FileData *new_fd = NULL; + gint match; + + if (work && work_fd) + { + id = work->data; + fd = id->fd; + + new_fd = work_fd->data; + + if (fd == new_fd) + { + /* not changed, go to next */ + work = work->next; + work_fd = work_fd->next; + if (id->selected & SELECTION_SELECTED) + { + VFICON(vf)->selection = g_list_prepend(VFICON(vf)->selection, id); + } + continue; + } + + match = filelist_sort_compare_filedata_full(fd, new_fd, vf->sort_method, vf->sort_ascend); + if (match == 0) g_warning("multiple fd for the same path"); + } + else if (work) + { + id = work->data; + fd = id->fd; + match = -1; + } + else /* work_fd */ + { + new_fd = work_fd->data; + match = 1; + } + + if (match < 0) + { + /* file no longer exists, delete from vf->list */ + GList *to_delete = work; + work = work->next; + if (id == VFICON(vf)->prev_selection) VFICON(vf)->prev_selection = NULL; + if (id == VFICON(vf)->click_id) VFICON(vf)->click_id = NULL; + file_data_unref(fd); + g_free(id); + vf->list = g_list_delete_link(vf->list, to_delete); + } + else + { + /* new file, add to vf->list */ + id = g_new0(IconData, 1); + + id->selected = SELECTION_NONE; + id->fd = file_data_ref(new_fd); + if (work) + vf->list = g_list_insert_before(vf->list, work, id); + else + new_iconlist = g_list_prepend(new_iconlist, id); /* it is faster to append all new entries together later */ + + work_fd = work_fd->next; + } + + } + + if (new_iconlist) + { + vf->list = g_list_concat(vf->list, g_list_reverse(new_iconlist)); + } + + VFICON(vf)->selection = g_list_reverse(VFICON(vf)->selection); + + filelist_free(new_filelist); + + vficon_populate(vf, TRUE, keep_position); + + if (first_selected && !VFICON(vf)->selection) + { + /* all selected files disappeared */ + vficon_select_closest(vf, first_selected); + } + file_data_unref(first_selected); + + /* attempt to keep focus on same icon when refreshing */ + if (focus_id && g_list_find(vf->list, focus_id)) + { + vficon_set_focus(vf, focus_id); + } + + return ret; +} + +gboolean vficon_refresh(ViewFile *vf) +{ + return vficon_refresh_real(vf, TRUE); +} + +/* + *----------------------------------------------------------------------------- + * draw, etc. + *----------------------------------------------------------------------------- + */ + +typedef struct _ColumnData ColumnData; +struct _ColumnData +{ + ViewFile *vf; + gint number; +}; + +static void vficon_cell_data_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell, + GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data) +{ + GList *list; + IconData *id; + ColumnData *cd = data; + ViewFile *vf = cd->vf; + + if (!GQV_IS_CELL_RENDERER_ICON(cell)) return; + + gtk_tree_model_get(tree_model, iter, FILE_COLUMN_POINTER, &list, -1); + + id = g_list_nth_data(list, cd->number); + + if (id) + { + GdkColor color_fg; + GdkColor color_bg; + GtkStyle *style; + gchar *name_sidecars; + gchar *link; + GtkStateType state = GTK_STATE_NORMAL; + + g_assert(id->fd->magick == FD_MAGICK); + + link = islink(id->fd->path) ? GQ_LINK_STR : ""; + if (id->fd->sidecar_files) + { + gchar *sidecars = file_data_sc_list_to_string(id->fd); + name_sidecars = g_strdup_printf("%s%s %s", link, id->fd->name, sidecars); + g_free(sidecars); + } + else + { + gchar *disabled_grouping = id->fd->disable_grouping ? _(" [NO GROUPING]") : ""; + name_sidecars = g_strdup_printf("%s%s%s", link, id->fd->name, disabled_grouping); + } + + style = gtk_widget_get_style(vf->listview); + if (id->selected & SELECTION_SELECTED) + { + state = GTK_STATE_SELECTED; + } + + memcpy(&color_fg, &style->text[state], sizeof(color_fg)); + memcpy(&color_bg, &style->base[state], sizeof(color_bg)); + + if (id->selected & SELECTION_PRELIGHT) + { + shift_color(&color_bg, -1, 0); + } + + g_object_set(cell, "pixbuf", id->fd->thumb_pixbuf, + "text", name_sidecars, + "marks", file_data_get_marks(id->fd), + "show_marks", vf->marks_enabled, + "cell-background-gdk", &color_bg, + "cell-background-set", TRUE, + "foreground-gdk", &color_fg, + "foreground-set", TRUE, + "has-focus", (VFICON(vf)->focus_id == id), NULL); + g_free(name_sidecars); + } + else + { + g_object_set(cell, "pixbuf", NULL, + "text", NULL, + "show_marks", FALSE, + "cell-background-set", FALSE, + "foreground-set", FALSE, + "has-focus", FALSE, NULL); + } +} + +static void vficon_append_column(ViewFile *vf, gint n) +{ + ColumnData *cd; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_min_width(column, 0); + + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_alignment(column, 0.5); + + renderer = gqv_cell_renderer_icon_new(); + gtk_tree_view_column_pack_start(column, renderer, FALSE); + g_object_set(G_OBJECT(renderer), "xpad", THUMB_BORDER_PADDING * 2, + "ypad", THUMB_BORDER_PADDING, + "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL); + + g_object_set_data(G_OBJECT(column), "column_number", GINT_TO_POINTER(n)); + g_object_set_data(G_OBJECT(renderer), "column_number", GINT_TO_POINTER(n)); + + cd = g_new0(ColumnData, 1); + cd->vf = vf; + cd->number = n; + gtk_tree_view_column_set_cell_data_func(column, renderer, vficon_cell_data_cb, cd, g_free); + + gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column); + + g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vficon_mark_toggled_cb), vf); +} + +/* + *----------------------------------------------------------------------------- + * base + *----------------------------------------------------------------------------- + */ + +gboolean vficon_set_fd(ViewFile *vf, FileData *dir_fd) +{ + gboolean ret; + + if (!dir_fd) return FALSE; + if (vf->dir_fd == dir_fd) return TRUE; + + file_data_unref(vf->dir_fd); + vf->dir_fd = file_data_ref(dir_fd); + + g_list_free(VFICON(vf)->selection); + VFICON(vf)->selection = NULL; + + iconlist_free(vf->list); + vf->list = NULL; + + /* NOTE: populate will clear the store for us */ + ret = vficon_refresh_real(vf, FALSE); + + VFICON(vf)->focus_id = NULL; + vficon_move_focus(vf, 0, 0, FALSE); + + return ret; +} + +void vficon_destroy_cb(GtkWidget *widget, gpointer data) +{ + ViewFile *vf = data; + + vf_refresh_idle_cancel(vf); + + file_data_unregister_notify_func(vf_notify_cb, vf); + + tip_unschedule(vf); + + vf_thumb_cleanup(vf); + + iconlist_free(vf->list); + g_list_free(VFICON(vf)->selection); +} + +ViewFile *vficon_new(ViewFile *vf, FileData *dir_fd) +{ + GtkListStore *store; + GtkTreeSelection *selection; + gint i; + + vf->info = g_new0(ViewFileInfoIcon, 1); + + VFICON(vf)->show_text = options->show_icon_names; + + store = gtk_list_store_new(1, G_TYPE_POINTER); + vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + g_object_unref(store); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview)); + gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_NONE); + + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE); + gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE); + + for (i = 0; i < VFICON_MAX_COLUMNS; i++) + { + vficon_append_column(vf, i); + } + + /* zero width column to hide tree view focus, we draw it ourselves */ + vficon_append_column(vf, i); + /* end column to fill white space */ + vficon_append_column(vf, i); + + g_signal_connect(G_OBJECT(vf->listview), "size_allocate", + G_CALLBACK(vficon_sized_cb), vf); + + gtk_widget_set_events(vf->listview, GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK | + GDK_BUTTON_PRESS_MASK | GDK_LEAVE_NOTIFY_MASK); + + g_signal_connect(G_OBJECT(vf->listview),"motion_notify_event", + G_CALLBACK(vficon_motion_cb), vf); + g_signal_connect(G_OBJECT(vf->listview), "leave_notify_event", + G_CALLBACK(vficon_leave_cb), vf); + + /* force VFICON(vf)->columns to be at least 1 (sane) - this will be corrected in the size_cb */ + vficon_populate_at_new_size(vf, 1, 1, FALSE); + + file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM); + + return vf; +} + +/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/view_file/view_file_icon.h Wed Jul 05 21:41:18 2017 +0000 @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2004 John Ellis + * Copyright (C) 2008 - 2016 The Geeqie Team + * + * Author: John Ellis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef VIEW_FILE_VIEW_FILE_ICON_H +#define VIEW_FILE_VIEW_FILE_ICON_H + +gboolean vficon_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data); +gboolean vficon_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data); +gboolean vficon_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data); + +void vficon_dnd_init(ViewFile *vf); + +void vficon_destroy_cb(GtkWidget *widget, gpointer data); +ViewFile *vficon_new(ViewFile *vf, FileData *dir_fd); + +gboolean vficon_set_fd(ViewFile *vf, FileData *dir_fd); +gboolean vficon_refresh(ViewFile *vf); + +void vficon_sort_set(ViewFile *vf, SortType type, gboolean ascend); + +void vficon_marks_set(ViewFile *vf, gboolean enable); + +GList *vficon_selection_get_one(ViewFile *vf, FileData *fd); +GList *vficon_pop_menu_file_list(ViewFile *vf); +void vficon_pop_menu_view_cb(GtkWidget *widget, gpointer data); +void vficon_pop_menu_rename_cb(GtkWidget *widget, gpointer data); +void vficon_pop_menu_refresh_cb(GtkWidget *widget, gpointer data); +void vficon_popup_destroy_cb(GtkWidget *widget, gpointer data); +void vficon_pop_menu_show_names_cb(GtkWidget *widget, gpointer data); + +FileData *vficon_index_get_data(ViewFile *vf, gint row); +gint vficon_index_by_fd(ViewFile *vf, FileData *in_fd); +guint vficon_count(ViewFile *vf, gint64 *bytes); +GList *vficon_get_list(ViewFile *vf); + +gboolean vficon_index_is_selected(ViewFile *vf, gint row); +guint vficon_selection_count(ViewFile *vf, gint64 *bytes); +GList *vficon_selection_get_list(ViewFile *vf); +GList *vficon_selection_get_list_by_index(ViewFile *vf); + +void vficon_select_all(ViewFile *vf); +void vficon_select_none(ViewFile *vf); +void vficon_select_invert(ViewFile *vf); +void vficon_select_by_fd(ViewFile *vf, FileData *fd); + +void vficon_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode); +void vficon_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode); + + +void vficon_thumb_progress_count(GList *list, gint *count, gint *done); +void vficon_set_thumb_fd(ViewFile *vf, FileData *fd); +FileData *vficon_thumb_next_fd(ViewFile *vf); +void vficon_thumb_reset_all(ViewFile *vf); + +#endif +/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/view_file/view_file_list.c Wed Jul 05 21:41:18 2017 +0000 @@ -0,0 +1,2052 @@ +/* + * Copyright (C) 2004 John Ellis + * Copyright (C) 2008 - 2016 The Geeqie Team + * + * Author: John Ellis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "main.h" +#include "view_file_list.h" + +#include "bar.h" +#include "cache_maint.h" +#include "dnd.h" +#include "editors.h" +#include "img-view.h" +#include "layout.h" +#include "layout_image.h" +#include "menu.h" +#include "metadata.h" +#include "thumb.h" +#include "utilops.h" +#include "ui_fileops.h" +#include "ui_menu.h" +#include "ui_tree_edit.h" +#include "uri_utils.h" +#include "view_file.h" + +#include <gdk/gdkkeysyms.h> /* for keyboard values */ + +/* Index to tree store */ +enum { + FILE_COLUMN_POINTER = 0, + FILE_COLUMN_VERSION, + FILE_COLUMN_THUMB, + FILE_COLUMN_FORMATTED, + FILE_COLUMN_NAME, + FILE_COLUMN_SIDECARS, + FILE_COLUMN_SIZE, + FILE_COLUMN_DATE, + FILE_COLUMN_EXPANDED, + FILE_COLUMN_COLOR, + FILE_COLUMN_MARKS, + FILE_COLUMN_MARKS_LAST = FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1, + FILE_COLUMN_COUNT +}; + + +/* Index to tree view */ +enum { + FILE_VIEW_COLUMN_MARKS = 0, + FILE_VIEW_COLUMN_MARKS_LAST = FILE_VIEW_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1, + FILE_VIEW_COLUMN_THUMB, + FILE_VIEW_COLUMN_FORMATTED, + FILE_VIEW_COLUMN_SIZE, + FILE_VIEW_COLUMN_DATE, + FILE_VIEW_COLUMN_COUNT +}; + + + +static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd); +static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data); +static void vflist_populate_view(ViewFile *vf, gboolean force); +static gboolean vflist_is_multiline(ViewFile *vf); +static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded); + + +/* + *----------------------------------------------------------------------------- + * misc + *----------------------------------------------------------------------------- + */ +typedef struct { + FileData *fd; + GtkTreeIter *iter; + gboolean found; + gint row; +} ViewFileFindRowData; + +static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) +{ + ViewFileFindRowData *find = data; + FileData *fd; + gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1); + if (fd == find->fd) + { + *find->iter = *iter; + find->found = TRUE; + return TRUE; + } + find->row++; + return FALSE; +} + +static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter) +{ + GtkTreeModel *store; + ViewFileFindRowData data = {fd, iter, FALSE, 0}; + + store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); + gtk_tree_model_foreach(store, vflist_find_row_cb, &data); + + if (data.found) + { + return data.row; + } + + return -1; +} + +static FileData *vflist_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter) +{ + GtkTreePath *tpath; + GtkTreeViewColumn *column; + + if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), x, y, + &tpath, &column, NULL, NULL)) + { + GtkTreeModel *store; + GtkTreeIter row; + FileData *fd; + + store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); + gtk_tree_model_get_iter(store, &row, tpath); + gtk_tree_path_free(tpath); + gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &fd, -1); + + return fd; + } + + return NULL; +} + +static gboolean vflist_store_clear_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) +{ + FileData *fd; + gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1); + + /* it seems that gtk_tree_store_clear may call some callbacks + that use the column. Set the pointer to NULL to be safe. */ + gtk_tree_store_set(GTK_TREE_STORE(model), iter, FILE_COLUMN_POINTER, NULL, -1); + file_data_unref(fd); + return FALSE; +} + +static void vflist_store_clear(ViewFile *vf, gboolean unlock_files) +{ + GtkTreeModel *store; + GList *files = NULL; + + if (unlock_files && vf->marks_enabled) + { + // unlock locked files in this directory + filelist_read(vf->dir_fd, &files, NULL); + while (files) + { + FileData *fd = files->data; + files = files->next; + file_data_unlock(fd); + file_data_unref(fd); // undo the ref that got added in filelist_read + } + } + + g_list_free(files); + store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); + gtk_tree_model_foreach(store, vflist_store_clear_cb, NULL); + gtk_tree_store_clear(GTK_TREE_STORE(store)); +} + +void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set) +{ + GtkTreeModel *store; + GtkTreeIter iter; + + if (vflist_find_row(vf, fd, &iter) < 0) return; + store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); + gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1); +} + +static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter) +{ + GtkTreeModel *store; + GtkTreePath *tpath; + + store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); + + tpath = gtk_tree_model_get_path(store, iter); + gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE); + gtk_tree_path_free(tpath); +} + + +/* + *----------------------------------------------------------------------------- + * dnd + *----------------------------------------------------------------------------- + */ + +static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context, + GtkSelectionData *selection_data, guint info, + guint time, gpointer data) +{ + ViewFile *vf = data; + GList *list = NULL; + + if (!VFLIST(vf)->click_fd) return; + + if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd)) + { + list = vf_selection_get_list(vf); + } + else + { + list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd)); + } + + if (!list) return; + uri_selection_data_set_uris_from_filelist(selection_data, list); + filelist_free(list); +} + +static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data) +{ + ViewFile *vf = data; + + vflist_color_set(vf, VFLIST(vf)->click_fd, TRUE); + + if (VFLIST(vf)->thumbs_enabled && + VFLIST(vf)->click_fd && VFLIST(vf)->click_fd->thumb_pixbuf) + { + guint items; + + if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd)) + items = vf_selection_count(vf, NULL); + else + items = 1; + + dnd_set_drag_icon(widget, context, VFLIST(vf)->click_fd->thumb_pixbuf, items); + } +} + +static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data) +{ + ViewFile *vf = data; + + vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE); + + if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE) + { + vf_refresh(vf); + } +} + +static void vflist_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context, + int x, int y, GtkSelectionData *selection, + guint info, guint time, gpointer data) +{ + ViewFile *vf = data; + + if (info == TARGET_TEXT_PLAIN) { + FileData *fd = vflist_find_data_by_coord(vf, x, y, NULL); + + if (fd) { + /* Add keywords to file */ + gchar *str = (gchar *) gtk_selection_data_get_text(selection); + GList *kw_list = string_to_keywords_list(str); + + metadata_append_list(fd, KEYWORD_KEY, kw_list); + string_list_free(kw_list); + g_free(str); + } + } +} + +void vflist_dnd_init(ViewFile *vf) +{ + gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK, + dnd_file_drag_types, dnd_file_drag_types_count, + GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK); + gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL, + dnd_file_drag_types, dnd_file_drag_types_count, + GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK); + + g_signal_connect(G_OBJECT(vf->listview), "drag_data_get", + G_CALLBACK(vflist_dnd_get), vf); + g_signal_connect(G_OBJECT(vf->listview), "drag_begin", + G_CALLBACK(vflist_dnd_begin), vf); + g_signal_connect(G_OBJECT(vf->listview), "drag_end", + G_CALLBACK(vflist_dnd_end), vf); + g_signal_connect(G_OBJECT(vf->listview), "drag_data_received", + G_CALLBACK(vflist_drag_data_received), vf); +} + +/* + *----------------------------------------------------------------------------- + * pop-up menu + *----------------------------------------------------------------------------- + */ + +GList *vflist_selection_get_one(ViewFile *vf, FileData *fd) +{ + GList *list = g_list_append(NULL, file_data_ref(fd)); + + if (fd->sidecar_files) + { + /* check if the row is expanded */ + GtkTreeModel *store; + GtkTreeIter iter; + + store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); + if (vflist_find_row(vf, fd, &iter) >= 0) + { + GtkTreePath *tpath; + + tpath = gtk_tree_model_get_path(store, &iter); + if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath)) + { + /* unexpanded - add whole group */ + GList *work = fd->sidecar_files; + while (work) + { + FileData *sfd = work->data; + list = g_list_prepend(list, file_data_ref(sfd)); + work = work->next; + } + } + gtk_tree_path_free(tpath); + } + list = g_list_reverse(list); + } + + return list; +} + +GList *vflist_pop_menu_file_list(ViewFile *vf) +{ + if (!VFLIST(vf)->click_fd) return NULL; + + if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd)) + { + return vf_selection_get_list(vf); + } + return vflist_selection_get_one(vf, VFLIST(vf)->click_fd); +} + + +void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data) +{ + ViewFile *vf = data; + + if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd)) + { + GList *list; + + list = vf_selection_get_list(vf); + view_window_new_from_list(list); + filelist_free(list); + } + else + { + view_window_new(VFLIST(vf)->click_fd); + } +} + +void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data) +{ + ViewFile *vf = data; + GList *list; + + list = vf_pop_menu_file_list(vf); + if (options->file_ops.enable_in_place_rename && + list && !list->next && VFLIST(vf)->click_fd) + { + GtkTreeModel *store; + GtkTreeIter iter; + + filelist_free(list); + + store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); + if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0) + { + GtkTreePath *tpath; + + tpath = gtk_tree_model_get_path(store, &iter); + tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath, + FILE_VIEW_COLUMN_FORMATTED, VFLIST(vf)->click_fd->name, + vflist_row_rename_cb, vf); + gtk_tree_path_free(tpath); + } + return; + } + + file_util_rename(NULL, list, vf->listview); +} + +void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data) +{ + ViewFile *vf = data; + + vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE); + if (vf->layout) + { + layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled); + } + else + { + vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled); + } +} + +void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data) +{ + ViewFile *vf = data; + + vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE); + vf_refresh(vf); + gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview)); +} + +void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data) +{ + ViewFile *vf = data; + vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE); + VFLIST(vf)->click_fd = NULL; + vf->popup = NULL; +} + + +/* + *----------------------------------------------------------------------------- + * callbacks + *----------------------------------------------------------------------------- + */ + +static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data) +{ + ViewFile *vf = data; + gchar *new_path; + + if (!new || !new[0]) return FALSE; + + new_path = g_build_filename(vf->dir_fd->path, new, NULL); + + if (strchr(new, G_DIR_SEPARATOR) != NULL) + { + gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new); + file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview); + g_free(text); + } + else + { + gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL); + FileData *fd = file_data_new_group(old_path); /* get the fd from cache */ + file_util_rename_simple(fd, new_path, vf->listview); + file_data_unref(fd); + g_free(old_path); + } + + g_free(new_path); + + return FALSE; +} + +static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data) +{ + ViewFile *vf = data; + GtkTreeModel *store; + GtkTreeIter iter; + GtkTreePath *tpath; + gint cw, ch; + + if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return; + store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); + tpath = gtk_tree_model_get_path(store, &iter); + tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch); + gtk_tree_path_free(tpath); + *y += ch; + popup_menu_position_clamp(menu, x, y, 0); +} + +gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data) +{ + ViewFile *vf = data; + GtkTreePath *tpath; + + if (event->keyval != GDK_KEY_Menu) return FALSE; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL); + if (tpath) + { + GtkTreeModel *store; + GtkTreeIter iter; + + store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget)); + gtk_tree_model_get_iter(store, &iter, tpath); + gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1); + gtk_tree_path_free(tpath); + } + else + { + VFLIST(vf)->click_fd = NULL; + } + + vf->popup = vf_pop_menu(vf); + gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME); + + return TRUE; +} + +gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data) +{ + ViewFile *vf = data; + GtkTreePath *tpath; + GtkTreeIter iter; + FileData *fd = NULL; + GtkTreeViewColumn *column; + + vf->clicked_mark = 0; + + if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y, + &tpath, &column, NULL, NULL)) + { + GtkTreeModel *store; + gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx")); + + if (bevent->button == MOUSE_BUTTON_LEFT && + col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST) + return FALSE; + + if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST) + vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS); + + store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget)); + + gtk_tree_model_get_iter(store, &iter, tpath); + gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1); + gtk_tree_path_free(tpath); + } + + VFLIST(vf)->click_fd = fd; + + if (bevent->button == MOUSE_BUTTON_RIGHT) + { + vf->popup = vf_pop_menu(vf); + gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL, + bevent->button, bevent->time); + return TRUE; + } + + if (!fd) return FALSE; + + if (bevent->button == MOUSE_BUTTON_MIDDLE) + { + if (!vflist_row_is_selected(vf, fd)) + { + vflist_color_set(vf, fd, TRUE); + } + return TRUE; + } + + + if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS && + !(bevent->state & GDK_SHIFT_MASK ) && + !(bevent->state & GDK_CONTROL_MASK ) && + vflist_row_is_selected(vf, fd)) + { + GtkTreeSelection *selection; + + gtk_widget_grab_focus(widget); + + + /* returning FALSE and further processing of the event is needed for + correct operation of the expander, to show the sidecar files. + It however resets the selection of multiple files. With this condition + it should work for both cases */ + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); + return (gtk_tree_selection_count_selected_rows(selection) > 1); + } + + if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS) + { + if (vf->layout) layout_image_full_screen_start(vf->layout); + } + + return FALSE; +} + +gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data) +{ + ViewFile *vf = data; + GtkTreePath *tpath; + GtkTreeIter iter; + FileData *fd = NULL; + + if (bevent->button == MOUSE_BUTTON_MIDDLE) + { + vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE); + } + + if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE) + { + return TRUE; + } + + if ((bevent->x != 0 || bevent->y != 0) && + gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y, + &tpath, NULL, NULL, NULL)) + { + GtkTreeModel *store; + + store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget)); + gtk_tree_model_get_iter(store, &iter, tpath); + gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1); + gtk_tree_path_free(tpath); + } + + if (bevent->button == MOUSE_BUTTON_MIDDLE) + { + if (fd && VFLIST(vf)->click_fd == fd) + { + GtkTreeSelection *selection; + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); + if (vflist_row_is_selected(vf, fd)) + { + gtk_tree_selection_unselect_iter(selection, &iter); + } + else + { + gtk_tree_selection_select_iter(selection, &iter); + } + } + return TRUE; + } + + if (fd && VFLIST(vf)->click_fd == fd && + !(bevent->state & GDK_SHIFT_MASK ) && + !(bevent->state & GDK_CONTROL_MASK ) && + vflist_row_is_selected(vf, fd)) + { + GtkTreeSelection *selection; + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); + gtk_tree_selection_unselect_all(selection); + gtk_tree_selection_select_iter(selection, &iter); + vflist_move_cursor(vf, &iter); + } + + return FALSE; +} + +static void vflist_select_image(ViewFile *vf, FileData *sel_fd) +{ + FileData *read_ahead_fd = NULL; + gint row; + FileData *cur_fd; + + if (!sel_fd) return; + + cur_fd = layout_image_get_fd(vf->layout); + if (sel_fd == cur_fd) return; /* no change */ + + row = g_list_index(vf->list, sel_fd); + // FIXME sidecar data + + if (sel_fd && options->image.enable_read_ahead && row >= 0) + { + if (row > g_list_index(vf->list, cur_fd) && + (guint) (row + 1) < vf_count(vf, NULL)) + { + read_ahead_fd = vf_index_get_data(vf, row + 1); + } + else if (row > 0) + { + read_ahead_fd = vf_index_get_data(vf, row - 1); + } + } + + layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd); +} + +static gboolean vflist_select_idle_cb(gpointer data) +{ + ViewFile *vf = data; + + if (!vf->layout) + { + VFLIST(vf)->select_idle_id = 0; + return FALSE; + } + + vf_send_update(vf); + + if (VFLIST(vf)->select_fd) + { + vflist_select_image(vf, VFLIST(vf)->select_fd); + VFLIST(vf)->select_fd = NULL; + } + + VFLIST(vf)->select_idle_id = 0; + return FALSE; +} + +static void vflist_select_idle_cancel(ViewFile *vf) +{ + if (VFLIST(vf)->select_idle_id) + { + g_source_remove(VFLIST(vf)->select_idle_id); + VFLIST(vf)->select_idle_id = 0; + } +} + +static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath, + gboolean path_currently_selected, gpointer data) +{ + ViewFile *vf = data; + GtkTreeIter iter; + + if (!path_currently_selected && + gtk_tree_model_get_iter(store, &iter, tpath)) + { + gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1); + } + else + { + VFLIST(vf)->select_fd = NULL; + } + + if (vf->layout && + !VFLIST(vf)->select_idle_id) + { + VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf); + } + + return TRUE; +} + +static void vflist_expand_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data) +{ + ViewFile *vf = data; + vflist_set_expanded(vf, iter, TRUE); +} + +static void vflist_collapse_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data) +{ + ViewFile *vf = data; + vflist_set_expanded(vf, iter, FALSE); +} + +/* + *----------------------------------------------------------------------------- + * misc + *----------------------------------------------------------------------------- + */ + + +static gchar* vflist_get_formatted(ViewFile *vf, const gchar *name, const gchar *sidecars, const gchar *size, const gchar *time, gboolean expanded) + { + gboolean multiline = vflist_is_multiline(vf); + gchar *text; + + if (multiline) + { + text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time); + } + else + { + text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars); + } + return text; +} + +static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded) +{ + GtkTreeStore *store; + gchar *name; + gchar *sidecars; + gchar *size; + gchar *time; + gchar *formatted; + + store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview))); + + gtk_tree_model_get(GTK_TREE_MODEL(store), iter, + FILE_COLUMN_NAME, &name, + FILE_COLUMN_SIDECARS, &sidecars, + FILE_COLUMN_SIZE, &size, + FILE_COLUMN_DATE, &time, + -1); + formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded); + + gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED, formatted, + FILE_COLUMN_EXPANDED, expanded, + -1); + g_free(time); + g_free(size); + g_free(sidecars); + g_free(name); + g_free(formatted); +} + +static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd) +{ + gchar *size; + gchar *sidecars = NULL; + gchar *name; + const gchar *time = text_from_time(fd->date); + gchar *link = islink(fd->path) ? GQ_LINK_STR : ""; + const gchar *disabled_grouping; + gchar *formatted; + gboolean expanded = FALSE; + + if (fd->sidecar_files) /* expanded has no effect on files without sidecars */ + { + gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_EXPANDED, &expanded, -1); + } + + sidecars = file_data_sc_list_to_string(fd); + + disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : ""; + name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping); + size = text_from_size(fd->size); + + formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded); + + gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd, + FILE_COLUMN_VERSION, fd->version, + FILE_COLUMN_THUMB, fd->thumb_pixbuf, + FILE_COLUMN_FORMATTED, formatted, + FILE_COLUMN_SIDECARS, sidecars, + FILE_COLUMN_NAME, name, + FILE_COLUMN_SIZE, size, + FILE_COLUMN_DATE, time, +#define STORE_SET_IS_SLOW 1 +#if STORE_SET_IS_SLOW +/* this is 3x faster on a directory with 20000 files */ + FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0), + FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1), + FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2), + FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3), + FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4), + FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5), +#if FILEDATA_MARKS_SIZE != 6 +#error this needs to be updated +#endif +#endif + FILE_COLUMN_COLOR, FALSE, -1); + +#if !STORE_SET_IS_SLOW + { + gint i; + for (i = 0; i < FILEDATA_MARKS_SIZE; i++) + gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1); + } +#endif + g_free(size); + g_free(sidecars); + g_free(name); + g_free(formatted); +} + +static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected, gboolean force) +{ + GList *work; + GtkTreeIter iter; + gboolean valid; + gint num_ordered = 0; + gint num_prepended = 0; + + valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter); + + work = list; + while (work) + { + gint match; + FileData *fd = work->data; + gboolean done = FALSE; + + while (!done) + { + FileData *old_fd = NULL; + gint old_version = 0; + + if (valid) + { + gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, + FILE_COLUMN_POINTER, &old_fd, + FILE_COLUMN_VERSION, &old_version, + -1); + + if (fd == old_fd) + { + match = 0; + } + else + { + if (parent_iter) + match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */ + else + match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend); + + if (match == 0) g_warning("multiple fd for the same path"); + } + + } + else + { + match = -1; + } + + if (match < 0) + { + GtkTreeIter new; + + if (valid) + { + num_ordered++; + gtk_tree_store_insert_before(store, &new, parent_iter, &iter); + } + else + { + /* + here should be used gtk_tree_store_append, but this function seems to be O(n) + and it seems to be much faster to add new entries to the beginning and reorder later + */ + num_prepended++; + gtk_tree_store_prepend(store, &new, parent_iter); + } + + vflist_setup_iter(vf, store, &new, file_data_ref(fd)); + vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected, force); + + if (g_list_find(selected, fd)) + { + /* renamed files - the same fd appears at different position - select it again*/ + GtkTreeSelection *selection; + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview)); + gtk_tree_selection_select_iter(selection, &new); + } + + done = TRUE; + } + else if (match > 0) + { + file_data_unref(old_fd); + valid = gtk_tree_store_remove(store, &iter); + } + else + { + num_ordered++; + if (fd->version != old_version || force) + { + vflist_setup_iter(vf, store, &iter, fd); + vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected, force); + } + + if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter); + + done = TRUE; + } + } + work = work->next; + } + + while (valid) + { + FileData *old_fd; + gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1); + file_data_unref(old_fd); + + valid = gtk_tree_store_remove(store, &iter); + } + + /* move the prepended entries to the correct position */ + if (num_prepended) + { + gint i; + gint num_total = num_prepended + num_ordered; + gint *new_order = g_malloc(num_total * sizeof(gint)); + + for (i = 0; i < num_total; i++) + { + if (i < num_ordered) + new_order[i] = num_prepended + i; + else + new_order[i] = num_total - 1 - i; + } + gtk_tree_store_reorder(store, parent_iter, new_order); + + g_free(new_order); + } +} + +void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend) +{ + gint i; + GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL); + gint *new_order; + GtkTreeStore *store; + GList *work; + + if (vf->sort_method == type && vf->sort_ascend == ascend) return; + if (!vf->list) return; + + work = vf->list; + i = 0; + while (work) + { + FileData *fd = work->data; + g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i)); + i++; + work = work->next; + } + + vf->sort_method = type; + vf->sort_ascend = ascend; + + vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend); + + new_order = g_malloc(i * sizeof(gint)); + + work = vf->list; + i = 0; + while (work) + { + FileData *fd = work->data; + new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd)); + i++; + work = work->next; + } + + store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview))); + gtk_tree_store_reorder(store, NULL, new_order); + + g_free(new_order); + g_hash_table_destroy(fd_idx_hash); +} + +/* + *----------------------------------------------------------------------------- + * thumb updates + *----------------------------------------------------------------------------- + */ + + +void vflist_thumb_progress_count(GList *list, gint *count, gint *done) +{ + GList *work = list; + while (work) + { + FileData *fd = work->data; + work = work->next; + + if (fd->thumb_pixbuf) (*done)++; + + if (fd->sidecar_files) + { + vflist_thumb_progress_count(fd->sidecar_files, count, done); + } + (*count)++; + } +} + +void vflist_set_thumb_fd(ViewFile *vf, FileData *fd) +{ + GtkTreeStore *store; + GtkTreeIter iter; + + if (!fd || vflist_find_row(vf, fd, &iter) < 0) return; + + store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview))); + gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1); +} + +FileData *vflist_thumb_next_fd(ViewFile *vf) +{ + GtkTreePath *tpath; + FileData *fd = NULL; + + /* first check the visible files */ + + if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL)) + { + GtkTreeModel *store; + GtkTreeIter iter; + gboolean valid = TRUE; + + store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); + gtk_tree_model_get_iter(store, &iter, tpath); + gtk_tree_path_free(tpath); + + while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0) + { + FileData *nfd; + + gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1); + + if (!nfd->thumb_pixbuf) fd = nfd; + + valid = gtk_tree_model_iter_next(store, &iter); + } + } + + /* then find first undone */ + + if (!fd) + { + GList *work = vf->list; + while (work && !fd) + { + FileData *fd_p = work->data; + if (!fd_p->thumb_pixbuf) + fd = fd_p; + else + { + GList *work2 = fd_p->sidecar_files; + + while (work2 && !fd) + { + fd_p = work2->data; + if (!fd_p->thumb_pixbuf) fd = fd_p; + work2 = work2->next; + } + } + work = work->next; + } + } + + return fd; +} + + +void vflist_thumb_reset_all(ViewFile *vf) +{ + GList *work = vf->list; + while (work) + { + FileData *fd = work->data; + if (fd->thumb_pixbuf) + { + g_object_unref(fd->thumb_pixbuf); + fd->thumb_pixbuf = NULL; + } + work = work->next; + } +} + +/* + *----------------------------------------------------------------------------- + * row stuff + *----------------------------------------------------------------------------- + */ + +FileData *vflist_index_get_data(ViewFile *vf, gint row) +{ + return g_list_nth_data(vf->list, row); +} + +gint vflist_index_by_fd(ViewFile *vf, FileData *fd) +{ + gint p = 0; + GList *work, *work2; + + work = vf->list; + while (work) + { + FileData *list_fd = work->data; + if (list_fd == fd) return p; + + work2 = list_fd->sidecar_files; + while (work2) + { + /* FIXME: return the same index also for sidecars + it is sufficient for next/prev navigation but it should be rewritten + without using indexes at all + */ + FileData *sidecar_fd = work2->data; + if (sidecar_fd == fd) return p; + work2 = work2->next; + } + + work = work->next; + p++; + } + + return -1; +} + +guint vflist_count(ViewFile *vf, gint64 *bytes) +{ + if (bytes) + { + gint64 b = 0; + GList *work; + + work = vf->list; + while (work) + { + FileData *fd = work->data; + work = work->next; + b += fd->size; + } + + *bytes = b; + } + + return g_list_length(vf->list); +} + +GList *vflist_get_list(ViewFile *vf) +{ + GList *list = NULL; + GList *work; + + work = vf->list; + while (work) + { + FileData *fd = work->data; + work = work->next; + + list = g_list_prepend(list, file_data_ref(fd)); + } + + return g_list_reverse(list); +} + +/* + *----------------------------------------------------------------------------- + * selections + *----------------------------------------------------------------------------- + */ + +static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd) +{ + GtkTreeModel *store; + GtkTreeSelection *selection; + GList *slist; + GList *work; + gboolean found = FALSE; + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview)); + slist = gtk_tree_selection_get_selected_rows(selection, &store); + work = slist; + while (!found && work) + { + GtkTreePath *tpath = work->data; + FileData *fd_n; + GtkTreeIter iter; + + gtk_tree_model_get_iter(store, &iter, tpath); + gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1); + if (fd_n == fd) found = TRUE; + work = work->next; + } + g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL); + g_list_free(slist); + + return found; +} + +gboolean vflist_index_is_selected(ViewFile *vf, gint row) +{ + FileData *fd; + + fd = vf_index_get_data(vf, row); + return vflist_row_is_selected(vf, fd); +} + +guint vflist_selection_count(ViewFile *vf, gint64 *bytes) +{ + GtkTreeModel *store; + GtkTreeSelection *selection; + GList *slist; + guint count; + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview)); + slist = gtk_tree_selection_get_selected_rows(selection, &store); + + if (bytes) + { + gint64 b = 0; + GList *work; + + work = slist; + while (work) + { + GtkTreePath *tpath = work->data; + GtkTreeIter iter; + FileData *fd; + + gtk_tree_model_get_iter(store, &iter, tpath); + gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1); + b += fd->size; + + work = work->next; + } + + *bytes = b; + } + + count = g_list_length(slist); + g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL); + g_list_free(slist); + + return count; +} + +GList *vflist_selection_get_list(ViewFile *vf) +{ + GtkTreeModel *store; + GtkTreeSelection *selection; + GList *slist; + GList *list = NULL; + GList *work; + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview)); + slist = gtk_tree_selection_get_selected_rows(selection, &store); + work = slist; + while (work) + { + GtkTreePath *tpath = work->data; + FileData *fd; + GtkTreeIter iter; + + gtk_tree_model_get_iter(store, &iter, tpath); + gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1); + + list = g_list_prepend(list, file_data_ref(fd)); + + if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath)) + { + /* unexpanded - add whole group */ + GList *work2 = fd->sidecar_files; + while (work2) + { + FileData *sfd = work2->data; + list = g_list_prepend(list, file_data_ref(sfd)); + work2 = work2->next; + } + } + + work = work->next; + } + g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL); + g_list_free(slist); + + return g_list_reverse(list); +} + +GList *vflist_selection_get_list_by_index(ViewFile *vf) +{ + GtkTreeModel *store; + GtkTreeSelection *selection; + GList *slist; + GList *list = NULL; + GList *work; + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview)); + slist = gtk_tree_selection_get_selected_rows(selection, &store); + work = slist; + while (work) + { + GtkTreePath *tpath = work->data; + FileData *fd; + GtkTreeIter iter; + + gtk_tree_model_get_iter(store, &iter, tpath); + gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1); + + list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd))); + + work = work->next; + } + g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL); + g_list_free(slist); + + return g_list_reverse(list); +} + +void vflist_select_all(ViewFile *vf) +{ + GtkTreeSelection *selection; + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview)); + gtk_tree_selection_select_all(selection); + + VFLIST(vf)->select_fd = NULL; +} + +void vflist_select_none(ViewFile *vf) +{ + GtkTreeSelection *selection; + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview)); + gtk_tree_selection_unselect_all(selection); +} + +static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter) +{ + GtkTreePath *tpath; + gboolean result; + + tpath = gtk_tree_model_get_path(store, iter); + result = gtk_tree_path_prev(tpath); + if (result) + gtk_tree_model_get_iter(store, iter, tpath); + + gtk_tree_path_free(tpath); + + return result; +} + +static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter) +{ + if (!gtk_tree_model_get_iter_first(store, iter)) + return FALSE; + + while (TRUE) + { + GtkTreeIter next = *iter; + + if (gtk_tree_model_iter_next(store, &next)) + *iter = next; + else + break; + } + + return TRUE; +} + +void vflist_select_invert(ViewFile *vf) +{ + GtkTreeIter iter; + GtkTreeSelection *selection; + GtkTreeModel *store; + gboolean valid; + + store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview)); + + /* Backward iteration prevents scrolling to the end of the list, + * it scrolls to the first selected row instead. */ + valid = tree_model_get_iter_last(store, &iter); + + while (valid) + { + gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter); + + if (selected) + gtk_tree_selection_unselect_iter(selection, &iter); + else + gtk_tree_selection_select_iter(selection, &iter); + + valid = tree_model_iter_prev(store, &iter); + } +} + +void vflist_select_by_fd(ViewFile *vf, FileData *fd) +{ + GtkTreeIter iter; + + if (vflist_find_row(vf, fd, &iter) < 0) return; + + tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE); + + if (!vflist_row_is_selected(vf, fd)) + { + GtkTreeSelection *selection; + GtkTreeModel *store; + GtkTreePath *tpath; + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview)); + gtk_tree_selection_unselect_all(selection); + gtk_tree_selection_select_iter(selection, &iter); + vflist_move_cursor(vf, &iter); + + store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); + tpath = gtk_tree_model_get_path(store, &iter); + gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE); + gtk_tree_path_free(tpath); + } +} + +static void vflist_select_closest(ViewFile *vf, FileData *sel_fd) +{ + GList *work; + FileData *fd = NULL; + + if (sel_fd->parent) sel_fd = sel_fd->parent; + work = vf->list; + + while (work) + { + gint match; + fd = work->data; + work = work->next; + + match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend); + + if (match >= 0) break; + } + + if (fd) vflist_select_by_fd(vf, fd); + +} + +void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode) +{ + GtkTreeModel *store; + GtkTreeIter iter; + GtkTreeSelection *selection; + gboolean valid; + gint n = mark - 1; + + g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE); + + store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview)); + + valid = gtk_tree_model_get_iter_first(store, &iter); + while (valid) + { + FileData *fd; + gboolean mark_val, selected; + gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1); + + mark_val = file_data_get_mark(fd, n); + selected = gtk_tree_selection_iter_is_selected(selection, &iter); + + switch (mode) + { + case MTS_MODE_SET: selected = mark_val; + break; + case MTS_MODE_OR: selected = mark_val || selected; + break; + case MTS_MODE_AND: selected = mark_val && selected; + break; + case MTS_MODE_MINUS: selected = !mark_val && selected; + break; + } + + if (selected) + gtk_tree_selection_select_iter(selection, &iter); + else + gtk_tree_selection_unselect_iter(selection, &iter); + + valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter); + } +} + +void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode) +{ + GtkTreeModel *store; + GtkTreeSelection *selection; + GList *slist; + GList *work; + gint n = mark - 1; + + g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview)); + slist = gtk_tree_selection_get_selected_rows(selection, &store); + work = slist; + while (work) + { + GtkTreePath *tpath = work->data; + FileData *fd; + GtkTreeIter iter; + + gtk_tree_model_get_iter(store, &iter, tpath); + gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1); + + /* the change has a very limited range and the standard notification would trigger + complete re-read of the directory - try to do only minimal update instead */ + file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */ + + switch (mode) + { + case STM_MODE_SET: file_data_set_mark(fd, n, 1); + break; + case STM_MODE_RESET: file_data_set_mark(fd, n, 0); + break; + case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n)); + break; + } + + if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */ + { + vf_refresh_idle(vf); + } + else + { + /* mark functions can have various side effects - update all columns to be sure */ + vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd); + /* mark functions can change sidecars too */ + vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE); + } + + + file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM); + + work = work->next; + } + g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL); + g_list_free(slist); +} + +/* + *----------------------------------------------------------------------------- + * core (population) + *----------------------------------------------------------------------------- + */ + +static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline) +{ + GtkTreeViewColumn *column; + GtkCellRenderer *cell; + GList *list; + + column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB); + if (!column) return; + + gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4); + + list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column)); + if (!list) return; + cell = list->data; + g_list_free(list); + + g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL); + gtk_tree_view_column_set_visible(column, thumb); + + column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED); + if (!column) return; + gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column); + + column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE); + if (!column) return; + gtk_tree_view_column_set_visible(column, !multiline); + + column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE); + if (!column) return; + gtk_tree_view_column_set_visible(column, !multiline); +} + +static gboolean vflist_is_multiline(ViewFile *vf) +{ + return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48); +} + + +static void vflist_populate_view(ViewFile *vf, gboolean force) +{ + GtkTreeStore *store; + GList *selected; + + store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview))); + + vf_thumb_stop(vf); + + if (!vf->list) + { + vflist_store_clear(vf, FALSE); + vf_send_update(vf); + return; + } + + vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf)); + + selected = vflist_selection_get_list(vf); + + vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force); + + if (selected && vflist_selection_count(vf, NULL) == 0) + { + /* all selected files disappeared */ + vflist_select_closest(vf, selected->data); + } + + filelist_free(selected); + + vf_send_update(vf); + vf_thumb_update(vf); +} + +gboolean vflist_refresh(ViewFile *vf) +{ + GList *old_list; + gboolean ret = TRUE; + + old_list = vf->list; + vf->list = NULL; + + DEBUG_1("%s vflist_refresh: read dir", get_exec_time()); + if (vf->dir_fd) + { + file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */ + + ret = filelist_read(vf->dir_fd, &vf->list, NULL); + + if (vf->marks_enabled) + { + // When marks are enabled, lock FileDatas so that we don't end up re-parsing XML + // each time a mark is changed. + file_data_lock_list(vf->list); + } + else + { + // FIXME: only do this when needed (aka when we just switched from + // FIXME: marks-enabled to marks-disabled) + file_data_unlock_list(vf->list); + } + + vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf)); + file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM); + + DEBUG_1("%s vflist_refresh: sort", get_exec_time()); + vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend); + } + + DEBUG_1("%s vflist_refresh: populate view", get_exec_time()); + + vflist_populate_view(vf, FALSE); + + DEBUG_1("%s vflist_refresh: free filelist", get_exec_time()); + + filelist_free(old_list); + DEBUG_1("%s vflist_refresh: done", get_exec_time()); + + return ret; +} + + + +/* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */ + +#define CELL_HEIGHT_OVERRIDE 512 + +static void cell_renderer_height_override(GtkCellRenderer *renderer) +{ + GParamSpec *spec; + + spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height"); + if (spec && G_IS_PARAM_SPEC_INT(spec)) + { + GParamSpecInt *spec_int; + + spec_int = G_PARAM_SPEC_INT(spec); + if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE; + } +} + +static GdkColor *vflist_listview_color_shifted(GtkWidget *widget) +{ + static GdkColor color; + static GtkWidget *done = NULL; + + if (done != widget) + { + GtkStyle *style; + + style = gtk_widget_get_style(widget); + memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color)); + shift_color(&color, -1, 0); + done = widget; + } + + return &color; +} + +static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell, + GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data) +{ + ViewFile *vf = data; + gboolean set; + + gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1); + g_object_set(G_OBJECT(cell), + "cell-background-gdk", vflist_listview_color_shifted(vf->listview), + "cell-background-set", set, NULL); +} + +static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand) +{ + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(column, title); + gtk_tree_view_column_set_min_width(column, 4); + + if (!image) + { + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY); + renderer = gtk_cell_renderer_text_new(); + if (right_justify) + { + g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL); + } + gtk_tree_view_column_pack_start(column, renderer, TRUE); + gtk_tree_view_column_add_attribute(column, renderer, "text", n); + if (expand) + gtk_tree_view_column_set_expand(column, TRUE); + } + else + { + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + renderer = gtk_cell_renderer_pixbuf_new(); + cell_renderer_height_override(renderer); + gtk_tree_view_column_pack_start(column, renderer, TRUE); + gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n); + } + + gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL); + g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n)); + g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n)); + + gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column); +} + +static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data) +{ + ViewFile *vf = data; + GtkTreeStore *store; + GtkTreePath *path = gtk_tree_path_new_from_string(path_str); + GtkTreeIter iter; + FileData *fd; + gboolean marked; + guint col_idx; + + store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview))); + if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path)) + return; + + col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx")); + + g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST); + + gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1); + marked = !marked; + + /* the change has a very limited range and the standard notification would trigger + complete re-read of the directory - try to do only minimal update instead */ + file_data_unregister_notify_func(vf_notify_cb, vf); + file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked); + if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */ + { + vf_refresh_idle(vf); + } + else + { + /* mark functions can have various side effects - update all columns to be sure */ + vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd); + /* mark functions can change sidecars too */ + vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE); + } + file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM); + + gtk_tree_path_free(path); +} + +static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title) +{ + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + + renderer = gtk_cell_renderer_toggle_new(); + column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL); + + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n)); + g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n)); + + gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column); + gtk_tree_view_column_set_fixed_width(column, 22); + gtk_tree_view_column_set_visible(column, vf->marks_enabled); + + + g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf); +} + +/* + *----------------------------------------------------------------------------- + * base + *----------------------------------------------------------------------------- + */ + +gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd) +{ + gboolean ret; + if (!dir_fd) return FALSE; + if (vf->dir_fd == dir_fd) return TRUE; + + file_data_unref(vf->dir_fd); + vf->dir_fd = file_data_ref(dir_fd); + + /* force complete reload */ + vflist_store_clear(vf, TRUE); + + filelist_free(vf->list); + vf->list = NULL; + + ret = vf_refresh(vf); + gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview)); + return ret; +} + +void vflist_destroy_cb(GtkWidget *widget, gpointer data) +{ + ViewFile *vf = data; + + file_data_unregister_notify_func(vf_notify_cb, vf); + + vflist_select_idle_cancel(vf); + vf_refresh_idle_cancel(vf); + vf_thumb_stop(vf); + + filelist_free(vf->list); +} + +ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd) +{ + GtkTreeStore *store; + GtkTreeSelection *selection; + GType flist_types[FILE_COLUMN_COUNT]; + gint i; + gint column; + + vf->info = g_new0(ViewFileInfoList, 1); + + flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER; + flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT; + flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF; + flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING; + flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING; + flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING; + flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING; + flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING; + flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN; + flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN; + for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++) + flist_types[i] = G_TYPE_BOOLEAN; + + store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types); + + vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + g_object_unref(store); + + g_signal_connect(G_OBJECT(vf->listview), "row-expanded", + G_CALLBACK(vflist_expand_cb), vf); + + g_signal_connect(G_OBJECT(vf->listview), "row-collapsed", + G_CALLBACK(vflist_collapse_cb), vf); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview)); + gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE); + gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL); + + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE); + gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE); + + column = 0; + + for (i = 0; i < FILEDATA_MARKS_SIZE; i++) + { + vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, ""); + g_assert(column == FILE_VIEW_COLUMN_MARKS + i); + column++; + } + + vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE); + g_assert(column == FILE_VIEW_COLUMN_THUMB); + column++; + + vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE); + g_assert(column == FILE_VIEW_COLUMN_FORMATTED); + column++; + + vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE); + g_assert(column == FILE_VIEW_COLUMN_SIZE); + column++; + + vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE); + g_assert(column == FILE_VIEW_COLUMN_DATE); + column++; + + file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM); + return vf; +} + +void vflist_thumb_set(ViewFile *vf, gboolean enable) +{ + if (VFLIST(vf)->thumbs_enabled == enable) return; + + VFLIST(vf)->thumbs_enabled = enable; + + /* vflist_populate_view is better than vf_refresh: + - no need to re-read the directory + - force update because the formatted string has changed + */ + if (vf->layout) + { + vflist_populate_view(vf, TRUE); + gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview)); + } +} + +void vflist_marks_set(ViewFile *vf, gboolean enable) +{ + GList *columns, *work; + + columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview)); + + work = columns; + while (work) + { + GtkTreeViewColumn *column = work->data; + gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx")); + work = work->next; + + if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS) + gtk_tree_view_column_set_visible(column, enable); + } + + if (enable) + { + // Previously disabled, which means that vf->list is complete + file_data_lock_list(vf->list); + } + else + { + // Previously enabled, which means that vf->list is incomplete + } + + g_list_free(columns); +} + +/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/view_file/view_file_list.h Wed Jul 05 21:41:18 2017 +0000 @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2004 John Ellis + * Copyright (C) 2008 - 2016 The Geeqie Team + * + * Author: John Ellis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef VIEW_FILE_VIEW_FILE_LIST_H +#define VIEW_FILE_VIEW_FILE_LIST_H + + +#include "filedata.h" + +gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data); +gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data); +gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data); + +void vflist_dnd_init(ViewFile *vf); + +void vflist_destroy_cb(GtkWidget *widget, gpointer data); +ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd); + +gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd); +gboolean vflist_refresh(ViewFile *vf); + +void vflist_thumb_set(ViewFile *vf, gboolean enable); +void vflist_marks_set(ViewFile *vf, gboolean enable); +void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend); + +GList *vflist_selection_get_one(ViewFile *vf, FileData *fd); +GList *vflist_pop_menu_file_list(ViewFile *vf); +void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data); +void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data); +void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data); +void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data); +void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data); + +FileData *vflist_index_get_data(ViewFile *vf, gint row); +gint vflist_index_by_fd(ViewFile *vf, FileData *fd); +guint vflist_count(ViewFile *vf, gint64 *bytes); +GList *vflist_get_list(ViewFile *vf); + +gboolean vflist_index_is_selected(ViewFile *vf, gint row); +guint vflist_selection_count(ViewFile *vf, gint64 *bytes); +GList *vflist_selection_get_list(ViewFile *vf); +GList *vflist_selection_get_list_by_index(ViewFile *vf); + +void vflist_select_all(ViewFile *vf); +void vflist_select_none(ViewFile *vf); +void vflist_select_invert(ViewFile *vf); +void vflist_select_by_fd(ViewFile *vf, FileData *fd); + +void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode); +void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode); + +void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set); + +void vflist_thumb_progress_count(GList *list, gint *count, gint *done); +void vflist_set_thumb_fd(ViewFile *vf, FileData *fd); +FileData *vflist_thumb_next_fd(ViewFile *vf); +void vflist_thumb_reset_all(ViewFile *vf); + +#endif +/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- a/src/view_file_icon.c Thu Jun 29 11:05:59 2017 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2300 +0,0 @@ -/* - * Copyright (C) 2006 John Ellis - * Copyright (C) 2008 - 2016 The Geeqie Team - * - * Author: John Ellis - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "main.h" -#include "view_file_icon.h" - -#include "bar.h" -#include "cellrenderericon.h" -#include "collect.h" -#include "collect-io.h" -#include "collect-table.h" -#include "dnd.h" -#include "editors.h" -#include "img-view.h" -#include "filedata.h" -#include "layout.h" -#include "layout_image.h" -#include "menu.h" -#include "metadata.h" -#include "thumb.h" -#include "utilops.h" -#include "ui_fileops.h" -#include "ui_menu.h" -#include "ui_tree_edit.h" -#include "uri_utils.h" -#include "view_file.h" - -#include <gdk/gdkkeysyms.h> /* for keyboard values */ - - -/* between these, the icon width is increased by thumb_max_width / 2 */ -#define THUMB_MIN_ICON_WIDTH 128 -#define THUMB_MAX_ICON_WIDTH 150 - -#define VFICON_MAX_COLUMNS 32 -#define THUMB_BORDER_PADDING 2 - -#define VFICON_TIP_DELAY 500 - -enum { - FILE_COLUMN_POINTER = 0, - FILE_COLUMN_COUNT -}; - -static gint vficon_index_by_id(ViewFile *vf, IconData *in_id); - -static IconData *vficon_icon_data(ViewFile *vf, FileData *fd) -{ - IconData *id = NULL; - GList *work; - - if (!fd) return NULL; - work = vf->list; - while (work && !id) - { - IconData *chk = work->data; - work = work->next; - if (chk->fd == fd) id = chk; - } - return id; -} - -static void iconlist_free(GList *list) -{ - GList *work = list; - while (work) - { - IconData *id = work->data; - file_data_unref(id->fd); - g_free(id); - work = work->next; - } - - g_list_free(list); - -} - -gint iconlist_sort_file_cb(gpointer a, gpointer b) -{ - IconData *ida = a; - IconData *idb = b; - return filelist_sort_compare_filedata(ida->fd, idb->fd); -} - -GList *iconlist_sort(GList *list, SortType method, gboolean ascend) -{ - return filelist_sort_full(list, method, ascend, (GCompareFunc) iconlist_sort_file_cb); -} - -GList *iconlist_insert_sort(GList *list, IconData *id, SortType method, gboolean ascend) -{ - return filelist_insert_sort_full(list, id, method, ascend, (GCompareFunc) iconlist_sort_file_cb); -} - - -static void vficon_toggle_filenames(ViewFile *vf); -static void vficon_selection_remove(ViewFile *vf, IconData *id, SelectionType mask, GtkTreeIter *iter); -static void vficon_move_focus(ViewFile *vf, gint row, gint col, gboolean relative); -static void vficon_set_focus(ViewFile *vf, IconData *id); -static void vficon_populate_at_new_size(ViewFile *vf, gint w, gint h, gboolean force); - - -/* - *----------------------------------------------------------------------------- - * pop-up menu - *----------------------------------------------------------------------------- - */ - -GList *vficon_selection_get_one(ViewFile *vf, FileData *fd) -{ - return g_list_prepend(filelist_copy(fd->sidecar_files), file_data_ref(fd)); -} - -GList *vficon_pop_menu_file_list(ViewFile *vf) -{ - if (!VFICON(vf)->click_id) return NULL; - - if (VFICON(vf)->click_id->selected & SELECTION_SELECTED) - { - return vf_selection_get_list(vf); - } - - return vficon_selection_get_one(vf, VFICON(vf)->click_id->fd); -} - -void vficon_pop_menu_view_cb(GtkWidget *widget, gpointer data) -{ - ViewFile *vf = data; - - if (!VFICON(vf)->click_id) return; - - if (VFICON(vf)->click_id->selected & SELECTION_SELECTED) - { - GList *list; - - list = vf_selection_get_list(vf); - view_window_new_from_list(list); - filelist_free(list); - } - else - { - view_window_new(VFICON(vf)->click_id->fd); - } -} - -void vficon_pop_menu_rename_cb(GtkWidget *widget, gpointer data) -{ - ViewFile *vf = data; - - file_util_rename(NULL, vf_pop_menu_file_list(vf), vf->listview); -} - -void vficon_pop_menu_show_names_cb(GtkWidget *widget, gpointer data) -{ - ViewFile *vf = data; - - vficon_toggle_filenames(vf); -} - -void vficon_pop_menu_refresh_cb(GtkWidget *widget, gpointer data) -{ - ViewFile *vf = data; - - vf_refresh(vf); -} - -void vficon_popup_destroy_cb(GtkWidget *widget, gpointer data) -{ - ViewFile *vf = data; - vficon_selection_remove(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, NULL); - VFICON(vf)->click_id = NULL; - vf->popup = NULL; -} - -/* - *------------------------------------------------------------------- - * signals - *------------------------------------------------------------------- - */ - -static void vficon_send_layout_select(ViewFile *vf, IconData *id) -{ - FileData *read_ahead_fd = NULL; - FileData *sel_fd; - FileData *cur_fd; - - if (!vf->layout || !id || !id->fd) return; - - sel_fd = id->fd; - - cur_fd = layout_image_get_fd(vf->layout); - if (sel_fd == cur_fd) return; /* no change */ - - if (options->image.enable_read_ahead) - { - gint row; - - row = g_list_index(vf->list, id); - if (row > vficon_index_by_fd(vf, cur_fd) && - (guint) (row + 1) < vf_count(vf, NULL)) - { - read_ahead_fd = vf_index_get_data(vf, row + 1); - } - else if (row > 0) - { - read_ahead_fd = vf_index_get_data(vf, row - 1); - } - } - - layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd); -} - -static void vficon_toggle_filenames(ViewFile *vf) -{ - GtkAllocation allocation; - VFICON(vf)->show_text = !VFICON(vf)->show_text; - options->show_icon_names = VFICON(vf)->show_text; - - gtk_widget_get_allocation(vf->listview, &allocation); - vficon_populate_at_new_size(vf, allocation.width, allocation.height, TRUE); -} - -static gint vficon_get_icon_width(ViewFile *vf) -{ - gint width; - - if (!VFICON(vf)->show_text) return options->thumbnails.max_width; - - width = options->thumbnails.max_width + options->thumbnails.max_width / 2; - if (width < THUMB_MIN_ICON_WIDTH) width = THUMB_MIN_ICON_WIDTH; - if (width > THUMB_MAX_ICON_WIDTH) width = options->thumbnails.max_width; - - return width; -} - -/* - *------------------------------------------------------------------- - * misc utils - *------------------------------------------------------------------- - */ - -static gboolean vficon_find_position(ViewFile *vf, IconData *id, gint *row, gint *col) -{ - gint n; - - n = g_list_index(vf->list, id); - - if (n < 0) return FALSE; - - *row = n / VFICON(vf)->columns; - *col = n - (*row * VFICON(vf)->columns); - - return TRUE; -} - -static gboolean vficon_find_iter(ViewFile *vf, IconData *id, GtkTreeIter *iter, gint *column) -{ - GtkTreeModel *store; - gint row, col; - - store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); - if (!vficon_find_position(vf, id, &row, &col)) return FALSE; - if (!gtk_tree_model_iter_nth_child(store, iter, NULL, row)) return FALSE; - if (column) *column = col; - - return TRUE; -} - -static IconData *vficon_find_data(ViewFile *vf, gint row, gint col, GtkTreeIter *iter) -{ - GtkTreeModel *store; - GtkTreeIter p; - - if (row < 0 || col < 0) return NULL; - - store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); - if (gtk_tree_model_iter_nth_child(store, &p, NULL, row)) - { - GList *list; - - gtk_tree_model_get(store, &p, FILE_COLUMN_POINTER, &list, -1); - if (!list) return NULL; - - if (iter) *iter = p; - - return g_list_nth_data(list, col); - } - - return NULL; -} - -static IconData *vficon_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter) -{ - GtkTreePath *tpath; - GtkTreeViewColumn *column; - - if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), x, y, - &tpath, &column, NULL, NULL)) - { - GtkTreeModel *store; - GtkTreeIter row; - GList *list; - gint n; - - store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); - gtk_tree_model_get_iter(store, &row, tpath); - gtk_tree_path_free(tpath); - - gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &list, -1); - - n = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_number")); - if (list) - { - if (iter) *iter = row; - return g_list_nth_data(list, n); - } - } - - return NULL; -} - -static void vficon_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data) -{ - ViewFile *vf = data; - GtkTreeModel *store; - GtkTreePath *path = gtk_tree_path_new_from_string(path_str); - GtkTreeIter row; - gint column; - GList *list; - guint toggled_mark; - IconData *id; - - store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); - if (!path || !gtk_tree_model_get_iter(store, &row, path)) - return; - - gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &list, -1); - - column = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_number")); - g_object_get(G_OBJECT(cell), "toggled_mark", &toggled_mark, NULL); - - id = g_list_nth_data(list, column); - if (id) - { - FileData *fd = id->fd; - file_data_set_mark(fd, toggled_mark, !file_data_get_mark(fd, toggled_mark)); - } -} - - -/* - *------------------------------------------------------------------- - * tooltip type window - *------------------------------------------------------------------- - */ - -static void tip_show(ViewFile *vf) -{ - GtkWidget *label; - gint x, y; -#if GTK_CHECK_VERSION(3,0,0) - GdkDisplay *display; - GdkDeviceManager *device_manager; - GdkDevice *device; -#endif - - if (VFICON(vf)->tip_window) return; - -#if GTK_CHECK_VERSION(3,0,0) - device_manager = gdk_display_get_device_manager(gdk_window_get_display( - gtk_tree_view_get_bin_window(GTK_TREE_VIEW(vf->listview)))); - device = gdk_device_manager_get_client_pointer(device_manager); - gdk_window_get_device_position(gtk_tree_view_get_bin_window(GTK_TREE_VIEW(vf->listview)), - device, &x, &y, NULL); -#else - gdk_window_get_pointer(gtk_tree_view_get_bin_window(GTK_TREE_VIEW(vf->listview)), &x, &y, NULL); -#endif - - VFICON(vf)->tip_id = vficon_find_data_by_coord(vf, x, y, NULL); - if (!VFICON(vf)->tip_id) return; - - VFICON(vf)->tip_window = gtk_window_new(GTK_WINDOW_POPUP); - gtk_window_set_resizable(GTK_WINDOW(VFICON(vf)->tip_window), FALSE); - gtk_container_set_border_width(GTK_CONTAINER(VFICON(vf)->tip_window), 2); - - label = gtk_label_new(VFICON(vf)->tip_id->fd->name); - - g_object_set_data(G_OBJECT(VFICON(vf)->tip_window), "tip_label", label); - gtk_container_add(GTK_CONTAINER(VFICON(vf)->tip_window), label); - gtk_widget_show(label); - -#if GTK_CHECK_VERSION(3,0,0) - display = gdk_display_get_default(); - device_manager = gdk_display_get_device_manager(display); - device = gdk_device_manager_get_client_pointer(device_manager); - gdk_device_get_position(device, NULL, &x, &y); -#else - gdk_window_get_pointer(NULL, &x, &y, NULL); -#endif - - if (!gtk_widget_get_realized(VFICON(vf)->tip_window)) gtk_widget_realize(VFICON(vf)->tip_window); - gtk_window_move(GTK_WINDOW(VFICON(vf)->tip_window), x + 16, y + 16); - gtk_widget_show(VFICON(vf)->tip_window); -} - -static void tip_hide(ViewFile *vf) -{ - if (VFICON(vf)->tip_window) gtk_widget_destroy(VFICON(vf)->tip_window); - VFICON(vf)->tip_window = NULL; -} - -static gboolean tip_schedule_cb(gpointer data) -{ - ViewFile *vf = data; - GtkWidget *window; - - if (!VFICON(vf)->tip_delay_id) return FALSE; - - window = gtk_widget_get_toplevel(vf->listview); - - if (gtk_widget_get_sensitive(window) && - gtk_window_has_toplevel_focus(GTK_WINDOW(window))) - { - tip_show(vf); - } - - VFICON(vf)->tip_delay_id = 0; - return FALSE; -} - -static void tip_schedule(ViewFile *vf) -{ - tip_hide(vf); - - if (VFICON(vf)->tip_delay_id) - { - g_source_remove(VFICON(vf)->tip_delay_id); - VFICON(vf)->tip_delay_id = 0; - } - - if (!VFICON(vf)->show_text) - { - VFICON(vf)->tip_delay_id = g_timeout_add(VFICON_TIP_DELAY, tip_schedule_cb, vf); - } -} - -static void tip_unschedule(ViewFile *vf) -{ - tip_hide(vf); - - if (VFICON(vf)->tip_delay_id) - { - g_source_remove(VFICON(vf)->tip_delay_id); - VFICON(vf)->tip_delay_id = 0; - } -} - -static void tip_update(ViewFile *vf, IconData *id) -{ -#if GTK_CHECK_VERSION(3,0,0) - GdkDisplay *display = gdk_display_get_default(); - GdkDeviceManager *device_manager = gdk_display_get_device_manager(display); - GdkDevice *device = gdk_device_manager_get_client_pointer(device_manager); -#endif - - if (VFICON(vf)->tip_window) - { - gint x, y; - -#if GTK_CHECK_VERSION(3,0,0) - gdk_device_get_position(device, NULL, &x, &y); -#else - gdk_window_get_pointer(NULL, &x, &y, NULL); -#endif - gtk_window_move(GTK_WINDOW(VFICON(vf)->tip_window), x + 16, y + 16); - - if (id != VFICON(vf)->tip_id) - { - GtkWidget *label; - - VFICON(vf)->tip_id = id; - - if (!VFICON(vf)->tip_id) - { - tip_hide(vf); - tip_schedule(vf); - return; - } - - label = g_object_get_data(G_OBJECT(VFICON(vf)->tip_window), "tip_label"); - gtk_label_set_text(GTK_LABEL(label), VFICON(vf)->tip_id->fd->name); - } - } - else - { - tip_schedule(vf); - } -} - -/* - *------------------------------------------------------------------- - * dnd - *------------------------------------------------------------------- - */ - -static void vficon_dnd_get(GtkWidget *widget, GdkDragContext *context, - GtkSelectionData *selection_data, guint info, - guint time, gpointer data) -{ - ViewFile *vf = data; - GList *list = NULL; - - if (!VFICON(vf)->click_id) return; - - if (VFICON(vf)->click_id->selected & SELECTION_SELECTED) - { - list = vf_selection_get_list(vf); - } - else - { - list = g_list_append(NULL, file_data_ref(VFICON(vf)->click_id->fd)); - } - - if (!list) return; - uri_selection_data_set_uris_from_filelist(selection_data, list); - filelist_free(list); -} - -static void vficon_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context, - int x, int y, GtkSelectionData *selection, - guint info, guint time, gpointer data) -{ - ViewFile *vf = data; - - if (info == TARGET_TEXT_PLAIN) { - IconData *id = vficon_find_data_by_coord(vf, x, y, NULL); - - if (id && id->fd) { - /* Add keywords to file */ - FileData *fd = id->fd; - gchar *str = (gchar *) gtk_selection_data_get_text(selection); - GList *kw_list = string_to_keywords_list(str); - - metadata_append_list(fd, KEYWORD_KEY, kw_list); - string_list_free(kw_list); - g_free(str); - } - } -} - -static void vficon_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data) -{ - ViewFile *vf = data; - - tip_unschedule(vf); - - if (VFICON(vf)->click_id && VFICON(vf)->click_id->fd->thumb_pixbuf) - { - gint items; - - if (VFICON(vf)->click_id->selected & SELECTION_SELECTED) - items = g_list_length(VFICON(vf)->selection); - else - items = 1; - - dnd_set_drag_icon(widget, context, VFICON(vf)->click_id->fd->thumb_pixbuf, items); - } -} - -static void vficon_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data) -{ - ViewFile *vf = data; - - vficon_selection_remove(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, NULL); - - if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE) - { - vf_refresh(vf); - } - - tip_unschedule(vf); -} - -void vficon_dnd_init(ViewFile *vf) -{ - gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK, - dnd_file_drag_types, dnd_file_drag_types_count, - GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK); - gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL, - dnd_file_drag_types, dnd_file_drag_types_count, - GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK); - - g_signal_connect(G_OBJECT(vf->listview), "drag_data_get", - G_CALLBACK(vficon_dnd_get), vf); - g_signal_connect(G_OBJECT(vf->listview), "drag_begin", - G_CALLBACK(vficon_dnd_begin), vf); - g_signal_connect(G_OBJECT(vf->listview), "drag_end", - G_CALLBACK(vficon_dnd_end), vf); - g_signal_connect(G_OBJECT(vf->listview), "drag_data_received", - G_CALLBACK(vficon_drag_data_received), vf); -} - -/* - *------------------------------------------------------------------- - * cell updates - *------------------------------------------------------------------- - */ - -static void vficon_selection_set(ViewFile *vf, IconData *id, SelectionType value, GtkTreeIter *iter) -{ - GtkTreeModel *store; - GList *list; - - if (!id) return; - - - if (id->selected == value) return; - id->selected = value; - - store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); - if (iter) - { - gtk_tree_model_get(store, iter, FILE_COLUMN_POINTER, &list, -1); - if (list) gtk_list_store_set(GTK_LIST_STORE(store), iter, FILE_COLUMN_POINTER, list, -1); - } - else - { - GtkTreeIter row; - - if (vficon_find_iter(vf, id, &row, NULL)) - { - gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &list, -1); - if (list) gtk_list_store_set(GTK_LIST_STORE(store), &row, FILE_COLUMN_POINTER, list, -1); - } - } -} - -static void vficon_selection_add(ViewFile *vf, IconData *id, SelectionType mask, GtkTreeIter *iter) -{ - if (!id) return; - - vficon_selection_set(vf, id, id->selected | mask, iter); -} - -static void vficon_selection_remove(ViewFile *vf, IconData *id, SelectionType mask, GtkTreeIter *iter) -{ - if (!id) return; - - vficon_selection_set(vf, id, id->selected & ~mask, iter); -} - -void vficon_marks_set(ViewFile *vf, gint enable) -{ - GtkAllocation allocation; - gtk_widget_get_allocation(vf->listview, &allocation); - vficon_populate_at_new_size(vf, allocation.width, allocation.height, TRUE); -} - -/* - *------------------------------------------------------------------- - * selections - *------------------------------------------------------------------- - */ - -static void vficon_verify_selections(ViewFile *vf) -{ - GList *work; - - work = VFICON(vf)->selection; - while (work) - { - IconData *id = work->data; - work = work->next; - - if (vficon_index_by_id(vf, id) >= 0) continue; - - VFICON(vf)->selection = g_list_remove(VFICON(vf)->selection, id); - } -} - -void vficon_select_all(ViewFile *vf) -{ - GList *work; - - g_list_free(VFICON(vf)->selection); - VFICON(vf)->selection = NULL; - - work = vf->list; - while (work) - { - IconData *id = work->data; - work = work->next; - - VFICON(vf)->selection = g_list_append(VFICON(vf)->selection, id); - vficon_selection_add(vf, id, SELECTION_SELECTED, NULL); - } - - vf_send_update(vf); -} - -void vficon_select_none(ViewFile *vf) -{ - GList *work; - - work = VFICON(vf)->selection; - while (work) - { - IconData *id = work->data; - work = work->next; - - vficon_selection_remove(vf, id, SELECTION_SELECTED, NULL); - } - - g_list_free(VFICON(vf)->selection); - VFICON(vf)->selection = NULL; - - vf_send_update(vf); -} - -void vficon_select_invert(ViewFile *vf) -{ - GList *work; - - work = vf->list; - while (work) - { - IconData *id = work->data; - work = work->next; - - if (id->selected & SELECTION_SELECTED) - { - VFICON(vf)->selection = g_list_remove(VFICON(vf)->selection, id); - vficon_selection_remove(vf, id, SELECTION_SELECTED, NULL); - } - else - { - VFICON(vf)->selection = g_list_append(VFICON(vf)->selection, id); - vficon_selection_add(vf, id, SELECTION_SELECTED, NULL); - } - } - - vf_send_update(vf); -} - -static void vficon_select(ViewFile *vf, IconData *id) -{ - VFICON(vf)->prev_selection = id; - - if (!id || id->selected & SELECTION_SELECTED) return; - - VFICON(vf)->selection = g_list_append(VFICON(vf)->selection, id); - vficon_selection_add(vf, id, SELECTION_SELECTED, NULL); - - vf_send_update(vf); -} - -static void vficon_unselect(ViewFile *vf, IconData *id) -{ - VFICON(vf)->prev_selection = id; - - if (!id || !(id->selected & SELECTION_SELECTED) ) return; - - VFICON(vf)->selection = g_list_remove(VFICON(vf)->selection, id); - vficon_selection_remove(vf, id, SELECTION_SELECTED, NULL); - - vf_send_update(vf); -} - -static void vficon_select_util(ViewFile *vf, IconData *id, gboolean select) -{ - if (select) - { - vficon_select(vf, id); - } - else - { - vficon_unselect(vf, id); - } -} - -static void vficon_select_region_util(ViewFile *vf, IconData *start, IconData *end, gboolean select) -{ - gint row1, col1; - gint row2, col2; - gint t; - gint i, j; - - if (!vficon_find_position(vf, start, &row1, &col1) || - !vficon_find_position(vf, end, &row2, &col2) ) return; - - VFICON(vf)->prev_selection = end; - - if (!options->collections.rectangular_selection) - { - GList *work; - IconData *id; - - if (g_list_index(vf->list, start) > g_list_index(vf->list, end)) - { - id = start; - start = end; - end = id; - } - - work = g_list_find(vf->list, start); - while (work) - { - id = work->data; - vficon_select_util(vf, id, select); - - if (work->data != end) - work = work->next; - else - work = NULL; - } - return; - } - - if (row2 < row1) - { - t = row1; - row1 = row2; - row2 = t; - } - if (col2 < col1) - { - t = col1; - col1 = col2; - col2 = t; - } - - DEBUG_1("table: %d x %d to %d x %d", row1, col1, row2, col2); - - for (i = row1; i <= row2; i++) - { - for (j = col1; j <= col2; j++) - { - IconData *id = vficon_find_data(vf, i, j, NULL); - if (id) vficon_select_util(vf, id, select); - } - } -} - -gboolean vficon_index_is_selected(ViewFile *vf, gint row) -{ - IconData *id = g_list_nth_data(vf->list, row); - - if (!id) return FALSE; - - return (id->selected & SELECTION_SELECTED); -} - -guint vficon_selection_count(ViewFile *vf, gint64 *bytes) -{ - if (bytes) - { - gint64 b = 0; - GList *work; - - work = VFICON(vf)->selection; - while (work) - { - IconData *id = work->data; - FileData *fd = id->fd; - g_assert(fd->magick == FD_MAGICK); - b += fd->size; - - work = work->next; - } - - *bytes = b; - } - - return g_list_length(VFICON(vf)->selection); -} - -GList *vficon_selection_get_list(ViewFile *vf) -{ - GList *list = NULL; - GList *work, *work2; - - work = VFICON(vf)->selection; - while (work) - { - IconData *id = work->data; - FileData *fd = id->fd; - g_assert(fd->magick == FD_MAGICK); - - list = g_list_prepend(list, file_data_ref(fd)); - - work2 = fd->sidecar_files; - while (work2) - { - fd = work2->data; - list = g_list_prepend(list, file_data_ref(fd)); - work2 = work2->next; - } - - work = work->next; - } - - list = g_list_reverse(list); - - return list; -} - -GList *vficon_selection_get_list_by_index(ViewFile *vf) -{ - GList *list = NULL; - GList *work; - - work = VFICON(vf)->selection; - while (work) - { - list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, work->data))); - work = work->next; - } - - return g_list_reverse(list); -} - -static void vficon_select_by_id(ViewFile *vf, IconData *id) -{ - if (!id) return; - - if (!(id->selected & SELECTION_SELECTED)) - { - vf_select_none(vf); - vficon_select(vf, id); - } - - vficon_set_focus(vf, id); -} - -void vficon_select_by_fd(ViewFile *vf, FileData *fd) -{ - IconData *id = NULL; - GList *work; - - if (!fd) return; - work = vf->list; - while (work && !id) - { - IconData *chk = work->data; - work = work->next; - if (chk->fd == fd) id = chk; - } - vficon_select_by_id(vf, id); -} - -void vficon_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode) -{ - GList *work; - gint n = mark - 1; - - g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE); - - work = vf->list; - while (work) - { - IconData *id = work->data; - FileData *fd = id->fd; - gboolean mark_val, selected; - - g_assert(fd->magick == FD_MAGICK); - - mark_val = file_data_get_mark(fd, n); - selected = (id->selected & SELECTION_SELECTED); - - switch (mode) - { - case MTS_MODE_SET: selected = mark_val; - break; - case MTS_MODE_OR: selected = mark_val || selected; - break; - case MTS_MODE_AND: selected = mark_val && selected; - break; - case MTS_MODE_MINUS: selected = !mark_val && selected; - break; - } - - vficon_select_util(vf, id, selected); - - work = work->next; - } -} - -void vficon_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode) -{ - GList *slist; - GList *work; - gint n = mark -1; - - g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE); - - slist = vf_selection_get_list(vf); - work = slist; - while (work) - { - FileData *fd = work->data; - - switch (mode) - { - case STM_MODE_SET: file_data_set_mark(fd, n, 1); - break; - case STM_MODE_RESET: file_data_set_mark(fd, n, 0); - break; - case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n)); - break; - } - work = work->next; - } - filelist_free(slist); -} - -static void vficon_select_closest(ViewFile *vf, FileData *sel_fd) -{ - GList *work; - IconData *id = NULL; - - if (sel_fd->parent) sel_fd = sel_fd->parent; - work = vf->list; - - while (work) - { - gint match; - FileData *fd; - - id = work->data; - fd = id->fd; - work = work->next; - - match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend); - - if (match >= 0) break; - } - - if (id) - { - vficon_select(vf, id); - vficon_send_layout_select(vf, id); - } -} - - -/* - *------------------------------------------------------------------- - * focus - *------------------------------------------------------------------- - */ - -static void vficon_move_focus(ViewFile *vf, gint row, gint col, gboolean relative) -{ - gint new_row; - gint new_col; - - if (relative) - { - new_row = VFICON(vf)->focus_row; - new_col = VFICON(vf)->focus_column; - - new_row += row; - if (new_row < 0) new_row = 0; - if (new_row >= VFICON(vf)->rows) new_row = VFICON(vf)->rows - 1; - - while (col != 0) - { - if (col < 0) - { - new_col--; - col++; - } - else - { - new_col++; - col--; - } - - if (new_col < 0) - { - if (new_row > 0) - { - new_row--; - new_col = VFICON(vf)->columns - 1; - } - else - { - new_col = 0; - } - } - if (new_col >= VFICON(vf)->columns) - { - if (new_row < VFICON(vf)->rows - 1) - { - new_row++; - new_col = 0; - } - else - { - new_col = VFICON(vf)->columns - 1; - } - } - } - } - else - { - new_row = row; - new_col = col; - - if (new_row >= VFICON(vf)->rows) - { - if (VFICON(vf)->rows > 0) - new_row = VFICON(vf)->rows - 1; - else - new_row = 0; - new_col = VFICON(vf)->columns - 1; - } - if (new_col >= VFICON(vf)->columns) new_col = VFICON(vf)->columns - 1; - } - - if (new_row == VFICON(vf)->rows - 1) - { - gint l; - - /* if we moved beyond the last image, go to the last image */ - - l = g_list_length(vf->list); - if (VFICON(vf)->rows > 1) l -= (VFICON(vf)->rows - 1) * VFICON(vf)->columns; - if (new_col >= l) new_col = l - 1; - } - - vficon_set_focus(vf, vficon_find_data(vf, new_row, new_col, NULL)); -} - -static void vficon_set_focus(ViewFile *vf, IconData *id) -{ - GtkTreeIter iter; - gint row, col; - - if (g_list_find(vf->list, VFICON(vf)->focus_id)) - { - if (id == VFICON(vf)->focus_id) - { - /* ensure focus row col are correct */ - vficon_find_position(vf, VFICON(vf)->focus_id, &VFICON(vf)->focus_row, &VFICON(vf)->focus_column); -#if GTK_CHECK_VERSION(3,0,0) -/* FIXME: Refer to issue #467 on Github. The thumbnail position is not - * preserved when the icon view is refreshed. Caused by an unknown call from - * the idle loop. This patch hides the problem. - */ - if (vficon_find_iter(vf, VFICON(vf)->focus_id, &iter, NULL)) - { - tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, FALSE); - } -#endif - return; - } - vficon_selection_remove(vf, VFICON(vf)->focus_id, SELECTION_FOCUS, NULL); - } - - if (!vficon_find_position(vf, id, &row, &col)) - { - VFICON(vf)->focus_id = NULL; - VFICON(vf)->focus_row = -1; - VFICON(vf)->focus_column = -1; - return; - } - - VFICON(vf)->focus_id = id; - VFICON(vf)->focus_row = row; - VFICON(vf)->focus_column = col; - vficon_selection_add(vf, VFICON(vf)->focus_id, SELECTION_FOCUS, NULL); - - if (vficon_find_iter(vf, VFICON(vf)->focus_id, &iter, NULL)) - { - GtkTreePath *tpath; - GtkTreeViewColumn *column; - GtkTreeModel *store; - - tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, FALSE); - - store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); - tpath = gtk_tree_model_get_path(store, &iter); - /* focus is set to an extra column with 0 width to hide focus, we draw it ourself */ - column = gtk_tree_view_get_column(GTK_TREE_VIEW(vf->listview), VFICON_MAX_COLUMNS); - gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, column, FALSE); - gtk_tree_path_free(tpath); - } -} - -/* used to figure the page up/down distances */ -static gint page_height(ViewFile *vf) -{ - GtkAdjustment *adj; - gint page_size; - gint row_height; - gint ret; - - adj = gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(vf->listview)); - page_size = (gint)gtk_adjustment_get_page_increment(adj); - - row_height = options->thumbnails.max_height + THUMB_BORDER_PADDING * 2; - if (VFICON(vf)->show_text) row_height += options->thumbnails.max_height / 3; - - ret = page_size / row_height; - if (ret < 1) ret = 1; - - return ret; -} - -/* - *------------------------------------------------------------------- - * keyboard - *------------------------------------------------------------------- - */ - -static void vfi_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data) -{ - ViewFile *vf = data; - GtkTreeModel *store; - GtkTreeIter iter; - gint column; - GtkTreePath *tpath; - gint cw, ch; - - if (!vficon_find_iter(vf, VFICON(vf)->click_id, &iter, &column)) return; - store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); - tpath = gtk_tree_model_get_path(store, &iter); - tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, column, FALSE, x, y, &cw, &ch); - gtk_tree_path_free(tpath); - *y += ch; - popup_menu_position_clamp(menu, x, y, 0); -} - -gboolean vficon_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data) -{ - ViewFile *vf = data; - gint focus_row = 0; - gint focus_col = 0; - IconData *id; - gboolean stop_signal; - - stop_signal = TRUE; - switch (event->keyval) - { - case GDK_KEY_Left: case GDK_KEY_KP_Left: - focus_col = -1; - break; - case GDK_KEY_Right: case GDK_KEY_KP_Right: - focus_col = 1; - break; - case GDK_KEY_Up: case GDK_KEY_KP_Up: - focus_row = -1; - break; - case GDK_KEY_Down: case GDK_KEY_KP_Down: - focus_row = 1; - break; - case GDK_KEY_Page_Up: case GDK_KEY_KP_Page_Up: - focus_row = -page_height(vf); - break; - case GDK_KEY_Page_Down: case GDK_KEY_KP_Page_Down: - focus_row = page_height(vf); - break; - case GDK_KEY_Home: case GDK_KEY_KP_Home: - focus_row = -VFICON(vf)->focus_row; - focus_col = -VFICON(vf)->focus_column; - break; - case GDK_KEY_End: case GDK_KEY_KP_End: - focus_row = VFICON(vf)->rows - 1 - VFICON(vf)->focus_row; - focus_col = VFICON(vf)->columns - 1 - VFICON(vf)->focus_column; - break; - case GDK_KEY_space: - id = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL); - if (id) - { - VFICON(vf)->click_id = id; - if (event->state & GDK_CONTROL_MASK) - { - gint selected; - - selected = id->selected & SELECTION_SELECTED; - if (selected) - { - vficon_unselect(vf, id); - } - else - { - vficon_select(vf, id); - vficon_send_layout_select(vf, id); - } - } - else - { - vf_select_none(vf); - vficon_select(vf, id); - vficon_send_layout_select(vf, id); - } - } - break; - case GDK_KEY_Menu: - id = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL); - VFICON(vf)->click_id = id; - - vficon_selection_add(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, NULL); - tip_unschedule(vf); - - vf->popup = vf_pop_menu(vf); - gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vfi_menu_position_cb, vf, 0, GDK_CURRENT_TIME); - break; - default: - stop_signal = FALSE; - break; - } - - if (focus_row != 0 || focus_col != 0) - { - IconData *new_id; - IconData *old_id; - - old_id = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL); - vficon_move_focus(vf, focus_row, focus_col, TRUE); - new_id = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL); - - if (new_id != old_id) - { - if (event->state & GDK_SHIFT_MASK) - { - if (!options->collections.rectangular_selection) - { - vficon_select_region_util(vf, old_id, new_id, FALSE); - } - else - { - vficon_select_region_util(vf, VFICON(vf)->click_id, old_id, FALSE); - } - vficon_select_region_util(vf, VFICON(vf)->click_id, new_id, TRUE); - vficon_send_layout_select(vf, new_id); - } - else if (event->state & GDK_CONTROL_MASK) - { - VFICON(vf)->click_id = new_id; - } - else - { - VFICON(vf)->click_id = new_id; - vf_select_none(vf); - vficon_select(vf, new_id); - vficon_send_layout_select(vf, new_id); - } - } - } - - if (stop_signal) - { - tip_unschedule(vf); - } - - return stop_signal; -} - -/* - *------------------------------------------------------------------- - * mouse - *------------------------------------------------------------------- - */ - -static gboolean vficon_motion_cb(GtkWidget *widget, GdkEventMotion *event, gpointer data) -{ - ViewFile *vf = data; - IconData *id; - - id = vficon_find_data_by_coord(vf, (gint)event->x, (gint)event->y, NULL); - tip_update(vf, id); - - return FALSE; -} - -gboolean vficon_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data) -{ - ViewFile *vf = data; - GtkTreeIter iter; - IconData *id; - - tip_unschedule(vf); - - id = vficon_find_data_by_coord(vf, (gint)bevent->x, (gint)bevent->y, &iter); - - VFICON(vf)->click_id = id; - vficon_selection_add(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, &iter); - - switch (bevent->button) - { - case MOUSE_BUTTON_LEFT: - if (!gtk_widget_has_focus(vf->listview)) - { - gtk_widget_grab_focus(vf->listview); - } - - if (bevent->type == GDK_2BUTTON_PRESS && - vf->layout) - { - vficon_selection_remove(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, &iter); - layout_image_full_screen_start(vf->layout); - } - break; - case MOUSE_BUTTON_RIGHT: - vf->popup = vf_pop_menu(vf); - gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL, bevent->button, bevent->time); - break; - default: - break; - } - - return FALSE; -} - -gboolean vficon_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data) -{ - ViewFile *vf = data; - GtkTreeIter iter; - IconData *id = NULL; - gboolean was_selected; - - tip_schedule(vf); - - if ((gint)bevent->x != 0 || (gint)bevent->y != 0) - { - id = vficon_find_data_by_coord(vf, (gint)bevent->x, (gint)bevent->y, &iter); - } - - if (VFICON(vf)->click_id) - { - vficon_selection_remove(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, NULL); - } - - if (!id || VFICON(vf)->click_id != id) return TRUE; - - was_selected = !!(id->selected & SELECTION_SELECTED); - - switch (bevent->button) - { - case MOUSE_BUTTON_LEFT: - { - vficon_set_focus(vf, id); - - if (bevent->state & GDK_CONTROL_MASK) - { - gboolean select; - - select = !(id->selected & SELECTION_SELECTED); - if ((bevent->state & GDK_SHIFT_MASK) && VFICON(vf)->prev_selection) - { - vficon_select_region_util(vf, VFICON(vf)->prev_selection, id, select); - } - else - { - vficon_select_util(vf, id, select); - } - } - else - { - vf_select_none(vf); - - if ((bevent->state & GDK_SHIFT_MASK) && VFICON(vf)->prev_selection) - { - vficon_select_region_util(vf, VFICON(vf)->prev_selection, id, TRUE); - } - else - { - vficon_select_util(vf, id, TRUE); - was_selected = FALSE; - } - } - } - break; - case MOUSE_BUTTON_MIDDLE: - { - vficon_select_util(vf, id, !(id->selected & SELECTION_SELECTED)); - } - break; - default: - break; - } - - if (!was_selected && (id->selected & SELECTION_SELECTED)) - { - vficon_send_layout_select(vf, id); - } - - return TRUE; -} - -static gboolean vficon_leave_cb(GtkWidget *widget, GdkEventCrossing *event, gpointer data) -{ - ViewFile *vf = data; - - tip_unschedule(vf); - return FALSE; -} - -/* - *------------------------------------------------------------------- - * population - *------------------------------------------------------------------- - */ - -static gboolean vficon_destroy_node_cb(GtkTreeModel *store, GtkTreePath *tpath, GtkTreeIter *iter, gpointer data) -{ - GList *list; - - gtk_tree_model_get(store, iter, FILE_COLUMN_POINTER, &list, -1); - - /* it seems that gtk_list_store_clear may call some callbacks - that use the column. Set the pointer to NULL to be safe. */ - gtk_list_store_set(GTK_LIST_STORE(store), iter, FILE_COLUMN_POINTER, NULL, -1); - g_list_free(list); - - return FALSE; -} - -static void vficon_clear_store(ViewFile *vf) -{ - GtkTreeModel *store; - - store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); - gtk_tree_model_foreach(store, vficon_destroy_node_cb, NULL); - - gtk_list_store_clear(GTK_LIST_STORE(store)); -} - -static GList *vficon_add_row(ViewFile *vf, GtkTreeIter *iter) -{ - GtkListStore *store; - GList *list = NULL; - gint i; - - for (i = 0; i < VFICON(vf)->columns; i++) list = g_list_prepend(list, NULL); - - store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview))); - gtk_list_store_append(store, iter); - gtk_list_store_set(store, iter, FILE_COLUMN_POINTER, list, -1); - - return list; -} - -static void vficon_populate(ViewFile *vf, gboolean resize, gboolean keep_position) -{ - GtkTreeModel *store; - GtkTreePath *tpath; - GList *work; - IconData *visible_id = NULL; - gint r, c; - gboolean valid; - GtkTreeIter iter; - - vficon_verify_selections(vf); - - store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); - - if (keep_position && gtk_widget_get_realized(vf->listview) && - gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL)) - { - GtkTreeIter iter; - GList *list; - - gtk_tree_model_get_iter(store, &iter, tpath); - gtk_tree_path_free(tpath); - - gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1); - if (list) visible_id = list->data; - } - - - if (resize) - { - gint i; - gint thumb_width; - - vficon_clear_store(vf); - - thumb_width = vficon_get_icon_width(vf); - - for (i = 0; i < VFICON_MAX_COLUMNS; i++) - { - GtkTreeViewColumn *column; - GtkCellRenderer *cell; - GList *list; - - column = gtk_tree_view_get_column(GTK_TREE_VIEW(vf->listview), i); - gtk_tree_view_column_set_visible(column, (i < VFICON(vf)->columns)); - gtk_tree_view_column_set_fixed_width(column, thumb_width + (THUMB_BORDER_PADDING * 6)); - - list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column)); - cell = (list) ? list->data : NULL; - g_list_free(list); - - if (cell && GQV_IS_CELL_RENDERER_ICON(cell)) - { - g_object_set(G_OBJECT(cell), "fixed_width", thumb_width, - "fixed_height", options->thumbnails.max_height, - "show_text", VFICON(vf)->show_text, - "show_marks", vf->marks_enabled, - "num_marks", FILEDATA_MARKS_SIZE, - NULL); - } - } - if (gtk_widget_get_realized(vf->listview)) gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview)); - } - - r = -1; - c = 0; - - valid = gtk_tree_model_iter_children(store, &iter, NULL); - - work = vf->list; - while (work) - { - GList *list; - r++; - c = 0; - if (valid) - { - gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1); - gtk_list_store_set(GTK_LIST_STORE(store), &iter, FILE_COLUMN_POINTER, list, -1); - } - else - { - list = vficon_add_row(vf, &iter); - } - - while (list) - { - IconData *id; - - if (work) - { - id = work->data; - work = work->next; - c++; - } - else - { - id = NULL; - } - - list->data = id; - list = list->next; - } - if (valid) valid = gtk_tree_model_iter_next(store, &iter); - } - - r++; - while (valid) - { - GList *list; - - gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1); - valid = gtk_list_store_remove(GTK_LIST_STORE(store), &iter); - g_list_free(list); - } - - VFICON(vf)->rows = r; - - if (visible_id && - gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL)) - { - GtkTreeIter iter; - GList *list; - - gtk_tree_model_get_iter(store, &iter, tpath); - gtk_tree_path_free(tpath); - - gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1); - if (g_list_find(list, visible_id) == NULL && - vficon_find_iter(vf, visible_id, &iter, NULL)) - { - tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, FALSE); - } - } - - - vf_send_update(vf); - vf_thumb_update(vf); -} - -static void vficon_populate_at_new_size(ViewFile *vf, gint w, gint h, gboolean force) -{ - gint new_cols; - gint thumb_width; - - thumb_width = vficon_get_icon_width(vf); - - new_cols = w / (thumb_width + (THUMB_BORDER_PADDING * 6)); - if (new_cols < 1) new_cols = 1; - - if (!force && new_cols == VFICON(vf)->columns) return; - - VFICON(vf)->columns = new_cols; - - vficon_populate(vf, TRUE, TRUE); - - DEBUG_1("col tab pop cols=%d rows=%d", VFICON(vf)->columns, VFICON(vf)->rows); -} - -static void vficon_sized_cb(GtkWidget *widget, GtkAllocation *allocation, gpointer data) -{ - ViewFile *vf = data; - - vficon_populate_at_new_size(vf, allocation->width, allocation->height, FALSE); -} - -/* - *----------------------------------------------------------------------------- - * misc - *----------------------------------------------------------------------------- - */ - -void vficon_sort_set(ViewFile *vf, SortType type, gboolean ascend) -{ - if (vf->sort_method == type && vf->sort_ascend == ascend) return; - - vf->sort_method = type; - vf->sort_ascend = ascend; - - if (!vf->list) return; - - vf_refresh(vf); -} - -/* - *----------------------------------------------------------------------------- - * thumb updates - *----------------------------------------------------------------------------- - */ - -void vficon_thumb_progress_count(GList *list, gint *count, gint *done) -{ - GList *work = list; - while (work) - { - IconData *id = work->data; - FileData *fd = id->fd; - work = work->next; - - if (fd->thumb_pixbuf) (*done)++; - (*count)++; - } -} - -void vficon_set_thumb_fd(ViewFile *vf, FileData *fd) -{ - GtkTreeModel *store; - GtkTreeIter iter; - GList *list; - - if (!vficon_find_iter(vf, vficon_icon_data(vf, fd), &iter, NULL)) return; - - store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); - - gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1); - gtk_list_store_set(GTK_LIST_STORE(store), &iter, FILE_COLUMN_POINTER, list, -1); -} - - -FileData *vficon_thumb_next_fd(ViewFile *vf) -{ - GtkTreePath *tpath; - FileData *fd = NULL; - - if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL)) - { - GtkTreeModel *store; - GtkTreeIter iter; - gboolean valid = TRUE; - - store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); - gtk_tree_model_get_iter(store, &iter, tpath); - gtk_tree_path_free(tpath); - - while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0) - { - GList *list; - - gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1); - - while (!fd && list) - { - IconData *id = list->data; - if (id && !id->fd->thumb_pixbuf) fd = id->fd; - list = list->next; - } - - valid = gtk_tree_model_iter_next(store, &iter); - } - } - - /* then find first undone */ - - if (!fd) - { - GList *work = vf->list; - while (work && !fd) - { - IconData *id = work->data; - FileData *fd_p = id->fd; - work = work->next; - - if (!fd_p->thumb_pixbuf) fd = fd_p; - } - } - - return fd; -} - -void vficon_thumb_reset_all(ViewFile *vf) -{ - GList *work = vf->list; - - while (work) - { - IconData *id = work->data; - FileData *fd = id->fd; - if (fd->thumb_pixbuf) - { - g_object_unref(fd->thumb_pixbuf); - fd->thumb_pixbuf = NULL; - } - work = work->next; - } -} - - -/* - *----------------------------------------------------------------------------- - * row stuff - *----------------------------------------------------------------------------- - */ - -FileData *vficon_index_get_data(ViewFile *vf, gint row) -{ - IconData *id; - - id = g_list_nth_data(vf->list, row); - return id ? id->fd : NULL; -} - - -gint vficon_index_by_fd(ViewFile *vf, FileData *in_fd) -{ - gint p = 0; - GList *work; - - if (!in_fd) return -1; - - work = vf->list; - while (work) - { - IconData *id = work->data; - FileData *fd = id->fd; - if (fd == in_fd) return p; - work = work->next; - p++; - } - - return -1; -} - -static gint vficon_index_by_id(ViewFile *vf, IconData *in_id) -{ - gint p = 0; - GList *work; - - if (!in_id) return -1; - - work = vf->list; - while (work) - { - IconData *id = work->data; - if (id == in_id) return p; - work = work->next; - p++; - } - - return -1; -} - -guint vficon_count(ViewFile *vf, gint64 *bytes) -{ - if (bytes) - { - gint64 b = 0; - GList *work; - - work = vf->list; - while (work) - { - IconData *id = work->data; - FileData *fd = id->fd; - work = work->next; - - b += fd->size; - } - - *bytes = b; - } - - return g_list_length(vf->list); -} - -GList *vficon_get_list(ViewFile *vf) -{ - GList *list = NULL; - GList *work; - - work = vf->list; - while (work) - { - IconData *id = work->data; - FileData *fd = id->fd; - work = work->next; - - list = g_list_prepend(list, file_data_ref(fd)); - } - - return g_list_reverse(list); -} - -/* - *----------------------------------------------------------------------------- - * - *----------------------------------------------------------------------------- - */ - -static gboolean vficon_refresh_real(ViewFile *vf, gboolean keep_position) -{ - gboolean ret = TRUE; - GList *work, *work_fd; - IconData *focus_id; - GList *new_filelist = NULL; - FileData *first_selected = NULL; - GList *new_iconlist = NULL; - - focus_id = VFICON(vf)->focus_id; - - if (vf->dir_fd) - { - ret = filelist_read(vf->dir_fd, &new_filelist, NULL); - new_filelist = file_data_filter_marks_list(new_filelist, vf_marks_get_filter(vf)); - } - - vf->list = iconlist_sort(vf->list, vf->sort_method, vf->sort_ascend); /* the list might not be sorted if there were renames */ - new_filelist = filelist_sort(new_filelist, vf->sort_method, vf->sort_ascend); - - if (VFICON(vf)->selection) - { - first_selected = ((IconData *)(VFICON(vf)->selection->data))->fd; - file_data_ref(first_selected); - g_list_free(VFICON(vf)->selection); - VFICON(vf)->selection = NULL; - - - } - - /* check for same files from old_list */ - work = vf->list; - work_fd = new_filelist; - while (work || work_fd) - { - IconData *id = NULL; - FileData *fd = NULL; - FileData *new_fd = NULL; - gint match; - - if (work && work_fd) - { - id = work->data; - fd = id->fd; - - new_fd = work_fd->data; - - if (fd == new_fd) - { - /* not changed, go to next */ - work = work->next; - work_fd = work_fd->next; - if (id->selected & SELECTION_SELECTED) - { - VFICON(vf)->selection = g_list_prepend(VFICON(vf)->selection, id); - } - continue; - } - - match = filelist_sort_compare_filedata_full(fd, new_fd, vf->sort_method, vf->sort_ascend); - if (match == 0) g_warning("multiple fd for the same path"); - } - else if (work) - { - id = work->data; - fd = id->fd; - match = -1; - } - else /* work_fd */ - { - new_fd = work_fd->data; - match = 1; - } - - if (match < 0) - { - /* file no longer exists, delete from vf->list */ - GList *to_delete = work; - work = work->next; - if (id == VFICON(vf)->prev_selection) VFICON(vf)->prev_selection = NULL; - if (id == VFICON(vf)->click_id) VFICON(vf)->click_id = NULL; - file_data_unref(fd); - g_free(id); - vf->list = g_list_delete_link(vf->list, to_delete); - } - else - { - /* new file, add to vf->list */ - id = g_new0(IconData, 1); - - id->selected = SELECTION_NONE; - id->fd = file_data_ref(new_fd); - if (work) - vf->list = g_list_insert_before(vf->list, work, id); - else - new_iconlist = g_list_prepend(new_iconlist, id); /* it is faster to append all new entries together later */ - - work_fd = work_fd->next; - } - - } - - if (new_iconlist) - { - vf->list = g_list_concat(vf->list, g_list_reverse(new_iconlist)); - } - - VFICON(vf)->selection = g_list_reverse(VFICON(vf)->selection); - - filelist_free(new_filelist); - - vficon_populate(vf, TRUE, keep_position); - - if (first_selected && !VFICON(vf)->selection) - { - /* all selected files disappeared */ - vficon_select_closest(vf, first_selected); - } - file_data_unref(first_selected); - - /* attempt to keep focus on same icon when refreshing */ - if (focus_id && g_list_find(vf->list, focus_id)) - { - vficon_set_focus(vf, focus_id); - } - - return ret; -} - -gboolean vficon_refresh(ViewFile *vf) -{ - return vficon_refresh_real(vf, TRUE); -} - -/* - *----------------------------------------------------------------------------- - * draw, etc. - *----------------------------------------------------------------------------- - */ - -typedef struct _ColumnData ColumnData; -struct _ColumnData -{ - ViewFile *vf; - gint number; -}; - -static void vficon_cell_data_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell, - GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data) -{ - GList *list; - IconData *id; - ColumnData *cd = data; - ViewFile *vf = cd->vf; - - if (!GQV_IS_CELL_RENDERER_ICON(cell)) return; - - gtk_tree_model_get(tree_model, iter, FILE_COLUMN_POINTER, &list, -1); - - id = g_list_nth_data(list, cd->number); - - if (id) - { - GdkColor color_fg; - GdkColor color_bg; - GtkStyle *style; - gchar *name_sidecars; - gchar *link; - GtkStateType state = GTK_STATE_NORMAL; - - g_assert(id->fd->magick == FD_MAGICK); - - link = islink(id->fd->path) ? GQ_LINK_STR : ""; - if (id->fd->sidecar_files) - { - gchar *sidecars = file_data_sc_list_to_string(id->fd); - name_sidecars = g_strdup_printf("%s%s %s", link, id->fd->name, sidecars); - g_free(sidecars); - } - else - { - gchar *disabled_grouping = id->fd->disable_grouping ? _(" [NO GROUPING]") : ""; - name_sidecars = g_strdup_printf("%s%s%s", link, id->fd->name, disabled_grouping); - } - - style = gtk_widget_get_style(vf->listview); - if (id->selected & SELECTION_SELECTED) - { - state = GTK_STATE_SELECTED; - } - - memcpy(&color_fg, &style->text[state], sizeof(color_fg)); - memcpy(&color_bg, &style->base[state], sizeof(color_bg)); - - if (id->selected & SELECTION_PRELIGHT) - { - shift_color(&color_bg, -1, 0); - } - - g_object_set(cell, "pixbuf", id->fd->thumb_pixbuf, - "text", name_sidecars, - "marks", file_data_get_marks(id->fd), - "show_marks", vf->marks_enabled, - "cell-background-gdk", &color_bg, - "cell-background-set", TRUE, - "foreground-gdk", &color_fg, - "foreground-set", TRUE, - "has-focus", (VFICON(vf)->focus_id == id), NULL); - g_free(name_sidecars); - } - else - { - g_object_set(cell, "pixbuf", NULL, - "text", NULL, - "show_marks", FALSE, - "cell-background-set", FALSE, - "foreground-set", FALSE, - "has-focus", FALSE, NULL); - } -} - -static void vficon_append_column(ViewFile *vf, gint n) -{ - ColumnData *cd; - GtkTreeViewColumn *column; - GtkCellRenderer *renderer; - - column = gtk_tree_view_column_new(); - gtk_tree_view_column_set_min_width(column, 0); - - gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); - gtk_tree_view_column_set_alignment(column, 0.5); - - renderer = gqv_cell_renderer_icon_new(); - gtk_tree_view_column_pack_start(column, renderer, FALSE); - g_object_set(G_OBJECT(renderer), "xpad", THUMB_BORDER_PADDING * 2, - "ypad", THUMB_BORDER_PADDING, - "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL); - - g_object_set_data(G_OBJECT(column), "column_number", GINT_TO_POINTER(n)); - g_object_set_data(G_OBJECT(renderer), "column_number", GINT_TO_POINTER(n)); - - cd = g_new0(ColumnData, 1); - cd->vf = vf; - cd->number = n; - gtk_tree_view_column_set_cell_data_func(column, renderer, vficon_cell_data_cb, cd, g_free); - - gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column); - - g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vficon_mark_toggled_cb), vf); -} - -/* - *----------------------------------------------------------------------------- - * base - *----------------------------------------------------------------------------- - */ - -gboolean vficon_set_fd(ViewFile *vf, FileData *dir_fd) -{ - gboolean ret; - - if (!dir_fd) return FALSE; - if (vf->dir_fd == dir_fd) return TRUE; - - file_data_unref(vf->dir_fd); - vf->dir_fd = file_data_ref(dir_fd); - - g_list_free(VFICON(vf)->selection); - VFICON(vf)->selection = NULL; - - iconlist_free(vf->list); - vf->list = NULL; - - /* NOTE: populate will clear the store for us */ - ret = vficon_refresh_real(vf, FALSE); - - VFICON(vf)->focus_id = NULL; - vficon_move_focus(vf, 0, 0, FALSE); - - return ret; -} - -void vficon_destroy_cb(GtkWidget *widget, gpointer data) -{ - ViewFile *vf = data; - - vf_refresh_idle_cancel(vf); - - file_data_unregister_notify_func(vf_notify_cb, vf); - - tip_unschedule(vf); - - vf_thumb_cleanup(vf); - - iconlist_free(vf->list); - g_list_free(VFICON(vf)->selection); -} - -ViewFile *vficon_new(ViewFile *vf, FileData *dir_fd) -{ - GtkListStore *store; - GtkTreeSelection *selection; - gint i; - - vf->info = g_new0(ViewFileInfoIcon, 1); - - VFICON(vf)->show_text = options->show_icon_names; - - store = gtk_list_store_new(1, G_TYPE_POINTER); - vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); - g_object_unref(store); - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview)); - gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_NONE); - - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE); - gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE); - - for (i = 0; i < VFICON_MAX_COLUMNS; i++) - { - vficon_append_column(vf, i); - } - - /* zero width column to hide tree view focus, we draw it ourselves */ - vficon_append_column(vf, i); - /* end column to fill white space */ - vficon_append_column(vf, i); - - g_signal_connect(G_OBJECT(vf->listview), "size_allocate", - G_CALLBACK(vficon_sized_cb), vf); - - gtk_widget_set_events(vf->listview, GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK | - GDK_BUTTON_PRESS_MASK | GDK_LEAVE_NOTIFY_MASK); - - g_signal_connect(G_OBJECT(vf->listview),"motion_notify_event", - G_CALLBACK(vficon_motion_cb), vf); - g_signal_connect(G_OBJECT(vf->listview), "leave_notify_event", - G_CALLBACK(vficon_leave_cb), vf); - - /* force VFICON(vf)->columns to be at least 1 (sane) - this will be corrected in the size_cb */ - vficon_populate_at_new_size(vf, 1, 1, FALSE); - - file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM); - - return vf; -} - -/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- a/src/view_file_icon.h Thu Jun 29 11:05:59 2017 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2004 John Ellis - * Copyright (C) 2008 - 2016 The Geeqie Team - * - * Author: John Ellis - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef VIEW_FILE_ICON_H -#define VIEW_FILE_ICON_H - -gboolean vficon_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data); -gboolean vficon_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data); -gboolean vficon_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data); - -void vficon_dnd_init(ViewFile *vf); - -void vficon_destroy_cb(GtkWidget *widget, gpointer data); -ViewFile *vficon_new(ViewFile *vf, FileData *dir_fd); - -gboolean vficon_set_fd(ViewFile *vf, FileData *dir_fd); -gboolean vficon_refresh(ViewFile *vf); - -void vficon_sort_set(ViewFile *vf, SortType type, gboolean ascend); - -void vficon_marks_set(ViewFile *vf, gboolean enable); - -GList *vficon_selection_get_one(ViewFile *vf, FileData *fd); -GList *vficon_pop_menu_file_list(ViewFile *vf); -void vficon_pop_menu_view_cb(GtkWidget *widget, gpointer data); -void vficon_pop_menu_rename_cb(GtkWidget *widget, gpointer data); -void vficon_pop_menu_refresh_cb(GtkWidget *widget, gpointer data); -void vficon_popup_destroy_cb(GtkWidget *widget, gpointer data); -void vficon_pop_menu_show_names_cb(GtkWidget *widget, gpointer data); - -FileData *vficon_index_get_data(ViewFile *vf, gint row); -gint vficon_index_by_fd(ViewFile *vf, FileData *in_fd); -guint vficon_count(ViewFile *vf, gint64 *bytes); -GList *vficon_get_list(ViewFile *vf); - -gboolean vficon_index_is_selected(ViewFile *vf, gint row); -guint vficon_selection_count(ViewFile *vf, gint64 *bytes); -GList *vficon_selection_get_list(ViewFile *vf); -GList *vficon_selection_get_list_by_index(ViewFile *vf); - -void vficon_select_all(ViewFile *vf); -void vficon_select_none(ViewFile *vf); -void vficon_select_invert(ViewFile *vf); -void vficon_select_by_fd(ViewFile *vf, FileData *fd); - -void vficon_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode); -void vficon_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode); - - -void vficon_thumb_progress_count(GList *list, gint *count, gint *done); -void vficon_set_thumb_fd(ViewFile *vf, FileData *fd); -FileData *vficon_thumb_next_fd(ViewFile *vf); -void vficon_thumb_reset_all(ViewFile *vf); - -#endif -/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- a/src/view_file_list.c Thu Jun 29 11:05:59 2017 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2052 +0,0 @@ -/* - * Copyright (C) 2004 John Ellis - * Copyright (C) 2008 - 2016 The Geeqie Team - * - * Author: John Ellis - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "main.h" -#include "view_file_list.h" - -#include "bar.h" -#include "cache_maint.h" -#include "dnd.h" -#include "editors.h" -#include "img-view.h" -#include "layout.h" -#include "layout_image.h" -#include "menu.h" -#include "metadata.h" -#include "thumb.h" -#include "utilops.h" -#include "ui_fileops.h" -#include "ui_menu.h" -#include "ui_tree_edit.h" -#include "uri_utils.h" -#include "view_file.h" - -#include <gdk/gdkkeysyms.h> /* for keyboard values */ - -/* Index to tree store */ -enum { - FILE_COLUMN_POINTER = 0, - FILE_COLUMN_VERSION, - FILE_COLUMN_THUMB, - FILE_COLUMN_FORMATTED, - FILE_COLUMN_NAME, - FILE_COLUMN_SIDECARS, - FILE_COLUMN_SIZE, - FILE_COLUMN_DATE, - FILE_COLUMN_EXPANDED, - FILE_COLUMN_COLOR, - FILE_COLUMN_MARKS, - FILE_COLUMN_MARKS_LAST = FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1, - FILE_COLUMN_COUNT -}; - - -/* Index to tree view */ -enum { - FILE_VIEW_COLUMN_MARKS = 0, - FILE_VIEW_COLUMN_MARKS_LAST = FILE_VIEW_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1, - FILE_VIEW_COLUMN_THUMB, - FILE_VIEW_COLUMN_FORMATTED, - FILE_VIEW_COLUMN_SIZE, - FILE_VIEW_COLUMN_DATE, - FILE_VIEW_COLUMN_COUNT -}; - - - -static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd); -static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data); -static void vflist_populate_view(ViewFile *vf, gboolean force); -static gboolean vflist_is_multiline(ViewFile *vf); -static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded); - - -/* - *----------------------------------------------------------------------------- - * misc - *----------------------------------------------------------------------------- - */ -typedef struct { - FileData *fd; - GtkTreeIter *iter; - gboolean found; - gint row; -} ViewFileFindRowData; - -static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) -{ - ViewFileFindRowData *find = data; - FileData *fd; - gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1); - if (fd == find->fd) - { - *find->iter = *iter; - find->found = TRUE; - return TRUE; - } - find->row++; - return FALSE; -} - -static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter) -{ - GtkTreeModel *store; - ViewFileFindRowData data = {fd, iter, FALSE, 0}; - - store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); - gtk_tree_model_foreach(store, vflist_find_row_cb, &data); - - if (data.found) - { - return data.row; - } - - return -1; -} - -static FileData *vflist_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter) -{ - GtkTreePath *tpath; - GtkTreeViewColumn *column; - - if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), x, y, - &tpath, &column, NULL, NULL)) - { - GtkTreeModel *store; - GtkTreeIter row; - FileData *fd; - - store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); - gtk_tree_model_get_iter(store, &row, tpath); - gtk_tree_path_free(tpath); - gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &fd, -1); - - return fd; - } - - return NULL; -} - -static gboolean vflist_store_clear_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) -{ - FileData *fd; - gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1); - - /* it seems that gtk_tree_store_clear may call some callbacks - that use the column. Set the pointer to NULL to be safe. */ - gtk_tree_store_set(GTK_TREE_STORE(model), iter, FILE_COLUMN_POINTER, NULL, -1); - file_data_unref(fd); - return FALSE; -} - -static void vflist_store_clear(ViewFile *vf, gboolean unlock_files) -{ - GtkTreeModel *store; - GList *files = NULL; - - if (unlock_files && vf->marks_enabled) - { - // unlock locked files in this directory - filelist_read(vf->dir_fd, &files, NULL); - while (files) - { - FileData *fd = files->data; - files = files->next; - file_data_unlock(fd); - file_data_unref(fd); // undo the ref that got added in filelist_read - } - } - - g_list_free(files); - store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); - gtk_tree_model_foreach(store, vflist_store_clear_cb, NULL); - gtk_tree_store_clear(GTK_TREE_STORE(store)); -} - -void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set) -{ - GtkTreeModel *store; - GtkTreeIter iter; - - if (vflist_find_row(vf, fd, &iter) < 0) return; - store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); - gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1); -} - -static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter) -{ - GtkTreeModel *store; - GtkTreePath *tpath; - - store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); - - tpath = gtk_tree_model_get_path(store, iter); - gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE); - gtk_tree_path_free(tpath); -} - - -/* - *----------------------------------------------------------------------------- - * dnd - *----------------------------------------------------------------------------- - */ - -static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context, - GtkSelectionData *selection_data, guint info, - guint time, gpointer data) -{ - ViewFile *vf = data; - GList *list = NULL; - - if (!VFLIST(vf)->click_fd) return; - - if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd)) - { - list = vf_selection_get_list(vf); - } - else - { - list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd)); - } - - if (!list) return; - uri_selection_data_set_uris_from_filelist(selection_data, list); - filelist_free(list); -} - -static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data) -{ - ViewFile *vf = data; - - vflist_color_set(vf, VFLIST(vf)->click_fd, TRUE); - - if (VFLIST(vf)->thumbs_enabled && - VFLIST(vf)->click_fd && VFLIST(vf)->click_fd->thumb_pixbuf) - { - guint items; - - if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd)) - items = vf_selection_count(vf, NULL); - else - items = 1; - - dnd_set_drag_icon(widget, context, VFLIST(vf)->click_fd->thumb_pixbuf, items); - } -} - -static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data) -{ - ViewFile *vf = data; - - vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE); - - if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE) - { - vf_refresh(vf); - } -} - -static void vflist_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context, - int x, int y, GtkSelectionData *selection, - guint info, guint time, gpointer data) -{ - ViewFile *vf = data; - - if (info == TARGET_TEXT_PLAIN) { - FileData *fd = vflist_find_data_by_coord(vf, x, y, NULL); - - if (fd) { - /* Add keywords to file */ - gchar *str = (gchar *) gtk_selection_data_get_text(selection); - GList *kw_list = string_to_keywords_list(str); - - metadata_append_list(fd, KEYWORD_KEY, kw_list); - string_list_free(kw_list); - g_free(str); - } - } -} - -void vflist_dnd_init(ViewFile *vf) -{ - gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK, - dnd_file_drag_types, dnd_file_drag_types_count, - GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK); - gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL, - dnd_file_drag_types, dnd_file_drag_types_count, - GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK); - - g_signal_connect(G_OBJECT(vf->listview), "drag_data_get", - G_CALLBACK(vflist_dnd_get), vf); - g_signal_connect(G_OBJECT(vf->listview), "drag_begin", - G_CALLBACK(vflist_dnd_begin), vf); - g_signal_connect(G_OBJECT(vf->listview), "drag_end", - G_CALLBACK(vflist_dnd_end), vf); - g_signal_connect(G_OBJECT(vf->listview), "drag_data_received", - G_CALLBACK(vflist_drag_data_received), vf); -} - -/* - *----------------------------------------------------------------------------- - * pop-up menu - *----------------------------------------------------------------------------- - */ - -GList *vflist_selection_get_one(ViewFile *vf, FileData *fd) -{ - GList *list = g_list_append(NULL, file_data_ref(fd)); - - if (fd->sidecar_files) - { - /* check if the row is expanded */ - GtkTreeModel *store; - GtkTreeIter iter; - - store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); - if (vflist_find_row(vf, fd, &iter) >= 0) - { - GtkTreePath *tpath; - - tpath = gtk_tree_model_get_path(store, &iter); - if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath)) - { - /* unexpanded - add whole group */ - GList *work = fd->sidecar_files; - while (work) - { - FileData *sfd = work->data; - list = g_list_prepend(list, file_data_ref(sfd)); - work = work->next; - } - } - gtk_tree_path_free(tpath); - } - list = g_list_reverse(list); - } - - return list; -} - -GList *vflist_pop_menu_file_list(ViewFile *vf) -{ - if (!VFLIST(vf)->click_fd) return NULL; - - if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd)) - { - return vf_selection_get_list(vf); - } - return vflist_selection_get_one(vf, VFLIST(vf)->click_fd); -} - - -void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data) -{ - ViewFile *vf = data; - - if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd)) - { - GList *list; - - list = vf_selection_get_list(vf); - view_window_new_from_list(list); - filelist_free(list); - } - else - { - view_window_new(VFLIST(vf)->click_fd); - } -} - -void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data) -{ - ViewFile *vf = data; - GList *list; - - list = vf_pop_menu_file_list(vf); - if (options->file_ops.enable_in_place_rename && - list && !list->next && VFLIST(vf)->click_fd) - { - GtkTreeModel *store; - GtkTreeIter iter; - - filelist_free(list); - - store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); - if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0) - { - GtkTreePath *tpath; - - tpath = gtk_tree_model_get_path(store, &iter); - tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath, - FILE_VIEW_COLUMN_FORMATTED, VFLIST(vf)->click_fd->name, - vflist_row_rename_cb, vf); - gtk_tree_path_free(tpath); - } - return; - } - - file_util_rename(NULL, list, vf->listview); -} - -void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data) -{ - ViewFile *vf = data; - - vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE); - if (vf->layout) - { - layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled); - } - else - { - vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled); - } -} - -void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data) -{ - ViewFile *vf = data; - - vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE); - vf_refresh(vf); - gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview)); -} - -void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data) -{ - ViewFile *vf = data; - vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE); - VFLIST(vf)->click_fd = NULL; - vf->popup = NULL; -} - - -/* - *----------------------------------------------------------------------------- - * callbacks - *----------------------------------------------------------------------------- - */ - -static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data) -{ - ViewFile *vf = data; - gchar *new_path; - - if (!new || !new[0]) return FALSE; - - new_path = g_build_filename(vf->dir_fd->path, new, NULL); - - if (strchr(new, G_DIR_SEPARATOR) != NULL) - { - gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new); - file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview); - g_free(text); - } - else - { - gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL); - FileData *fd = file_data_new_group(old_path); /* get the fd from cache */ - file_util_rename_simple(fd, new_path, vf->listview); - file_data_unref(fd); - g_free(old_path); - } - - g_free(new_path); - - return FALSE; -} - -static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data) -{ - ViewFile *vf = data; - GtkTreeModel *store; - GtkTreeIter iter; - GtkTreePath *tpath; - gint cw, ch; - - if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return; - store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); - tpath = gtk_tree_model_get_path(store, &iter); - tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch); - gtk_tree_path_free(tpath); - *y += ch; - popup_menu_position_clamp(menu, x, y, 0); -} - -gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data) -{ - ViewFile *vf = data; - GtkTreePath *tpath; - - if (event->keyval != GDK_KEY_Menu) return FALSE; - - gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL); - if (tpath) - { - GtkTreeModel *store; - GtkTreeIter iter; - - store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget)); - gtk_tree_model_get_iter(store, &iter, tpath); - gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1); - gtk_tree_path_free(tpath); - } - else - { - VFLIST(vf)->click_fd = NULL; - } - - vf->popup = vf_pop_menu(vf); - gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME); - - return TRUE; -} - -gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data) -{ - ViewFile *vf = data; - GtkTreePath *tpath; - GtkTreeIter iter; - FileData *fd = NULL; - GtkTreeViewColumn *column; - - vf->clicked_mark = 0; - - if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y, - &tpath, &column, NULL, NULL)) - { - GtkTreeModel *store; - gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx")); - - if (bevent->button == MOUSE_BUTTON_LEFT && - col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST) - return FALSE; - - if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST) - vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS); - - store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget)); - - gtk_tree_model_get_iter(store, &iter, tpath); - gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1); - gtk_tree_path_free(tpath); - } - - VFLIST(vf)->click_fd = fd; - - if (bevent->button == MOUSE_BUTTON_RIGHT) - { - vf->popup = vf_pop_menu(vf); - gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL, - bevent->button, bevent->time); - return TRUE; - } - - if (!fd) return FALSE; - - if (bevent->button == MOUSE_BUTTON_MIDDLE) - { - if (!vflist_row_is_selected(vf, fd)) - { - vflist_color_set(vf, fd, TRUE); - } - return TRUE; - } - - - if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS && - !(bevent->state & GDK_SHIFT_MASK ) && - !(bevent->state & GDK_CONTROL_MASK ) && - vflist_row_is_selected(vf, fd)) - { - GtkTreeSelection *selection; - - gtk_widget_grab_focus(widget); - - - /* returning FALSE and further processing of the event is needed for - correct operation of the expander, to show the sidecar files. - It however resets the selection of multiple files. With this condition - it should work for both cases */ - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); - return (gtk_tree_selection_count_selected_rows(selection) > 1); - } - - if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS) - { - if (vf->layout) layout_image_full_screen_start(vf->layout); - } - - return FALSE; -} - -gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data) -{ - ViewFile *vf = data; - GtkTreePath *tpath; - GtkTreeIter iter; - FileData *fd = NULL; - - if (bevent->button == MOUSE_BUTTON_MIDDLE) - { - vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE); - } - - if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE) - { - return TRUE; - } - - if ((bevent->x != 0 || bevent->y != 0) && - gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y, - &tpath, NULL, NULL, NULL)) - { - GtkTreeModel *store; - - store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget)); - gtk_tree_model_get_iter(store, &iter, tpath); - gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1); - gtk_tree_path_free(tpath); - } - - if (bevent->button == MOUSE_BUTTON_MIDDLE) - { - if (fd && VFLIST(vf)->click_fd == fd) - { - GtkTreeSelection *selection; - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); - if (vflist_row_is_selected(vf, fd)) - { - gtk_tree_selection_unselect_iter(selection, &iter); - } - else - { - gtk_tree_selection_select_iter(selection, &iter); - } - } - return TRUE; - } - - if (fd && VFLIST(vf)->click_fd == fd && - !(bevent->state & GDK_SHIFT_MASK ) && - !(bevent->state & GDK_CONTROL_MASK ) && - vflist_row_is_selected(vf, fd)) - { - GtkTreeSelection *selection; - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); - gtk_tree_selection_unselect_all(selection); - gtk_tree_selection_select_iter(selection, &iter); - vflist_move_cursor(vf, &iter); - } - - return FALSE; -} - -static void vflist_select_image(ViewFile *vf, FileData *sel_fd) -{ - FileData *read_ahead_fd = NULL; - gint row; - FileData *cur_fd; - - if (!sel_fd) return; - - cur_fd = layout_image_get_fd(vf->layout); - if (sel_fd == cur_fd) return; /* no change */ - - row = g_list_index(vf->list, sel_fd); - // FIXME sidecar data - - if (sel_fd && options->image.enable_read_ahead && row >= 0) - { - if (row > g_list_index(vf->list, cur_fd) && - (guint) (row + 1) < vf_count(vf, NULL)) - { - read_ahead_fd = vf_index_get_data(vf, row + 1); - } - else if (row > 0) - { - read_ahead_fd = vf_index_get_data(vf, row - 1); - } - } - - layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd); -} - -static gboolean vflist_select_idle_cb(gpointer data) -{ - ViewFile *vf = data; - - if (!vf->layout) - { - VFLIST(vf)->select_idle_id = 0; - return FALSE; - } - - vf_send_update(vf); - - if (VFLIST(vf)->select_fd) - { - vflist_select_image(vf, VFLIST(vf)->select_fd); - VFLIST(vf)->select_fd = NULL; - } - - VFLIST(vf)->select_idle_id = 0; - return FALSE; -} - -static void vflist_select_idle_cancel(ViewFile *vf) -{ - if (VFLIST(vf)->select_idle_id) - { - g_source_remove(VFLIST(vf)->select_idle_id); - VFLIST(vf)->select_idle_id = 0; - } -} - -static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath, - gboolean path_currently_selected, gpointer data) -{ - ViewFile *vf = data; - GtkTreeIter iter; - - if (!path_currently_selected && - gtk_tree_model_get_iter(store, &iter, tpath)) - { - gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1); - } - else - { - VFLIST(vf)->select_fd = NULL; - } - - if (vf->layout && - !VFLIST(vf)->select_idle_id) - { - VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf); - } - - return TRUE; -} - -static void vflist_expand_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data) -{ - ViewFile *vf = data; - vflist_set_expanded(vf, iter, TRUE); -} - -static void vflist_collapse_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data) -{ - ViewFile *vf = data; - vflist_set_expanded(vf, iter, FALSE); -} - -/* - *----------------------------------------------------------------------------- - * misc - *----------------------------------------------------------------------------- - */ - - -static gchar* vflist_get_formatted(ViewFile *vf, const gchar *name, const gchar *sidecars, const gchar *size, const gchar *time, gboolean expanded) - { - gboolean multiline = vflist_is_multiline(vf); - gchar *text; - - if (multiline) - { - text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time); - } - else - { - text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars); - } - return text; -} - -static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded) -{ - GtkTreeStore *store; - gchar *name; - gchar *sidecars; - gchar *size; - gchar *time; - gchar *formatted; - - store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview))); - - gtk_tree_model_get(GTK_TREE_MODEL(store), iter, - FILE_COLUMN_NAME, &name, - FILE_COLUMN_SIDECARS, &sidecars, - FILE_COLUMN_SIZE, &size, - FILE_COLUMN_DATE, &time, - -1); - formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded); - - gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED, formatted, - FILE_COLUMN_EXPANDED, expanded, - -1); - g_free(time); - g_free(size); - g_free(sidecars); - g_free(name); - g_free(formatted); -} - -static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd) -{ - gchar *size; - gchar *sidecars = NULL; - gchar *name; - const gchar *time = text_from_time(fd->date); - gchar *link = islink(fd->path) ? GQ_LINK_STR : ""; - const gchar *disabled_grouping; - gchar *formatted; - gboolean expanded = FALSE; - - if (fd->sidecar_files) /* expanded has no effect on files without sidecars */ - { - gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_EXPANDED, &expanded, -1); - } - - sidecars = file_data_sc_list_to_string(fd); - - disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : ""; - name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping); - size = text_from_size(fd->size); - - formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded); - - gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd, - FILE_COLUMN_VERSION, fd->version, - FILE_COLUMN_THUMB, fd->thumb_pixbuf, - FILE_COLUMN_FORMATTED, formatted, - FILE_COLUMN_SIDECARS, sidecars, - FILE_COLUMN_NAME, name, - FILE_COLUMN_SIZE, size, - FILE_COLUMN_DATE, time, -#define STORE_SET_IS_SLOW 1 -#if STORE_SET_IS_SLOW -/* this is 3x faster on a directory with 20000 files */ - FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0), - FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1), - FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2), - FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3), - FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4), - FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5), -#if FILEDATA_MARKS_SIZE != 6 -#error this needs to be updated -#endif -#endif - FILE_COLUMN_COLOR, FALSE, -1); - -#if !STORE_SET_IS_SLOW - { - gint i; - for (i = 0; i < FILEDATA_MARKS_SIZE; i++) - gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1); - } -#endif - g_free(size); - g_free(sidecars); - g_free(name); - g_free(formatted); -} - -static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected, gboolean force) -{ - GList *work; - GtkTreeIter iter; - gboolean valid; - gint num_ordered = 0; - gint num_prepended = 0; - - valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter); - - work = list; - while (work) - { - gint match; - FileData *fd = work->data; - gboolean done = FALSE; - - while (!done) - { - FileData *old_fd = NULL; - gint old_version = 0; - - if (valid) - { - gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, - FILE_COLUMN_POINTER, &old_fd, - FILE_COLUMN_VERSION, &old_version, - -1); - - if (fd == old_fd) - { - match = 0; - } - else - { - if (parent_iter) - match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */ - else - match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend); - - if (match == 0) g_warning("multiple fd for the same path"); - } - - } - else - { - match = -1; - } - - if (match < 0) - { - GtkTreeIter new; - - if (valid) - { - num_ordered++; - gtk_tree_store_insert_before(store, &new, parent_iter, &iter); - } - else - { - /* - here should be used gtk_tree_store_append, but this function seems to be O(n) - and it seems to be much faster to add new entries to the beginning and reorder later - */ - num_prepended++; - gtk_tree_store_prepend(store, &new, parent_iter); - } - - vflist_setup_iter(vf, store, &new, file_data_ref(fd)); - vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected, force); - - if (g_list_find(selected, fd)) - { - /* renamed files - the same fd appears at different position - select it again*/ - GtkTreeSelection *selection; - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview)); - gtk_tree_selection_select_iter(selection, &new); - } - - done = TRUE; - } - else if (match > 0) - { - file_data_unref(old_fd); - valid = gtk_tree_store_remove(store, &iter); - } - else - { - num_ordered++; - if (fd->version != old_version || force) - { - vflist_setup_iter(vf, store, &iter, fd); - vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected, force); - } - - if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter); - - done = TRUE; - } - } - work = work->next; - } - - while (valid) - { - FileData *old_fd; - gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1); - file_data_unref(old_fd); - - valid = gtk_tree_store_remove(store, &iter); - } - - /* move the prepended entries to the correct position */ - if (num_prepended) - { - gint i; - gint num_total = num_prepended + num_ordered; - gint *new_order = g_malloc(num_total * sizeof(gint)); - - for (i = 0; i < num_total; i++) - { - if (i < num_ordered) - new_order[i] = num_prepended + i; - else - new_order[i] = num_total - 1 - i; - } - gtk_tree_store_reorder(store, parent_iter, new_order); - - g_free(new_order); - } -} - -void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend) -{ - gint i; - GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL); - gint *new_order; - GtkTreeStore *store; - GList *work; - - if (vf->sort_method == type && vf->sort_ascend == ascend) return; - if (!vf->list) return; - - work = vf->list; - i = 0; - while (work) - { - FileData *fd = work->data; - g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i)); - i++; - work = work->next; - } - - vf->sort_method = type; - vf->sort_ascend = ascend; - - vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend); - - new_order = g_malloc(i * sizeof(gint)); - - work = vf->list; - i = 0; - while (work) - { - FileData *fd = work->data; - new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd)); - i++; - work = work->next; - } - - store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview))); - gtk_tree_store_reorder(store, NULL, new_order); - - g_free(new_order); - g_hash_table_destroy(fd_idx_hash); -} - -/* - *----------------------------------------------------------------------------- - * thumb updates - *----------------------------------------------------------------------------- - */ - - -void vflist_thumb_progress_count(GList *list, gint *count, gint *done) -{ - GList *work = list; - while (work) - { - FileData *fd = work->data; - work = work->next; - - if (fd->thumb_pixbuf) (*done)++; - - if (fd->sidecar_files) - { - vflist_thumb_progress_count(fd->sidecar_files, count, done); - } - (*count)++; - } -} - -void vflist_set_thumb_fd(ViewFile *vf, FileData *fd) -{ - GtkTreeStore *store; - GtkTreeIter iter; - - if (!fd || vflist_find_row(vf, fd, &iter) < 0) return; - - store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview))); - gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1); -} - -FileData *vflist_thumb_next_fd(ViewFile *vf) -{ - GtkTreePath *tpath; - FileData *fd = NULL; - - /* first check the visible files */ - - if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL)) - { - GtkTreeModel *store; - GtkTreeIter iter; - gboolean valid = TRUE; - - store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); - gtk_tree_model_get_iter(store, &iter, tpath); - gtk_tree_path_free(tpath); - - while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0) - { - FileData *nfd; - - gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1); - - if (!nfd->thumb_pixbuf) fd = nfd; - - valid = gtk_tree_model_iter_next(store, &iter); - } - } - - /* then find first undone */ - - if (!fd) - { - GList *work = vf->list; - while (work && !fd) - { - FileData *fd_p = work->data; - if (!fd_p->thumb_pixbuf) - fd = fd_p; - else - { - GList *work2 = fd_p->sidecar_files; - - while (work2 && !fd) - { - fd_p = work2->data; - if (!fd_p->thumb_pixbuf) fd = fd_p; - work2 = work2->next; - } - } - work = work->next; - } - } - - return fd; -} - - -void vflist_thumb_reset_all(ViewFile *vf) -{ - GList *work = vf->list; - while (work) - { - FileData *fd = work->data; - if (fd->thumb_pixbuf) - { - g_object_unref(fd->thumb_pixbuf); - fd->thumb_pixbuf = NULL; - } - work = work->next; - } -} - -/* - *----------------------------------------------------------------------------- - * row stuff - *----------------------------------------------------------------------------- - */ - -FileData *vflist_index_get_data(ViewFile *vf, gint row) -{ - return g_list_nth_data(vf->list, row); -} - -gint vflist_index_by_fd(ViewFile *vf, FileData *fd) -{ - gint p = 0; - GList *work, *work2; - - work = vf->list; - while (work) - { - FileData *list_fd = work->data; - if (list_fd == fd) return p; - - work2 = list_fd->sidecar_files; - while (work2) - { - /* FIXME: return the same index also for sidecars - it is sufficient for next/prev navigation but it should be rewritten - without using indexes at all - */ - FileData *sidecar_fd = work2->data; - if (sidecar_fd == fd) return p; - work2 = work2->next; - } - - work = work->next; - p++; - } - - return -1; -} - -guint vflist_count(ViewFile *vf, gint64 *bytes) -{ - if (bytes) - { - gint64 b = 0; - GList *work; - - work = vf->list; - while (work) - { - FileData *fd = work->data; - work = work->next; - b += fd->size; - } - - *bytes = b; - } - - return g_list_length(vf->list); -} - -GList *vflist_get_list(ViewFile *vf) -{ - GList *list = NULL; - GList *work; - - work = vf->list; - while (work) - { - FileData *fd = work->data; - work = work->next; - - list = g_list_prepend(list, file_data_ref(fd)); - } - - return g_list_reverse(list); -} - -/* - *----------------------------------------------------------------------------- - * selections - *----------------------------------------------------------------------------- - */ - -static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd) -{ - GtkTreeModel *store; - GtkTreeSelection *selection; - GList *slist; - GList *work; - gboolean found = FALSE; - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview)); - slist = gtk_tree_selection_get_selected_rows(selection, &store); - work = slist; - while (!found && work) - { - GtkTreePath *tpath = work->data; - FileData *fd_n; - GtkTreeIter iter; - - gtk_tree_model_get_iter(store, &iter, tpath); - gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1); - if (fd_n == fd) found = TRUE; - work = work->next; - } - g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL); - g_list_free(slist); - - return found; -} - -gboolean vflist_index_is_selected(ViewFile *vf, gint row) -{ - FileData *fd; - - fd = vf_index_get_data(vf, row); - return vflist_row_is_selected(vf, fd); -} - -guint vflist_selection_count(ViewFile *vf, gint64 *bytes) -{ - GtkTreeModel *store; - GtkTreeSelection *selection; - GList *slist; - guint count; - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview)); - slist = gtk_tree_selection_get_selected_rows(selection, &store); - - if (bytes) - { - gint64 b = 0; - GList *work; - - work = slist; - while (work) - { - GtkTreePath *tpath = work->data; - GtkTreeIter iter; - FileData *fd; - - gtk_tree_model_get_iter(store, &iter, tpath); - gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1); - b += fd->size; - - work = work->next; - } - - *bytes = b; - } - - count = g_list_length(slist); - g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL); - g_list_free(slist); - - return count; -} - -GList *vflist_selection_get_list(ViewFile *vf) -{ - GtkTreeModel *store; - GtkTreeSelection *selection; - GList *slist; - GList *list = NULL; - GList *work; - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview)); - slist = gtk_tree_selection_get_selected_rows(selection, &store); - work = slist; - while (work) - { - GtkTreePath *tpath = work->data; - FileData *fd; - GtkTreeIter iter; - - gtk_tree_model_get_iter(store, &iter, tpath); - gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1); - - list = g_list_prepend(list, file_data_ref(fd)); - - if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath)) - { - /* unexpanded - add whole group */ - GList *work2 = fd->sidecar_files; - while (work2) - { - FileData *sfd = work2->data; - list = g_list_prepend(list, file_data_ref(sfd)); - work2 = work2->next; - } - } - - work = work->next; - } - g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL); - g_list_free(slist); - - return g_list_reverse(list); -} - -GList *vflist_selection_get_list_by_index(ViewFile *vf) -{ - GtkTreeModel *store; - GtkTreeSelection *selection; - GList *slist; - GList *list = NULL; - GList *work; - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview)); - slist = gtk_tree_selection_get_selected_rows(selection, &store); - work = slist; - while (work) - { - GtkTreePath *tpath = work->data; - FileData *fd; - GtkTreeIter iter; - - gtk_tree_model_get_iter(store, &iter, tpath); - gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1); - - list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd))); - - work = work->next; - } - g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL); - g_list_free(slist); - - return g_list_reverse(list); -} - -void vflist_select_all(ViewFile *vf) -{ - GtkTreeSelection *selection; - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview)); - gtk_tree_selection_select_all(selection); - - VFLIST(vf)->select_fd = NULL; -} - -void vflist_select_none(ViewFile *vf) -{ - GtkTreeSelection *selection; - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview)); - gtk_tree_selection_unselect_all(selection); -} - -static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter) -{ - GtkTreePath *tpath; - gboolean result; - - tpath = gtk_tree_model_get_path(store, iter); - result = gtk_tree_path_prev(tpath); - if (result) - gtk_tree_model_get_iter(store, iter, tpath); - - gtk_tree_path_free(tpath); - - return result; -} - -static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter) -{ - if (!gtk_tree_model_get_iter_first(store, iter)) - return FALSE; - - while (TRUE) - { - GtkTreeIter next = *iter; - - if (gtk_tree_model_iter_next(store, &next)) - *iter = next; - else - break; - } - - return TRUE; -} - -void vflist_select_invert(ViewFile *vf) -{ - GtkTreeIter iter; - GtkTreeSelection *selection; - GtkTreeModel *store; - gboolean valid; - - store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview)); - - /* Backward iteration prevents scrolling to the end of the list, - * it scrolls to the first selected row instead. */ - valid = tree_model_get_iter_last(store, &iter); - - while (valid) - { - gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter); - - if (selected) - gtk_tree_selection_unselect_iter(selection, &iter); - else - gtk_tree_selection_select_iter(selection, &iter); - - valid = tree_model_iter_prev(store, &iter); - } -} - -void vflist_select_by_fd(ViewFile *vf, FileData *fd) -{ - GtkTreeIter iter; - - if (vflist_find_row(vf, fd, &iter) < 0) return; - - tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE); - - if (!vflist_row_is_selected(vf, fd)) - { - GtkTreeSelection *selection; - GtkTreeModel *store; - GtkTreePath *tpath; - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview)); - gtk_tree_selection_unselect_all(selection); - gtk_tree_selection_select_iter(selection, &iter); - vflist_move_cursor(vf, &iter); - - store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); - tpath = gtk_tree_model_get_path(store, &iter); - gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE); - gtk_tree_path_free(tpath); - } -} - -static void vflist_select_closest(ViewFile *vf, FileData *sel_fd) -{ - GList *work; - FileData *fd = NULL; - - if (sel_fd->parent) sel_fd = sel_fd->parent; - work = vf->list; - - while (work) - { - gint match; - fd = work->data; - work = work->next; - - match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend); - - if (match >= 0) break; - } - - if (fd) vflist_select_by_fd(vf, fd); - -} - -void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode) -{ - GtkTreeModel *store; - GtkTreeIter iter; - GtkTreeSelection *selection; - gboolean valid; - gint n = mark - 1; - - g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE); - - store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)); - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview)); - - valid = gtk_tree_model_get_iter_first(store, &iter); - while (valid) - { - FileData *fd; - gboolean mark_val, selected; - gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1); - - mark_val = file_data_get_mark(fd, n); - selected = gtk_tree_selection_iter_is_selected(selection, &iter); - - switch (mode) - { - case MTS_MODE_SET: selected = mark_val; - break; - case MTS_MODE_OR: selected = mark_val || selected; - break; - case MTS_MODE_AND: selected = mark_val && selected; - break; - case MTS_MODE_MINUS: selected = !mark_val && selected; - break; - } - - if (selected) - gtk_tree_selection_select_iter(selection, &iter); - else - gtk_tree_selection_unselect_iter(selection, &iter); - - valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter); - } -} - -void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode) -{ - GtkTreeModel *store; - GtkTreeSelection *selection; - GList *slist; - GList *work; - gint n = mark - 1; - - g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE); - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview)); - slist = gtk_tree_selection_get_selected_rows(selection, &store); - work = slist; - while (work) - { - GtkTreePath *tpath = work->data; - FileData *fd; - GtkTreeIter iter; - - gtk_tree_model_get_iter(store, &iter, tpath); - gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1); - - /* the change has a very limited range and the standard notification would trigger - complete re-read of the directory - try to do only minimal update instead */ - file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */ - - switch (mode) - { - case STM_MODE_SET: file_data_set_mark(fd, n, 1); - break; - case STM_MODE_RESET: file_data_set_mark(fd, n, 0); - break; - case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n)); - break; - } - - if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */ - { - vf_refresh_idle(vf); - } - else - { - /* mark functions can have various side effects - update all columns to be sure */ - vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd); - /* mark functions can change sidecars too */ - vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE); - } - - - file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM); - - work = work->next; - } - g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL); - g_list_free(slist); -} - -/* - *----------------------------------------------------------------------------- - * core (population) - *----------------------------------------------------------------------------- - */ - -static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline) -{ - GtkTreeViewColumn *column; - GtkCellRenderer *cell; - GList *list; - - column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB); - if (!column) return; - - gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4); - - list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column)); - if (!list) return; - cell = list->data; - g_list_free(list); - - g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL); - gtk_tree_view_column_set_visible(column, thumb); - - column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED); - if (!column) return; - gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column); - - column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE); - if (!column) return; - gtk_tree_view_column_set_visible(column, !multiline); - - column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE); - if (!column) return; - gtk_tree_view_column_set_visible(column, !multiline); -} - -static gboolean vflist_is_multiline(ViewFile *vf) -{ - return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48); -} - - -static void vflist_populate_view(ViewFile *vf, gboolean force) -{ - GtkTreeStore *store; - GList *selected; - - store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview))); - - vf_thumb_stop(vf); - - if (!vf->list) - { - vflist_store_clear(vf, FALSE); - vf_send_update(vf); - return; - } - - vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf)); - - selected = vflist_selection_get_list(vf); - - vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force); - - if (selected && vflist_selection_count(vf, NULL) == 0) - { - /* all selected files disappeared */ - vflist_select_closest(vf, selected->data); - } - - filelist_free(selected); - - vf_send_update(vf); - vf_thumb_update(vf); -} - -gboolean vflist_refresh(ViewFile *vf) -{ - GList *old_list; - gboolean ret = TRUE; - - old_list = vf->list; - vf->list = NULL; - - DEBUG_1("%s vflist_refresh: read dir", get_exec_time()); - if (vf->dir_fd) - { - file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */ - - ret = filelist_read(vf->dir_fd, &vf->list, NULL); - - if (vf->marks_enabled) - { - // When marks are enabled, lock FileDatas so that we don't end up re-parsing XML - // each time a mark is changed. - file_data_lock_list(vf->list); - } - else - { - // FIXME: only do this when needed (aka when we just switched from - // FIXME: marks-enabled to marks-disabled) - file_data_unlock_list(vf->list); - } - - vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf)); - file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM); - - DEBUG_1("%s vflist_refresh: sort", get_exec_time()); - vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend); - } - - DEBUG_1("%s vflist_refresh: populate view", get_exec_time()); - - vflist_populate_view(vf, FALSE); - - DEBUG_1("%s vflist_refresh: free filelist", get_exec_time()); - - filelist_free(old_list); - DEBUG_1("%s vflist_refresh: done", get_exec_time()); - - return ret; -} - - - -/* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */ - -#define CELL_HEIGHT_OVERRIDE 512 - -static void cell_renderer_height_override(GtkCellRenderer *renderer) -{ - GParamSpec *spec; - - spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height"); - if (spec && G_IS_PARAM_SPEC_INT(spec)) - { - GParamSpecInt *spec_int; - - spec_int = G_PARAM_SPEC_INT(spec); - if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE; - } -} - -static GdkColor *vflist_listview_color_shifted(GtkWidget *widget) -{ - static GdkColor color; - static GtkWidget *done = NULL; - - if (done != widget) - { - GtkStyle *style; - - style = gtk_widget_get_style(widget); - memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color)); - shift_color(&color, -1, 0); - done = widget; - } - - return &color; -} - -static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell, - GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data) -{ - ViewFile *vf = data; - gboolean set; - - gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1); - g_object_set(G_OBJECT(cell), - "cell-background-gdk", vflist_listview_color_shifted(vf->listview), - "cell-background-set", set, NULL); -} - -static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand) -{ - GtkTreeViewColumn *column; - GtkCellRenderer *renderer; - - column = gtk_tree_view_column_new(); - gtk_tree_view_column_set_title(column, title); - gtk_tree_view_column_set_min_width(column, 4); - - if (!image) - { - gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY); - renderer = gtk_cell_renderer_text_new(); - if (right_justify) - { - g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL); - } - gtk_tree_view_column_pack_start(column, renderer, TRUE); - gtk_tree_view_column_add_attribute(column, renderer, "text", n); - if (expand) - gtk_tree_view_column_set_expand(column, TRUE); - } - else - { - gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); - renderer = gtk_cell_renderer_pixbuf_new(); - cell_renderer_height_override(renderer); - gtk_tree_view_column_pack_start(column, renderer, TRUE); - gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n); - } - - gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL); - g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n)); - g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n)); - - gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column); -} - -static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data) -{ - ViewFile *vf = data; - GtkTreeStore *store; - GtkTreePath *path = gtk_tree_path_new_from_string(path_str); - GtkTreeIter iter; - FileData *fd; - gboolean marked; - guint col_idx; - - store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview))); - if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path)) - return; - - col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx")); - - g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST); - - gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1); - marked = !marked; - - /* the change has a very limited range and the standard notification would trigger - complete re-read of the directory - try to do only minimal update instead */ - file_data_unregister_notify_func(vf_notify_cb, vf); - file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked); - if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */ - { - vf_refresh_idle(vf); - } - else - { - /* mark functions can have various side effects - update all columns to be sure */ - vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd); - /* mark functions can change sidecars too */ - vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE); - } - file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM); - - gtk_tree_path_free(path); -} - -static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title) -{ - GtkTreeViewColumn *column; - GtkCellRenderer *renderer; - - renderer = gtk_cell_renderer_toggle_new(); - column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL); - - gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); - g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n)); - g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n)); - - gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column); - gtk_tree_view_column_set_fixed_width(column, 22); - gtk_tree_view_column_set_visible(column, vf->marks_enabled); - - - g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf); -} - -/* - *----------------------------------------------------------------------------- - * base - *----------------------------------------------------------------------------- - */ - -gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd) -{ - gboolean ret; - if (!dir_fd) return FALSE; - if (vf->dir_fd == dir_fd) return TRUE; - - file_data_unref(vf->dir_fd); - vf->dir_fd = file_data_ref(dir_fd); - - /* force complete reload */ - vflist_store_clear(vf, TRUE); - - filelist_free(vf->list); - vf->list = NULL; - - ret = vf_refresh(vf); - gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview)); - return ret; -} - -void vflist_destroy_cb(GtkWidget *widget, gpointer data) -{ - ViewFile *vf = data; - - file_data_unregister_notify_func(vf_notify_cb, vf); - - vflist_select_idle_cancel(vf); - vf_refresh_idle_cancel(vf); - vf_thumb_stop(vf); - - filelist_free(vf->list); -} - -ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd) -{ - GtkTreeStore *store; - GtkTreeSelection *selection; - GType flist_types[FILE_COLUMN_COUNT]; - gint i; - gint column; - - vf->info = g_new0(ViewFileInfoList, 1); - - flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER; - flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT; - flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF; - flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING; - flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING; - flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING; - flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING; - flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING; - flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN; - flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN; - for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++) - flist_types[i] = G_TYPE_BOOLEAN; - - store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types); - - vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); - g_object_unref(store); - - g_signal_connect(G_OBJECT(vf->listview), "row-expanded", - G_CALLBACK(vflist_expand_cb), vf); - - g_signal_connect(G_OBJECT(vf->listview), "row-collapsed", - G_CALLBACK(vflist_collapse_cb), vf); - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview)); - gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE); - gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL); - - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE); - gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE); - - column = 0; - - for (i = 0; i < FILEDATA_MARKS_SIZE; i++) - { - vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, ""); - g_assert(column == FILE_VIEW_COLUMN_MARKS + i); - column++; - } - - vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE); - g_assert(column == FILE_VIEW_COLUMN_THUMB); - column++; - - vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE); - g_assert(column == FILE_VIEW_COLUMN_FORMATTED); - column++; - - vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE); - g_assert(column == FILE_VIEW_COLUMN_SIZE); - column++; - - vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE); - g_assert(column == FILE_VIEW_COLUMN_DATE); - column++; - - file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM); - return vf; -} - -void vflist_thumb_set(ViewFile *vf, gboolean enable) -{ - if (VFLIST(vf)->thumbs_enabled == enable) return; - - VFLIST(vf)->thumbs_enabled = enable; - - /* vflist_populate_view is better than vf_refresh: - - no need to re-read the directory - - force update because the formatted string has changed - */ - if (vf->layout) - { - vflist_populate_view(vf, TRUE); - gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview)); - } -} - -void vflist_marks_set(ViewFile *vf, gboolean enable) -{ - GList *columns, *work; - - columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview)); - - work = columns; - while (work) - { - GtkTreeViewColumn *column = work->data; - gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx")); - work = work->next; - - if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS) - gtk_tree_view_column_set_visible(column, enable); - } - - if (enable) - { - // Previously disabled, which means that vf->list is complete - file_data_lock_list(vf->list); - } - else - { - // Previously enabled, which means that vf->list is incomplete - } - - g_list_free(columns); -} - -/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- a/src/view_file_list.h Thu Jun 29 11:05:59 2017 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2004 John Ellis - * Copyright (C) 2008 - 2016 The Geeqie Team - * - * Author: John Ellis - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef VIEW_FILE_LIST_H -#define VIEW_FILE_LIST_H - - -#include "filedata.h" - -gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data); -gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data); -gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data); - -void vflist_dnd_init(ViewFile *vf); - -void vflist_destroy_cb(GtkWidget *widget, gpointer data); -ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd); - -gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd); -gboolean vflist_refresh(ViewFile *vf); - -void vflist_thumb_set(ViewFile *vf, gboolean enable); -void vflist_marks_set(ViewFile *vf, gboolean enable); -void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend); - -GList *vflist_selection_get_one(ViewFile *vf, FileData *fd); -GList *vflist_pop_menu_file_list(ViewFile *vf); -void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data); -void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data); -void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data); -void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data); -void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data); - -FileData *vflist_index_get_data(ViewFile *vf, gint row); -gint vflist_index_by_fd(ViewFile *vf, FileData *fd); -guint vflist_count(ViewFile *vf, gint64 *bytes); -GList *vflist_get_list(ViewFile *vf); - -gboolean vflist_index_is_selected(ViewFile *vf, gint row); -guint vflist_selection_count(ViewFile *vf, gint64 *bytes); -GList *vflist_selection_get_list(ViewFile *vf); -GList *vflist_selection_get_list_by_index(ViewFile *vf); - -void vflist_select_all(ViewFile *vf); -void vflist_select_none(ViewFile *vf); -void vflist_select_invert(ViewFile *vf); -void vflist_select_by_fd(ViewFile *vf, FileData *fd); - -void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode); -void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode); - -void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set); - -void vflist_thumb_progress_count(GList *list, gint *count, gint *done); -void vflist_set_thumb_fd(ViewFile *vf, FileData *fd); -FileData *vflist_thumb_next_fd(ViewFile *vf); -void vflist_thumb_reset_all(ViewFile *vf); - -#endif -/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */