2 * Copyright (C) 2004 John Ellis
3 * Copyright (C) 2008 - 2016 The Geeqie Team
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "view_file_list.h"
26 #include "cache_maint.h"
30 #include "filecluster.h"
32 #include "layout_image.h"
37 #include "ui_fileops.h"
39 #include "ui_tree_edit.h"
40 #include "uri_utils.h"
41 #include "view_file.h"
43 #include <gdk/gdkkeysyms.h> /* for keyboard values */
45 /* Index to tree store */
47 FILE_COLUMN_POINTER = 0,
50 FILE_COLUMN_FORMATTED,
58 FILE_COLUMN_MARKS_LAST = FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
63 /* Index to tree view */
65 FILE_VIEW_COLUMN_MARKS = 0,
66 FILE_VIEW_COLUMN_MARKS_LAST = FILE_VIEW_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
67 FILE_VIEW_COLUMN_THUMB,
68 FILE_VIEW_COLUMN_FORMATTED,
69 FILE_VIEW_COLUMN_SIZE,
70 FILE_VIEW_COLUMN_DATE,
71 FILE_VIEW_COLUMN_COUNT
76 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd);
77 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data);
78 static void vflist_populate_view(ViewFile *vf, gboolean force);
79 static gboolean vflist_is_multiline(ViewFile *vf);
80 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded);
84 *-----------------------------------------------------------------------------
86 *-----------------------------------------------------------------------------
93 } ViewFileFindRowData;
95 static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
97 ViewFileFindRowData *find = data;
99 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
110 static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter)
113 ViewFileFindRowData data = {fd, iter, FALSE, 0};
115 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
116 gtk_tree_model_foreach(store, vflist_find_row_cb, &data);
126 static FileData *vflist_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter)
129 GtkTreeViewColumn *column;
131 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), x, y,
132 &tpath, &column, NULL, NULL))
138 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
139 gtk_tree_model_get_iter(store, &row, tpath);
140 gtk_tree_path_free(tpath);
141 gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &fd, -1);
149 static gboolean vflist_store_clear_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
152 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
154 /* it seems that gtk_tree_store_clear may call some callbacks
155 that use the column. Set the pointer to NULL to be safe. */
156 gtk_tree_store_set(GTK_TREE_STORE(model), iter, FILE_COLUMN_POINTER, NULL, -1);
161 static void vflist_store_clear(ViewFile *vf, gboolean unlock_files)
166 if (unlock_files && vf->marks_enabled)
168 // unlock locked files in this directory
169 filelist_read(vf->dir_fd, &files, NULL);
172 FileData *fd = files->data;
174 file_data_unlock(fd);
175 file_data_unref(fd); // undo the ref that got added in filelist_read
180 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
181 gtk_tree_model_foreach(store, vflist_store_clear_cb, NULL);
182 gtk_tree_store_clear(GTK_TREE_STORE(store));
185 void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set)
190 if (vflist_find_row(vf, fd, &iter) < 0) return;
191 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
192 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
195 static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
200 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
202 tpath = gtk_tree_model_get_path(store, iter);
203 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
204 gtk_tree_path_free(tpath);
209 *-----------------------------------------------------------------------------
211 *-----------------------------------------------------------------------------
214 static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
215 GtkSelectionData *selection_data, guint info,
216 guint time, gpointer data)
221 if (!VFLIST(vf)->click_fd) return;
223 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
225 list = vf_selection_get_list(vf);
229 list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
233 uri_selection_data_set_uris_from_filelist(selection_data, list);
237 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
241 vflist_color_set(vf, VFLIST(vf)->click_fd, TRUE);
243 if (VFLIST(vf)->thumbs_enabled &&
244 VFLIST(vf)->click_fd && VFLIST(vf)->click_fd->thumb_pixbuf)
248 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
249 items = vf_selection_count(vf, NULL);
253 dnd_set_drag_icon(widget, context, VFLIST(vf)->click_fd->thumb_pixbuf, items);
257 static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
261 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
263 if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE)
269 static void vflist_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context,
270 int x, int y, GtkSelectionData *selection,
271 guint info, guint time, gpointer data)
275 if (info == TARGET_TEXT_PLAIN) {
276 FileData *fd = vflist_find_data_by_coord(vf, x, y, NULL);
279 /* Add keywords to file */
280 gchar *str = (gchar *) gtk_selection_data_get_text(selection);
281 GList *kw_list = string_to_keywords_list(str);
283 metadata_append_list(fd, KEYWORD_KEY, kw_list);
284 string_list_free(kw_list);
290 void vflist_dnd_init(ViewFile *vf)
292 gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
293 dnd_file_drag_types, dnd_file_drag_types_count,
294 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
295 gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL,
296 dnd_file_drag_types, dnd_file_drag_types_count,
297 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
299 g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
300 G_CALLBACK(vflist_dnd_get), vf);
301 g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
302 G_CALLBACK(vflist_dnd_begin), vf);
303 g_signal_connect(G_OBJECT(vf->listview), "drag_end",
304 G_CALLBACK(vflist_dnd_end), vf);
305 g_signal_connect(G_OBJECT(vf->listview), "drag_data_received",
306 G_CALLBACK(vflist_drag_data_received), vf);
310 *-----------------------------------------------------------------------------
312 *-----------------------------------------------------------------------------
315 GList *vflist_selection_get_one(ViewFile *vf, FileData *fd)
317 GList *list = g_list_append(NULL, file_data_ref(fd));
319 if (fd->sidecar_files)
321 /* check if the row is expanded */
325 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
326 if (vflist_find_row(vf, fd, &iter) >= 0)
330 tpath = gtk_tree_model_get_path(store, &iter);
331 if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
333 /* unexpanded - add whole group */
334 GList *work = fd->sidecar_files;
337 FileData *sfd = work->data;
338 list = g_list_prepend(list, file_data_ref(sfd));
342 gtk_tree_path_free(tpath);
344 list = g_list_reverse(list);
350 GList *vflist_pop_menu_file_list(ViewFile *vf)
352 if (!VFLIST(vf)->click_fd) return NULL;
354 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
356 return vf_selection_get_list(vf);
358 return vflist_selection_get_one(vf, VFLIST(vf)->click_fd);
362 void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
366 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
370 list = vf_selection_get_list(vf);
371 view_window_new_from_list(list);
376 view_window_new(VFLIST(vf)->click_fd);
380 void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
385 list = vf_pop_menu_file_list(vf);
386 if (options->file_ops.enable_in_place_rename &&
387 list && !list->next && VFLIST(vf)->click_fd)
394 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
395 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
399 tpath = gtk_tree_model_get_path(store, &iter);
400 tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
401 FILE_VIEW_COLUMN_FORMATTED, VFLIST(vf)->click_fd->name,
402 vflist_row_rename_cb, vf);
403 gtk_tree_path_free(tpath);
408 file_util_rename(NULL, list, vf->listview);
411 void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
415 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
418 layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
422 vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled);
426 void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
430 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
432 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
435 void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
438 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
439 VFLIST(vf)->click_fd = NULL;
445 *-----------------------------------------------------------------------------
447 *-----------------------------------------------------------------------------
450 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
455 if (!new || !new[0]) return FALSE;
457 new_path = g_build_filename(vf->dir_fd->path, new, NULL);
459 if (strchr(new, G_DIR_SEPARATOR) != NULL)
461 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
462 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
467 gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL);
468 FileData *fd = file_data_new_group(old_path); /* get the fd from cache */
469 file_util_rename_simple(fd, new_path, vf->listview);
479 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
487 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return;
488 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
489 tpath = gtk_tree_model_get_path(store, &iter);
490 tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
491 gtk_tree_path_free(tpath);
493 popup_menu_position_clamp(menu, x, y, 0);
496 gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
501 if (event->keyval != GDK_KEY_Menu) return FALSE;
503 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
509 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
510 gtk_tree_model_get_iter(store, &iter, tpath);
511 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
512 gtk_tree_path_free(tpath);
516 VFLIST(vf)->click_fd = NULL;
519 vf->popup = vf_pop_menu(vf);
520 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
525 gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
531 GtkTreeViewColumn *column;
533 vf->clicked_mark = 0;
535 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
536 &tpath, &column, NULL, NULL))
539 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
541 if (bevent->button == MOUSE_BUTTON_LEFT &&
542 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
545 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
546 vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
548 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
550 gtk_tree_model_get_iter(store, &iter, tpath);
551 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
552 gtk_tree_path_free(tpath);
555 VFLIST(vf)->click_fd = fd;
557 if (bevent->button == MOUSE_BUTTON_RIGHT)
559 vf->popup = vf_pop_menu(vf);
560 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
561 bevent->button, bevent->time);
565 if (!fd) return FALSE;
567 if (bevent->button == MOUSE_BUTTON_MIDDLE)
569 if (!vflist_row_is_selected(vf, fd))
571 vflist_color_set(vf, fd, TRUE);
577 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
578 !(bevent->state & GDK_SHIFT_MASK ) &&
579 !(bevent->state & GDK_CONTROL_MASK ) &&
580 vflist_row_is_selected(vf, fd))
582 GtkTreeSelection *selection;
584 gtk_widget_grab_focus(widget);
587 /* returning FALSE and further processing of the event is needed for
588 correct operation of the expander, to show the sidecar files.
589 It however resets the selection of multiple files. With this condition
590 it should work for both cases */
591 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
592 return (gtk_tree_selection_count_selected_rows(selection) > 1);
595 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
597 if (vf->layout) layout_image_full_screen_start(vf->layout);
603 gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
610 if (bevent->button == MOUSE_BUTTON_MIDDLE)
612 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
615 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
620 if ((bevent->x != 0 || bevent->y != 0) &&
621 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
622 &tpath, NULL, NULL, NULL))
626 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
627 gtk_tree_model_get_iter(store, &iter, tpath);
628 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
629 gtk_tree_path_free(tpath);
632 if (bevent->button == MOUSE_BUTTON_MIDDLE)
634 if (fd && VFLIST(vf)->click_fd == fd)
636 GtkTreeSelection *selection;
638 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
639 if (vflist_row_is_selected(vf, fd))
641 gtk_tree_selection_unselect_iter(selection, &iter);
645 gtk_tree_selection_select_iter(selection, &iter);
651 if (fd && VFLIST(vf)->click_fd == fd &&
652 !(bevent->state & GDK_SHIFT_MASK ) &&
653 !(bevent->state & GDK_CONTROL_MASK ) &&
654 vflist_row_is_selected(vf, fd))
656 GtkTreeSelection *selection;
658 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
659 gtk_tree_selection_unselect_all(selection);
660 gtk_tree_selection_select_iter(selection, &iter);
661 vflist_move_cursor(vf, &iter);
667 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
669 FileData *read_ahead_fd = NULL;
675 cur_fd = layout_image_get_fd(vf->layout);
676 if (sel_fd == cur_fd) return; /* no change */
678 row = g_list_index(vf->list, sel_fd);
679 // FIXME sidecar data
681 if (sel_fd && options->image.enable_read_ahead && row >= 0)
683 if (row > g_list_index(vf->list, cur_fd) &&
684 (guint) (row + 1) < vf_count(vf, NULL))
686 read_ahead_fd = vf_index_get_data(vf, row + 1);
690 read_ahead_fd = vf_index_get_data(vf, row - 1);
694 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
697 static gboolean vflist_select_idle_cb(gpointer data)
703 VFLIST(vf)->select_idle_id = 0;
709 if (VFLIST(vf)->select_fd)
711 vflist_select_image(vf, VFLIST(vf)->select_fd);
712 VFLIST(vf)->select_fd = NULL;
715 VFLIST(vf)->select_idle_id = 0;
719 static void vflist_select_idle_cancel(ViewFile *vf)
721 if (VFLIST(vf)->select_idle_id)
723 g_source_remove(VFLIST(vf)->select_idle_id);
724 VFLIST(vf)->select_idle_id = 0;
728 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
729 gboolean path_currently_selected, gpointer data)
734 if (!path_currently_selected &&
735 gtk_tree_model_get_iter(store, &iter, tpath))
737 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
741 VFLIST(vf)->select_fd = NULL;
745 !VFLIST(vf)->select_idle_id)
747 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
753 static void vflist_expand_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
756 vflist_set_expanded(vf, iter, TRUE);
759 static void vflist_collapse_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
762 vflist_set_expanded(vf, iter, FALSE);
766 *-----------------------------------------------------------------------------
768 *-----------------------------------------------------------------------------
772 static gchar* vflist_get_formatted(ViewFile *vf, const gchar *name, const gchar *sidecars, const gchar *size, const gchar *time, gboolean expanded)
774 gboolean multiline = vflist_is_multiline(vf);
779 text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time);
783 text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars);
788 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded)
797 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
799 gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
800 FILE_COLUMN_NAME, &name,
801 FILE_COLUMN_SIDECARS, &sidecars,
802 FILE_COLUMN_SIZE, &size,
803 FILE_COLUMN_DATE, &time,
805 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
807 gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED, formatted,
808 FILE_COLUMN_EXPANDED, expanded,
817 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
820 gchar *sidecars = NULL;
822 const gchar *time = text_from_time(fd->date);
823 gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
824 const gchar *disabled_grouping;
826 gboolean expanded = FALSE;
828 if (fd->sidecar_files) /* expanded has no effect on files without sidecars */
830 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_EXPANDED, &expanded, -1);
833 sidecars = file_data_sc_list_to_string(fd);
835 disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
836 name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
837 size = text_from_size(fd->size);
839 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
841 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
842 FILE_COLUMN_VERSION, fd->version,
843 FILE_COLUMN_THUMB, fd->thumb_pixbuf,
844 FILE_COLUMN_FORMATTED, formatted,
845 FILE_COLUMN_SIDECARS, sidecars,
846 FILE_COLUMN_NAME, name,
847 FILE_COLUMN_SIZE, size,
848 FILE_COLUMN_DATE, time,
849 #define STORE_SET_IS_SLOW 1
850 #if STORE_SET_IS_SLOW
851 /* this is 3x faster on a directory with 20000 files */
852 FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
853 FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
854 FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
855 FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
856 FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
857 FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
858 #if FILEDATA_MARKS_SIZE != 6
859 #error this needs to be updated
862 FILE_COLUMN_COLOR, FALSE, -1);
864 #if !STORE_SET_IS_SLOW
867 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
868 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
877 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected, gboolean force)
882 gint num_ordered = 0;
883 gint num_prepended = 0;
885 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
891 FileData *fd = work->data;
892 gboolean done = FALSE;
896 FileData *old_fd = NULL;
897 gint old_version = 0;
901 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
902 FILE_COLUMN_POINTER, &old_fd,
903 FILE_COLUMN_VERSION, &old_version,
913 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
915 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
917 if (match == 0) g_warning("multiple fd for the same path");
933 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
938 here should be used gtk_tree_store_append, but this function seems to be O(n)
939 and it seems to be much faster to add new entries to the beginning and reorder later
942 gtk_tree_store_prepend(store, &new, parent_iter);
945 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
946 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected, force);
948 if (g_list_find(selected, fd))
950 /* renamed files - the same fd appears at different position - select it again*/
951 GtkTreeSelection *selection;
952 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
953 gtk_tree_selection_select_iter(selection, &new);
960 file_data_unref(old_fd);
961 valid = gtk_tree_store_remove(store, &iter);
966 if (fd->version != old_version || force)
968 vflist_setup_iter(vf, store, &iter, fd);
969 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected, force);
972 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
983 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
984 file_data_unref(old_fd);
986 valid = gtk_tree_store_remove(store, &iter);
989 /* move the prepended entries to the correct position */
993 gint num_total = num_prepended + num_ordered;
994 gint *new_order = g_malloc(num_total * sizeof(gint));
996 for (i = 0; i < num_total; i++)
999 new_order[i] = num_prepended + i;
1001 new_order[i] = num_total - 1 - i;
1003 gtk_tree_store_reorder(store, parent_iter, new_order);
1009 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
1012 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
1014 GtkTreeStore *store;
1017 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1018 if (!vf->list) return;
1024 FileData *fd = work->data;
1025 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1030 vf->sort_method = type;
1031 vf->sort_ascend = ascend;
1033 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1035 new_order = g_malloc(i * sizeof(gint));
1041 FileData *fd = work->data;
1042 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1047 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1048 gtk_tree_store_reorder(store, NULL, new_order);
1051 g_hash_table_destroy(fd_idx_hash);
1055 *-----------------------------------------------------------------------------
1057 *-----------------------------------------------------------------------------
1061 void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1066 FileData *fd = work->data;
1069 if (fd->thumb_pixbuf) (*done)++;
1071 if (fd->sidecar_files)
1073 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1079 void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
1081 GtkTreeStore *store;
1084 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1086 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1087 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1090 FileData *vflist_thumb_next_fd(ViewFile *vf)
1093 FileData *fd = NULL;
1095 /* first check the visible files */
1097 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1099 GtkTreeModel *store;
1101 gboolean valid = TRUE;
1103 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1104 gtk_tree_model_get_iter(store, &iter, tpath);
1105 gtk_tree_path_free(tpath);
1108 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1112 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1114 if (!nfd->thumb_pixbuf) fd = nfd;
1116 valid = gtk_tree_model_iter_next(store, &iter);
1120 /* then find first undone */
1124 GList *work = vf->list;
1127 FileData *fd_p = work->data;
1128 if (!fd_p->thumb_pixbuf)
1132 GList *work2 = fd_p->sidecar_files;
1134 while (work2 && !fd)
1137 if (!fd_p->thumb_pixbuf) fd = fd_p;
1138 work2 = work2->next;
1149 *-----------------------------------------------------------------------------
1151 *-----------------------------------------------------------------------------
1154 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1157 GList *work, *work2;
1162 FileData *list_fd = work->data;
1163 if (list_fd == fd) return p;
1165 work2 = list_fd->sidecar_files;
1168 /* FIXME: return the same index also for sidecars
1169 it is sufficient for next/prev navigation but it should be rewritten
1170 without using indexes at all
1172 FileData *sidecar_fd = work2->data;
1173 if (sidecar_fd == fd) return p;
1174 work2 = work2->next;
1185 *-----------------------------------------------------------------------------
1187 *-----------------------------------------------------------------------------
1190 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1192 GtkTreeModel *store;
1193 GtkTreeSelection *selection;
1196 gboolean found = FALSE;
1198 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1199 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1201 while (!found && work)
1203 GtkTreePath *tpath = work->data;
1207 gtk_tree_model_get_iter(store, &iter, tpath);
1208 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1209 if (fd_n == fd) found = TRUE;
1212 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1218 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1222 fd = vf_index_get_data(vf, row);
1223 return vflist_row_is_selected(vf, fd);
1226 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1228 GtkTreeModel *store;
1229 GtkTreeSelection *selection;
1233 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1234 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1244 GtkTreePath *tpath = work->data;
1248 gtk_tree_model_get_iter(store, &iter, tpath);
1249 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1258 count = g_list_length(slist);
1259 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1265 GList *vflist_selection_get_list(ViewFile *vf)
1267 GtkTreeModel *store;
1268 GtkTreeSelection *selection;
1273 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1274 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1278 GtkTreePath *tpath = work->data;
1282 gtk_tree_model_get_iter(store, &iter, tpath);
1283 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1285 list = g_list_prepend(list, file_data_ref(fd));
1287 if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
1289 /* unexpanded - add whole group */
1290 GList *work2 = fd->sidecar_files;
1293 FileData *sfd = work2->data;
1294 list = g_list_prepend(list, file_data_ref(sfd));
1295 work2 = work2->next;
1301 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1304 return g_list_reverse(list);
1307 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1309 GtkTreeModel *store;
1310 GtkTreeSelection *selection;
1315 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1316 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1320 GtkTreePath *tpath = work->data;
1324 gtk_tree_model_get_iter(store, &iter, tpath);
1325 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1327 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1331 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1334 return g_list_reverse(list);
1337 void vflist_select_all(ViewFile *vf)
1339 GtkTreeSelection *selection;
1341 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1342 gtk_tree_selection_select_all(selection);
1344 VFLIST(vf)->select_fd = NULL;
1347 void vflist_select_none(ViewFile *vf)
1349 GtkTreeSelection *selection;
1351 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1352 gtk_tree_selection_unselect_all(selection);
1355 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1360 tpath = gtk_tree_model_get_path(store, iter);
1361 result = gtk_tree_path_prev(tpath);
1363 gtk_tree_model_get_iter(store, iter, tpath);
1365 gtk_tree_path_free(tpath);
1370 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1372 if (!gtk_tree_model_get_iter_first(store, iter))
1377 GtkTreeIter next = *iter;
1379 if (gtk_tree_model_iter_next(store, &next))
1388 void vflist_select_invert(ViewFile *vf)
1391 GtkTreeSelection *selection;
1392 GtkTreeModel *store;
1395 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1396 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1398 /* Backward iteration prevents scrolling to the end of the list,
1399 * it scrolls to the first selected row instead. */
1400 valid = tree_model_get_iter_last(store, &iter);
1404 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1407 gtk_tree_selection_unselect_iter(selection, &iter);
1409 gtk_tree_selection_select_iter(selection, &iter);
1411 valid = tree_model_iter_prev(store, &iter);
1415 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1419 if (vflist_find_row(vf, fd, &iter) < 0) return;
1421 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1423 if (!vflist_row_is_selected(vf, fd))
1425 GtkTreeSelection *selection;
1426 GtkTreeModel *store;
1429 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1430 gtk_tree_selection_unselect_all(selection);
1431 gtk_tree_selection_select_iter(selection, &iter);
1432 vflist_move_cursor(vf, &iter);
1434 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1435 tpath = gtk_tree_model_get_path(store, &iter);
1436 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1437 gtk_tree_path_free(tpath);
1441 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1444 FileData *fd = NULL;
1446 if (sel_fd->parent) sel_fd = sel_fd->parent;
1455 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1457 if (match >= 0) break;
1460 if (fd) vflist_select_by_fd(vf, fd);
1464 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1466 GtkTreeModel *store;
1468 GtkTreeSelection *selection;
1472 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1474 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1475 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1477 valid = gtk_tree_model_get_iter_first(store, &iter);
1481 gboolean mark_val, selected;
1482 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1484 mark_val = file_data_get_mark(fd, n);
1485 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1489 case MTS_MODE_SET: selected = mark_val;
1491 case MTS_MODE_OR: selected = mark_val || selected;
1493 case MTS_MODE_AND: selected = mark_val && selected;
1495 case MTS_MODE_MINUS: selected = !mark_val && selected;
1500 gtk_tree_selection_select_iter(selection, &iter);
1502 gtk_tree_selection_unselect_iter(selection, &iter);
1504 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1508 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1510 GtkTreeModel *store;
1511 GtkTreeSelection *selection;
1516 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1518 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1519 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1523 GtkTreePath *tpath = work->data;
1527 gtk_tree_model_get_iter(store, &iter, tpath);
1528 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1530 /* the change has a very limited range and the standard notification would trigger
1531 complete re-read of the directory - try to do only minimal update instead */
1532 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1536 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1538 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1540 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1544 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1546 vf_refresh_idle(vf);
1550 /* mark functions can have various side effects - update all columns to be sure */
1551 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1552 /* mark functions can change sidecars too */
1553 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1557 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1561 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1566 *-----------------------------------------------------------------------------
1568 *-----------------------------------------------------------------------------
1571 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
1573 GtkTreeViewColumn *column;
1574 GtkCellRenderer *cell;
1577 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1578 if (!column) return;
1580 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1582 list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1587 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1588 gtk_tree_view_column_set_visible(column, thumb);
1590 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1591 if (!column) return;
1592 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1594 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1595 if (!column) return;
1596 gtk_tree_view_column_set_visible(column, !multiline);
1598 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1599 if (!column) return;
1600 gtk_tree_view_column_set_visible(column, !multiline);
1603 static gboolean vflist_is_multiline(ViewFile *vf)
1605 return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
1609 static void vflist_populate_view(ViewFile *vf, gboolean force)
1611 GtkTreeStore *store;
1614 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1620 vflist_store_clear(vf, FALSE);
1625 vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
1627 selected = vflist_selection_get_list(vf);
1629 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force);
1631 if (selected && vflist_selection_count(vf, NULL) == 0)
1633 /* all selected files disappeared */
1634 vflist_select_closest(vf, selected->data);
1637 filelist_free(selected);
1640 vf_thumb_update(vf);
1643 gboolean vflist_refresh(ViewFile *vf)
1646 gboolean ret = TRUE;
1648 old_list = vf->list;
1651 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1654 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1656 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1658 if (vf->marks_enabled)
1660 // When marks are enabled, lock FileDatas so that we don't end up re-parsing XML
1661 // each time a mark is changed.
1662 file_data_lock_list(vf->list);
1666 // FIXME: only do this when needed (aka when we just switched from
1667 // FIXME: marks-enabled to marks-disabled)
1668 file_data_unlock_list(vf->list);
1671 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1672 vf->list = fileclusterlist_remove_children_from_list(vf->cluster_list, vf->list);
1673 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1675 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1676 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1679 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1681 vflist_populate_view(vf, FALSE);
1683 DEBUG_1("%s vflist_refresh: free filelist", get_exec_time());
1685 filelist_free(old_list);
1686 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1693 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1695 #define CELL_HEIGHT_OVERRIDE 512
1697 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1701 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1702 if (spec && G_IS_PARAM_SPEC_INT(spec))
1704 GParamSpecInt *spec_int;
1706 spec_int = G_PARAM_SPEC_INT(spec);
1707 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1711 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1713 static GdkColor color;
1714 static GtkWidget *done = NULL;
1720 style = gtk_widget_get_style(widget);
1721 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1722 shift_color(&color, -1, 0);
1729 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1730 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1732 ViewFile *vf = data;
1735 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1736 g_object_set(G_OBJECT(cell),
1737 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1738 "cell-background-set", set, NULL);
1741 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1743 GtkTreeViewColumn *column;
1744 GtkCellRenderer *renderer;
1746 column = gtk_tree_view_column_new();
1747 gtk_tree_view_column_set_title(column, title);
1748 gtk_tree_view_column_set_min_width(column, 4);
1752 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1753 renderer = gtk_cell_renderer_text_new();
1756 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1758 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1759 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1761 gtk_tree_view_column_set_expand(column, TRUE);
1765 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1766 renderer = gtk_cell_renderer_pixbuf_new();
1767 cell_renderer_height_override(renderer);
1768 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1769 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1772 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1773 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1774 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1776 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1779 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1781 ViewFile *vf = data;
1782 GtkTreeStore *store;
1783 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1789 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1790 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1793 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1795 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1797 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1800 /* the change has a very limited range and the standard notification would trigger
1801 complete re-read of the directory - try to do only minimal update instead */
1802 file_data_unregister_notify_func(vf_notify_cb, vf);
1803 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1804 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1806 vf_refresh_idle(vf);
1810 /* mark functions can have various side effects - update all columns to be sure */
1811 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1812 /* mark functions can change sidecars too */
1813 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1815 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1817 gtk_tree_path_free(path);
1820 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1822 GtkTreeViewColumn *column;
1823 GtkCellRenderer *renderer;
1825 renderer = gtk_cell_renderer_toggle_new();
1826 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1828 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1829 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1830 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1832 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1833 gtk_tree_view_column_set_fixed_width(column, 22);
1834 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1837 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1841 *-----------------------------------------------------------------------------
1843 *-----------------------------------------------------------------------------
1846 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1849 if (!dir_fd) return FALSE;
1850 if (vf->dir_fd == dir_fd) return TRUE;
1852 file_data_unref(vf->dir_fd);
1853 vf->dir_fd = file_data_ref(dir_fd);
1855 /* force complete reload */
1856 vflist_store_clear(vf, TRUE);
1858 filelist_free(vf->list);
1861 ret = vf_refresh(vf);
1862 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
1866 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1868 ViewFile *vf = data;
1870 file_data_unregister_notify_func(vf_notify_cb, vf);
1872 vflist_select_idle_cancel(vf);
1873 vf_refresh_idle_cancel(vf);
1876 filelist_free(vf->list);
1879 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1881 GtkTreeStore *store;
1882 GtkTreeSelection *selection;
1883 GType flist_types[FILE_COLUMN_COUNT];
1887 vf->info = g_new0(ViewFileInfoList, 1);
1889 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1890 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1891 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1892 flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING;
1893 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1894 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
1895 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1896 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1897 flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN;
1898 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1899 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1900 flist_types[i] = G_TYPE_BOOLEAN;
1902 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
1904 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1905 g_object_unref(store);
1907 g_signal_connect(G_OBJECT(vf->listview), "row-expanded",
1908 G_CALLBACK(vflist_expand_cb), vf);
1910 g_signal_connect(G_OBJECT(vf->listview), "row-collapsed",
1911 G_CALLBACK(vflist_collapse_cb), vf);
1913 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1914 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1915 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
1917 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
1918 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
1922 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
1924 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
1925 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
1929 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
1930 g_assert(column == FILE_VIEW_COLUMN_THUMB);
1933 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE);
1934 g_assert(column == FILE_VIEW_COLUMN_FORMATTED);
1937 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
1938 g_assert(column == FILE_VIEW_COLUMN_SIZE);
1941 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
1942 g_assert(column == FILE_VIEW_COLUMN_DATE);
1945 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1949 void vflist_thumb_set(ViewFile *vf, gboolean enable)
1951 if (VFLIST(vf)->thumbs_enabled == enable) return;
1953 VFLIST(vf)->thumbs_enabled = enable;
1955 /* vflist_populate_view is better than vf_refresh:
1956 - no need to re-read the directory
1957 - force update because the formatted string has changed
1961 vflist_populate_view(vf, TRUE);
1962 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
1966 void vflist_marks_set(ViewFile *vf, gboolean enable)
1968 GList *columns, *work;
1970 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
1975 GtkTreeViewColumn *column = work->data;
1976 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
1979 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
1980 gtk_tree_view_column_set_visible(column, enable);
1985 // Previously disabled, which means that vf->list is complete
1986 file_data_lock_list(vf->list);
1990 // Previously enabled, which means that vf->list is incomplete
1993 g_list_free(columns);
1996 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */