4 * Copyright (C) 2008 - 2012 The Geeqie Team
8 * This software is released under the GNU General Public License (GNU GPL).
9 * Please read the included file COPYING for more information.
10 * This software comes with no warranty of any kind, use at your own risk!
14 #include "view_file_list.h"
17 #include "cache_maint.h"
22 #include "layout_image.h"
27 #include "ui_fileops.h"
29 #include "ui_tree_edit.h"
30 #include "uri_utils.h"
31 #include "view_file.h"
33 #include <gdk/gdkkeysyms.h> /* for keyboard values */
35 /* Index to tree store */
37 FILE_COLUMN_POINTER = 0,
40 FILE_COLUMN_FORMATTED,
48 FILE_COLUMN_MARKS_LAST = FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
53 /* Index to tree view */
55 FILE_VIEW_COLUMN_MARKS = 0,
56 FILE_VIEW_COLUMN_MARKS_LAST = FILE_VIEW_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
57 FILE_VIEW_COLUMN_THUMB,
58 FILE_VIEW_COLUMN_FORMATTED,
59 FILE_VIEW_COLUMN_SIZE,
60 FILE_VIEW_COLUMN_DATE,
61 FILE_VIEW_COLUMN_COUNT
66 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd);
67 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data);
68 static void vflist_populate_view(ViewFile *vf, gboolean force);
69 static gboolean vflist_is_multiline(ViewFile *vf);
70 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded);
74 *-----------------------------------------------------------------------------
76 *-----------------------------------------------------------------------------
83 } ViewFileFindRowData;
85 static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
87 ViewFileFindRowData *find = data;
89 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
100 static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter)
103 ViewFileFindRowData data = {fd, iter, FALSE, 0};
105 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
106 gtk_tree_model_foreach(store, vflist_find_row_cb, &data);
116 static FileData *vflist_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter)
119 GtkTreeViewColumn *column;
121 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), x, y,
122 &tpath, &column, NULL, NULL))
128 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
129 gtk_tree_model_get_iter(store, &row, tpath);
130 gtk_tree_path_free(tpath);
131 gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &fd, -1);
140 static gint vflist_find_sidecar_list_idx(GList *work, FileData *fd)
145 FileData *fd_p = work->data;
146 if (fd == fd_p) return i;
150 GList *work2 = fd_p->sidecar_files;
154 if (fd == fd_p) return i;
164 static gint vflist_sidecar_list_count(GList *work)
169 FileData *fd = work->data;
172 GList *work2 = fd->sidecar_files;
184 static gboolean vflist_store_clear_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
187 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
192 static void vflist_store_clear(ViewFile *vf)
195 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
196 gtk_tree_model_foreach(store, vflist_store_clear_cb, NULL);
197 gtk_tree_store_clear(GTK_TREE_STORE(store));
200 void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set)
205 if (vflist_find_row(vf, fd, &iter) < 0) return;
206 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
207 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
210 static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
215 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
217 tpath = gtk_tree_model_get_path(store, iter);
218 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
219 gtk_tree_path_free(tpath);
223 static gint vflist_column_idx(ViewFile *vf, gint store_idx)
225 GList *columns, *work;
228 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
232 GtkTreeViewColumn *column = work->data;
233 if (store_idx == GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx")))
239 g_list_free(columns);
245 *-----------------------------------------------------------------------------
247 *-----------------------------------------------------------------------------
250 static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
251 GtkSelectionData *selection_data, guint info,
252 guint time, gpointer data)
256 gchar *uri_text = NULL;
259 if (!VFLIST(vf)->click_fd) return;
261 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
263 list = vf_selection_get_list(vf);
267 list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
272 uri_text = uri_text_from_filelist(list, &total, (info == TARGET_TEXT_PLAIN));
275 DEBUG_1("%s", uri_text);
277 gtk_selection_data_set_text(selection_data, uri_text, total);
281 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
285 vflist_color_set(vf, VFLIST(vf)->click_fd, TRUE);
287 if (VFLIST(vf)->thumbs_enabled &&
288 VFLIST(vf)->click_fd && VFLIST(vf)->click_fd->thumb_pixbuf)
292 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
293 items = vf_selection_count(vf, NULL);
297 dnd_set_drag_icon(widget, context, VFLIST(vf)->click_fd->thumb_pixbuf, items);
301 static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
305 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
307 if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE)
313 static void vflist_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context,
314 int x, int y, GtkSelectionData *selection,
315 guint info, guint time, gpointer data)
319 if (info == TARGET_TEXT_PLAIN) {
320 FileData *fd = vflist_find_data_by_coord(vf, x, y, NULL);
323 /* Add keywords to file */
324 gchar *str = gtk_selection_data_get_text(selection);
325 GList *kw_list = string_to_keywords_list(str);
327 metadata_append_list(fd, KEYWORD_KEY, kw_list);
328 string_list_free(kw_list);
331 file notification should handle this automatically
332 if (vf->layout && vf->layout->bar_info) {
333 bar_set_fd(vf->layout->bar_info, fd);
340 void vflist_dnd_init(ViewFile *vf)
342 gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
343 dnd_file_drag_types, dnd_file_drag_types_count,
344 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
345 gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL,
346 dnd_file_drag_types, dnd_file_drag_types_count,
347 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
349 g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
350 G_CALLBACK(vflist_dnd_get), vf);
351 g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
352 G_CALLBACK(vflist_dnd_begin), vf);
353 g_signal_connect(G_OBJECT(vf->listview), "drag_end",
354 G_CALLBACK(vflist_dnd_end), vf);
355 g_signal_connect(G_OBJECT(vf->listview), "drag_data_received",
356 G_CALLBACK(vflist_drag_data_received), vf);
360 *-----------------------------------------------------------------------------
362 *-----------------------------------------------------------------------------
365 GList *vflist_selection_get_one(ViewFile *vf, FileData *fd)
367 GList *list = g_list_append(NULL, file_data_ref(fd));
369 if (fd->sidecar_files)
371 /* check if the row is expanded */
375 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
376 if (vflist_find_row(vf, fd, &iter) >= 0)
380 tpath = gtk_tree_model_get_path(store, &iter);
381 if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
383 /* unexpanded - add whole group */
384 GList *work = fd->sidecar_files;
387 FileData *sfd = work->data;
388 list = g_list_prepend(list, file_data_ref(sfd));
392 gtk_tree_path_free(tpath);
394 list = g_list_reverse(list);
400 GList *vflist_pop_menu_file_list(ViewFile *vf)
402 if (!VFLIST(vf)->click_fd) return NULL;
404 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
406 return vf_selection_get_list(vf);
408 return vflist_selection_get_one(vf, VFLIST(vf)->click_fd);
412 void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
416 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
420 list = vf_selection_get_list(vf);
421 view_window_new_from_list(list);
426 view_window_new(VFLIST(vf)->click_fd);
430 void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
435 list = vf_pop_menu_file_list(vf);
436 if (options->file_ops.enable_in_place_rename &&
437 list && !list->next && VFLIST(vf)->click_fd)
444 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
445 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
449 tpath = gtk_tree_model_get_path(store, &iter);
450 tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
451 FILE_VIEW_COLUMN_FORMATTED, VFLIST(vf)->click_fd->name,
452 vflist_row_rename_cb, vf);
453 gtk_tree_path_free(tpath);
458 file_util_rename(NULL, list, vf->listview);
461 void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
465 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
468 layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
472 vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled);
476 void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
480 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
482 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
485 void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
488 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
489 VFLIST(vf)->click_fd = NULL;
495 *-----------------------------------------------------------------------------
497 *-----------------------------------------------------------------------------
500 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
505 if (!new || !new[0]) return FALSE;
507 new_path = g_build_filename(vf->dir_fd->path, new, NULL);
509 if (strchr(new, G_DIR_SEPARATOR) != NULL)
511 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
512 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
517 gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL);
518 FileData *fd = file_data_new_group(old_path); /* get the fd from cache */
519 file_util_rename_simple(fd, new_path, vf->listview);
529 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
537 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return;
538 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
539 tpath = gtk_tree_model_get_path(store, &iter);
540 tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
541 gtk_tree_path_free(tpath);
543 popup_menu_position_clamp(menu, x, y, 0);
546 gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
551 if (event->keyval != GDK_Menu) return FALSE;
553 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
559 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
560 gtk_tree_model_get_iter(store, &iter, tpath);
561 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
562 gtk_tree_path_free(tpath);
566 VFLIST(vf)->click_fd = NULL;
569 vf->popup = vf_pop_menu(vf);
570 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
575 gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
581 GtkTreeViewColumn *column;
583 vf->clicked_mark = 0;
585 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
586 &tpath, &column, NULL, NULL))
589 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
591 if (bevent->button == MOUSE_BUTTON_LEFT &&
592 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
595 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
596 vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
598 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
600 gtk_tree_model_get_iter(store, &iter, tpath);
601 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
603 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE);
605 gtk_tree_path_free(tpath);
608 VFLIST(vf)->click_fd = fd;
610 if (bevent->button == MOUSE_BUTTON_RIGHT)
612 vf->popup = vf_pop_menu(vf);
613 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
614 bevent->button, bevent->time);
618 if (!fd) return FALSE;
620 if (bevent->button == MOUSE_BUTTON_MIDDLE)
622 if (!vflist_row_is_selected(vf, fd))
624 vflist_color_set(vf, fd, TRUE);
630 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
631 !(bevent->state & GDK_SHIFT_MASK ) &&
632 !(bevent->state & GDK_CONTROL_MASK ) &&
633 vflist_row_is_selected(vf, fd))
635 GtkTreeSelection *selection;
637 gtk_widget_grab_focus(widget);
640 /* returning FALSE and further processing of the event is needed for
641 correct operation of the expander, to show the sidecar files.
642 It however resets the selection of multiple files. With this condition
643 it should work for both cases */
644 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
645 return (gtk_tree_selection_count_selected_rows(selection) > 1);
649 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
651 if (vf->layout) layout_image_full_screen_start(vf->layout);
658 gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
665 if (bevent->button == MOUSE_BUTTON_MIDDLE)
667 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
670 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
675 if ((bevent->x != 0 || bevent->y != 0) &&
676 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
677 &tpath, NULL, NULL, NULL))
681 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
682 gtk_tree_model_get_iter(store, &iter, tpath);
683 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
684 gtk_tree_path_free(tpath);
687 if (bevent->button == MOUSE_BUTTON_MIDDLE)
689 if (fd && VFLIST(vf)->click_fd == fd)
691 GtkTreeSelection *selection;
693 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
694 if (vflist_row_is_selected(vf, fd))
696 gtk_tree_selection_unselect_iter(selection, &iter);
700 gtk_tree_selection_select_iter(selection, &iter);
706 if (fd && VFLIST(vf)->click_fd == fd &&
707 !(bevent->state & GDK_SHIFT_MASK ) &&
708 !(bevent->state & GDK_CONTROL_MASK ) &&
709 vflist_row_is_selected(vf, fd))
711 GtkTreeSelection *selection;
713 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
714 gtk_tree_selection_unselect_all(selection);
715 gtk_tree_selection_select_iter(selection, &iter);
716 vflist_move_cursor(vf, &iter);
717 // return TRUE;// FIXME - expand
723 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
725 FileData *read_ahead_fd = NULL;
731 cur_fd = layout_image_get_fd(vf->layout);
732 if (sel_fd == cur_fd) return; /* no change */
734 row = g_list_index(vf->list, sel_fd);
735 // FIXME sidecar data
737 if (sel_fd && options->image.enable_read_ahead && row >= 0)
739 if (row > g_list_index(vf->list, cur_fd) &&
740 (guint) (row + 1) < vf_count(vf, NULL))
742 read_ahead_fd = vf_index_get_data(vf, row + 1);
746 read_ahead_fd = vf_index_get_data(vf, row - 1);
750 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
753 static gboolean vflist_select_idle_cb(gpointer data)
759 VFLIST(vf)->select_idle_id = 0;
765 if (VFLIST(vf)->select_fd)
767 vflist_select_image(vf, VFLIST(vf)->select_fd);
768 VFLIST(vf)->select_fd = NULL;
771 VFLIST(vf)->select_idle_id = 0;
775 static void vflist_select_idle_cancel(ViewFile *vf)
777 if (VFLIST(vf)->select_idle_id)
779 g_source_remove(VFLIST(vf)->select_idle_id);
780 VFLIST(vf)->select_idle_id = 0;
784 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
785 gboolean path_currently_selected, gpointer data)
790 if (!path_currently_selected &&
791 gtk_tree_model_get_iter(store, &iter, tpath))
793 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
797 VFLIST(vf)->select_fd = NULL;
801 !VFLIST(vf)->select_idle_id)
803 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
809 static void vflist_expand_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
812 vflist_set_expanded(vf, iter, TRUE);
815 static void vflist_collapse_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
818 vflist_set_expanded(vf, iter, FALSE);
822 *-----------------------------------------------------------------------------
824 *-----------------------------------------------------------------------------
828 static gboolean vflist_dummy_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
829 gboolean path_currently_selected, gpointer data)
835 static gchar* vflist_get_formatted(ViewFile *vf, const gchar *name, const gchar *sidecars, const gchar *size, const gchar *time, gboolean expanded)
837 gboolean multiline = vflist_is_multiline(vf);
842 text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time);
846 text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars);
851 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded)
860 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
862 gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
863 FILE_COLUMN_NAME, &name,
864 FILE_COLUMN_SIDECARS, &sidecars,
865 FILE_COLUMN_SIZE, &size,
866 FILE_COLUMN_DATE, &time,
868 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
870 gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED, formatted,
871 FILE_COLUMN_EXPANDED, expanded,
880 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
883 gchar *sidecars = NULL;
885 const gchar *time = text_from_time(fd->date);
886 gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
887 const gchar *disabled_grouping;
889 gboolean expanded = FALSE;
891 if (fd->sidecar_files) /* expanded has no effect on files without sidecars */
893 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_EXPANDED, &expanded, -1);
896 sidecars = file_data_sc_list_to_string(fd);
898 disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
899 name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
900 size = text_from_size(fd->size);
902 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
904 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
905 FILE_COLUMN_VERSION, fd->version,
906 FILE_COLUMN_THUMB, fd->thumb_pixbuf,
907 FILE_COLUMN_FORMATTED, formatted,
908 FILE_COLUMN_SIDECARS, sidecars,
909 FILE_COLUMN_NAME, name,
910 FILE_COLUMN_SIZE, size,
911 FILE_COLUMN_DATE, time,
912 #define STORE_SET_IS_SLOW 1
913 #if STORE_SET_IS_SLOW
914 /* this is 3x faster on a directory with 20000 files */
915 FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
916 FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
917 FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
918 FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
919 FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
920 FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
921 #if FILEDATA_MARKS_SIZE != 6
922 #error this needs to be updated
925 FILE_COLUMN_COLOR, FALSE, -1);
927 #if !STORE_SET_IS_SLOW
930 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
931 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
940 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected, gboolean force)
945 gint num_ordered = 0;
946 gint num_prepended = 0;
948 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
954 FileData *fd = work->data;
955 gboolean done = FALSE;
959 FileData *old_fd = NULL;
960 gint old_version = 0;
964 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
965 FILE_COLUMN_POINTER, &old_fd,
966 FILE_COLUMN_VERSION, &old_version,
976 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
978 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
980 if (match == 0) g_warning("multiple fd for the same path");
996 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
1001 here should be used gtk_tree_store_append, but this function seems to be O(n)
1002 and it seems to be much faster to add new entries to the beginning and reorder later
1005 gtk_tree_store_prepend(store, &new, parent_iter);
1008 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
1009 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected, force);
1011 if (g_list_find(selected, fd))
1013 /* renamed files - the same fd appears at different position - select it again*/
1014 GtkTreeSelection *selection;
1015 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1016 gtk_tree_selection_select_iter(selection, &new);
1023 file_data_unref(old_fd);
1024 valid = gtk_tree_store_remove(store, &iter);
1029 if (fd->version != old_version || force)
1031 vflist_setup_iter(vf, store, &iter, fd);
1032 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected, force);
1035 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1046 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1047 file_data_unref(old_fd);
1049 valid = gtk_tree_store_remove(store, &iter);
1052 /* move the prepended entries to the correct position */
1056 gint num_total = num_prepended + num_ordered;
1057 gint *new_order = g_malloc(num_total * sizeof(gint));
1059 for (i = 0; i < num_total; i++)
1061 if (i < num_ordered)
1062 new_order[i] = num_prepended + i;
1064 new_order[i] = num_total - 1 - i;
1066 gtk_tree_store_reorder(store, parent_iter, new_order);
1072 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
1075 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
1077 GtkTreeStore *store;
1080 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1081 if (!vf->list) return;
1087 FileData *fd = work->data;
1088 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1093 vf->sort_method = type;
1094 vf->sort_ascend = ascend;
1096 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1098 new_order = g_malloc(i * sizeof(gint));
1104 FileData *fd = work->data;
1105 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1110 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1111 gtk_tree_store_reorder(store, NULL, new_order);
1114 g_hash_table_destroy(fd_idx_hash);
1118 *-----------------------------------------------------------------------------
1120 *-----------------------------------------------------------------------------
1124 void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1129 FileData *fd = work->data;
1132 if (fd->thumb_pixbuf) (*done)++;
1134 if (fd->sidecar_files)
1136 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1142 void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
1144 GtkTreeStore *store;
1147 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1149 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1150 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1153 FileData *vflist_thumb_next_fd(ViewFile *vf)
1156 FileData *fd = NULL;
1158 /* first check the visible files */
1160 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1162 GtkTreeModel *store;
1164 gboolean valid = TRUE;
1166 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1167 gtk_tree_model_get_iter(store, &iter, tpath);
1168 gtk_tree_path_free(tpath);
1170 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1174 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1176 if (!nfd->thumb_pixbuf) fd = nfd;
1178 valid = gtk_tree_model_iter_next(store, &iter);
1182 /* then find first undone */
1186 GList *work = vf->list;
1189 FileData *fd_p = work->data;
1190 if (!fd_p->thumb_pixbuf)
1194 GList *work2 = fd_p->sidecar_files;
1196 while (work2 && !fd)
1199 if (!fd_p->thumb_pixbuf) fd = fd_p;
1200 work2 = work2->next;
1211 void vflist_thumb_reset_all(ViewFile *vf)
1213 GList *work = vf->list;
1216 FileData *fd = work->data;
1217 if (fd->thumb_pixbuf)
1219 g_object_unref(fd->thumb_pixbuf);
1220 fd->thumb_pixbuf = NULL;
1227 *-----------------------------------------------------------------------------
1229 *-----------------------------------------------------------------------------
1232 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1234 return g_list_nth_data(vf->list, row);
1237 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1240 GList *work, *work2;
1245 FileData *list_fd = work->data;
1246 if (list_fd == fd) return p;
1248 work2 = list_fd->sidecar_files;
1251 /* FIXME: return the same index also for sidecars
1252 it is sufficient for next/prev navigation but it should be rewritten
1253 without using indexes at all
1255 FileData *sidecar_fd = work2->data;
1256 if (sidecar_fd == fd) return p;
1257 work2 = work2->next;
1267 guint vflist_count(ViewFile *vf, gint64 *bytes)
1277 FileData *fd = work->data;
1285 return g_list_length(vf->list);
1288 GList *vflist_get_list(ViewFile *vf)
1296 FileData *fd = work->data;
1299 list = g_list_prepend(list, file_data_ref(fd));
1302 return g_list_reverse(list);
1306 *-----------------------------------------------------------------------------
1308 *-----------------------------------------------------------------------------
1311 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1313 GtkTreeModel *store;
1314 GtkTreeSelection *selection;
1317 gboolean found = FALSE;
1319 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1320 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1322 while (!found && work)
1324 GtkTreePath *tpath = work->data;
1328 gtk_tree_model_get_iter(store, &iter, tpath);
1329 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1330 if (fd_n == fd) found = TRUE;
1333 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1339 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1343 fd = vf_index_get_data(vf, row);
1344 return vflist_row_is_selected(vf, fd);
1347 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1349 GtkTreeModel *store;
1350 GtkTreeSelection *selection;
1354 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1355 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1365 GtkTreePath *tpath = work->data;
1369 gtk_tree_model_get_iter(store, &iter, tpath);
1370 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1379 count = g_list_length(slist);
1380 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1386 GList *vflist_selection_get_list(ViewFile *vf)
1388 GtkTreeModel *store;
1389 GtkTreeSelection *selection;
1394 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1395 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1399 GtkTreePath *tpath = work->data;
1403 gtk_tree_model_get_iter(store, &iter, tpath);
1404 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1406 list = g_list_prepend(list, file_data_ref(fd));
1408 if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
1410 /* unexpanded - add whole group */
1411 GList *work2 = fd->sidecar_files;
1414 FileData *sfd = work2->data;
1415 list = g_list_prepend(list, file_data_ref(sfd));
1416 work2 = work2->next;
1422 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1425 return g_list_reverse(list);
1428 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1430 GtkTreeModel *store;
1431 GtkTreeSelection *selection;
1436 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1437 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1441 GtkTreePath *tpath = work->data;
1445 gtk_tree_model_get_iter(store, &iter, tpath);
1446 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1448 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1452 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1455 return g_list_reverse(list);
1458 void vflist_select_all(ViewFile *vf)
1460 GtkTreeSelection *selection;
1462 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1463 gtk_tree_selection_select_all(selection);
1465 VFLIST(vf)->select_fd = NULL;
1468 void vflist_select_none(ViewFile *vf)
1470 GtkTreeSelection *selection;
1472 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1473 gtk_tree_selection_unselect_all(selection);
1476 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1481 tpath = gtk_tree_model_get_path(store, iter);
1482 result = gtk_tree_path_prev(tpath);
1484 gtk_tree_model_get_iter(store, iter, tpath);
1486 gtk_tree_path_free(tpath);
1491 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1493 if (!gtk_tree_model_get_iter_first(store, iter))
1498 GtkTreeIter next = *iter;
1500 if (gtk_tree_model_iter_next(store, &next))
1509 void vflist_select_invert(ViewFile *vf)
1512 GtkTreeSelection *selection;
1513 GtkTreeModel *store;
1516 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1517 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1519 /* Backward iteration prevents scrolling to the end of the list,
1520 * it scrolls to the first selected row instead. */
1521 valid = tree_model_get_iter_last(store, &iter);
1525 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1528 gtk_tree_selection_unselect_iter(selection, &iter);
1530 gtk_tree_selection_select_iter(selection, &iter);
1532 valid = tree_model_iter_prev(store, &iter);
1536 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1540 if (vflist_find_row(vf, fd, &iter) < 0) return;
1542 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1544 if (!vflist_row_is_selected(vf, fd))
1546 GtkTreeSelection *selection;
1547 GtkTreeModel *store;
1550 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1551 gtk_tree_selection_unselect_all(selection);
1552 gtk_tree_selection_select_iter(selection, &iter);
1553 vflist_move_cursor(vf, &iter);
1555 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1556 tpath = gtk_tree_model_get_path(store, &iter);
1557 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1558 gtk_tree_path_free(tpath);
1562 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1565 FileData *fd = NULL;
1567 if (sel_fd->parent) sel_fd = sel_fd->parent;
1576 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1578 if (match >= 0) break;
1581 if (fd) vflist_select_by_fd(vf, fd);
1585 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1587 GtkTreeModel *store;
1589 GtkTreeSelection *selection;
1593 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1595 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1596 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1598 valid = gtk_tree_model_get_iter_first(store, &iter);
1602 gboolean mark_val, selected;
1603 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1605 mark_val = file_data_get_mark(fd, n);
1606 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1610 case MTS_MODE_SET: selected = mark_val;
1612 case MTS_MODE_OR: selected = mark_val || selected;
1614 case MTS_MODE_AND: selected = mark_val && selected;
1616 case MTS_MODE_MINUS: selected = !mark_val && selected;
1621 gtk_tree_selection_select_iter(selection, &iter);
1623 gtk_tree_selection_unselect_iter(selection, &iter);
1625 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1629 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1631 GtkTreeModel *store;
1632 GtkTreeSelection *selection;
1637 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1639 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1640 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1644 GtkTreePath *tpath = work->data;
1648 gtk_tree_model_get_iter(store, &iter, tpath);
1649 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1651 /* the change has a very limited range and the standard notification would trigger
1652 complete re-read of the directory - try to do only minimal update instead */
1653 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1657 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1659 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1661 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1665 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1667 vf_refresh_idle(vf);
1671 /* mark functions can have various side effects - update all columns to be sure */
1672 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1673 /* mark functions can change sidecars too */
1674 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1678 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1682 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1687 *-----------------------------------------------------------------------------
1689 *-----------------------------------------------------------------------------
1692 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
1694 GtkTreeViewColumn *column;
1695 GtkCellRenderer *cell;
1698 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1699 if (!column) return;
1701 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1703 #if GTK_CHECK_VERSION(2,18,0)
1704 list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1706 list = gtk_tree_view_column_get_cell_renderers(column);
1712 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1713 gtk_tree_view_column_set_visible(column, thumb);
1715 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1716 if (!column) return;
1717 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1719 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1720 if (!column) return;
1721 gtk_tree_view_column_set_visible(column, !multiline);
1723 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1724 if (!column) return;
1725 gtk_tree_view_column_set_visible(column, !multiline);
1728 static gboolean vflist_is_multiline(ViewFile *vf)
1730 return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
1734 static void vflist_populate_view(ViewFile *vf, gboolean force)
1736 GtkTreeStore *store;
1739 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1745 vflist_store_clear(vf);
1750 vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
1752 selected = vflist_selection_get_list(vf);
1754 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force);
1756 if (selected && vflist_selection_count(vf, NULL) == 0)
1758 /* all selected files disappeared */
1759 vflist_select_closest(vf, selected->data);
1762 filelist_free(selected);
1765 vf_thumb_update(vf);
1768 gboolean vflist_refresh(ViewFile *vf)
1771 gboolean ret = TRUE;
1773 old_list = vf->list;
1776 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1779 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1781 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1782 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1783 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1785 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1786 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1789 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1791 vflist_populate_view(vf, FALSE);
1793 filelist_free(old_list);
1794 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1801 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1803 #define CELL_HEIGHT_OVERRIDE 512
1805 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1809 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1810 if (spec && G_IS_PARAM_SPEC_INT(spec))
1812 GParamSpecInt *spec_int;
1814 spec_int = G_PARAM_SPEC_INT(spec);
1815 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1819 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1821 static GdkColor color;
1822 static GtkWidget *done = NULL;
1828 style = gtk_widget_get_style(widget);
1829 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1830 shift_color(&color, -1, 0);
1837 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1838 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1840 ViewFile *vf = data;
1843 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1844 g_object_set(G_OBJECT(cell),
1845 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1846 "cell-background-set", set, NULL);
1849 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1851 GtkTreeViewColumn *column;
1852 GtkCellRenderer *renderer;
1854 column = gtk_tree_view_column_new();
1855 gtk_tree_view_column_set_title(column, title);
1856 gtk_tree_view_column_set_min_width(column, 4);
1860 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1861 renderer = gtk_cell_renderer_text_new();
1864 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1866 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1867 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1869 gtk_tree_view_column_set_expand(column, TRUE);
1873 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1874 renderer = gtk_cell_renderer_pixbuf_new();
1875 cell_renderer_height_override(renderer);
1876 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1877 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1880 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1881 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1882 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1884 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1887 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1889 ViewFile *vf = data;
1890 GtkTreeStore *store;
1891 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1897 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1898 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1901 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1903 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1905 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1908 /* the change has a very limited range and the standard notification would trigger
1909 complete re-read of the directory - try to do only minimal update instead */
1910 file_data_unregister_notify_func(vf_notify_cb, vf);
1911 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1912 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1914 vf_refresh_idle(vf);
1918 /* mark functions can have various side effects - update all columns to be sure */
1919 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1920 /* mark functions can change sidecars too */
1921 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1923 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1925 gtk_tree_path_free(path);
1928 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1930 GtkTreeViewColumn *column;
1931 GtkCellRenderer *renderer;
1932 GtkTreeStore *store;
1935 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1937 renderer = gtk_cell_renderer_toggle_new();
1938 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1940 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1941 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1942 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1944 index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1945 gtk_tree_view_column_set_fixed_width(column, 22);
1946 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1949 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1953 *-----------------------------------------------------------------------------
1955 *-----------------------------------------------------------------------------
1958 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1961 if (!dir_fd) return FALSE;
1962 if (vf->dir_fd == dir_fd) return TRUE;
1964 file_data_unref(vf->dir_fd);
1965 vf->dir_fd = file_data_ref(dir_fd);
1967 /* force complete reload */
1968 vflist_store_clear(vf);
1970 filelist_free(vf->list);
1973 ret = vf_refresh(vf);
1974 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
1978 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1980 ViewFile *vf = data;
1982 file_data_unregister_notify_func(vf_notify_cb, vf);
1984 vflist_select_idle_cancel(vf);
1985 vf_refresh_idle_cancel(vf);
1988 filelist_free(vf->list);
1991 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1993 GtkTreeStore *store;
1994 GtkTreeSelection *selection;
1995 GType flist_types[FILE_COLUMN_COUNT];
1999 vf->info = g_new0(ViewFileInfoList, 1);
2001 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
2002 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
2003 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
2004 flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING;
2005 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
2006 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
2007 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
2008 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
2009 flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN;
2010 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
2011 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
2012 flist_types[i] = G_TYPE_BOOLEAN;
2014 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
2016 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2017 g_object_unref(store);
2019 g_signal_connect(G_OBJECT(vf->listview), "row-expanded",
2020 G_CALLBACK(vflist_expand_cb), vf);
2022 g_signal_connect(G_OBJECT(vf->listview), "row-collapsed",
2023 G_CALLBACK(vflist_collapse_cb), vf);
2025 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2026 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
2027 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
2029 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2030 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2034 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2036 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2037 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
2041 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2042 g_assert(column == FILE_VIEW_COLUMN_THUMB);
2045 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE);
2046 g_assert(column == FILE_VIEW_COLUMN_FORMATTED);
2049 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2050 g_assert(column == FILE_VIEW_COLUMN_SIZE);
2053 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2054 g_assert(column == FILE_VIEW_COLUMN_DATE);
2057 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2061 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2063 if (VFLIST(vf)->thumbs_enabled == enable) return;
2065 VFLIST(vf)->thumbs_enabled = enable;
2067 /* vflist_populate_view is better than vf_refresh:
2068 - no need to re-read the directory
2069 - force update because the formatted string has changed
2073 vflist_populate_view(vf, TRUE);
2074 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
2078 void vflist_marks_set(ViewFile *vf, gboolean enable)
2080 GList *columns, *work;
2082 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2087 GtkTreeViewColumn *column = work->data;
2088 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2091 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2092 gtk_tree_view_column_set_visible(column, enable);
2095 g_list_free(columns);
2099 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */