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));
271 uri_selection_data_set_uris_from_filelist(selection_data, list);
275 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
279 vflist_color_set(vf, VFLIST(vf)->click_fd, TRUE);
281 if (VFLIST(vf)->thumbs_enabled &&
282 VFLIST(vf)->click_fd && VFLIST(vf)->click_fd->thumb_pixbuf)
286 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
287 items = vf_selection_count(vf, NULL);
291 dnd_set_drag_icon(widget, context, VFLIST(vf)->click_fd->thumb_pixbuf, items);
295 static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
299 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
301 if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE)
307 static void vflist_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context,
308 int x, int y, GtkSelectionData *selection,
309 guint info, guint time, gpointer data)
313 if (info == TARGET_TEXT_PLAIN) {
314 FileData *fd = vflist_find_data_by_coord(vf, x, y, NULL);
317 /* Add keywords to file */
318 gchar *str = gtk_selection_data_get_text(selection);
319 GList *kw_list = string_to_keywords_list(str);
321 metadata_append_list(fd, KEYWORD_KEY, kw_list);
322 string_list_free(kw_list);
325 file notification should handle this automatically
326 if (vf->layout && vf->layout->bar_info) {
327 bar_set_fd(vf->layout->bar_info, fd);
334 void vflist_dnd_init(ViewFile *vf)
336 gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
337 dnd_file_drag_types, dnd_file_drag_types_count,
338 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
339 gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL,
340 dnd_file_drag_types, dnd_file_drag_types_count,
341 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
343 g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
344 G_CALLBACK(vflist_dnd_get), vf);
345 g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
346 G_CALLBACK(vflist_dnd_begin), vf);
347 g_signal_connect(G_OBJECT(vf->listview), "drag_end",
348 G_CALLBACK(vflist_dnd_end), vf);
349 g_signal_connect(G_OBJECT(vf->listview), "drag_data_received",
350 G_CALLBACK(vflist_drag_data_received), vf);
354 *-----------------------------------------------------------------------------
356 *-----------------------------------------------------------------------------
359 GList *vflist_selection_get_one(ViewFile *vf, FileData *fd)
361 GList *list = g_list_append(NULL, file_data_ref(fd));
363 if (fd->sidecar_files)
365 /* check if the row is expanded */
369 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
370 if (vflist_find_row(vf, fd, &iter) >= 0)
374 tpath = gtk_tree_model_get_path(store, &iter);
375 if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
377 /* unexpanded - add whole group */
378 GList *work = fd->sidecar_files;
381 FileData *sfd = work->data;
382 list = g_list_prepend(list, file_data_ref(sfd));
386 gtk_tree_path_free(tpath);
388 list = g_list_reverse(list);
394 GList *vflist_pop_menu_file_list(ViewFile *vf)
396 if (!VFLIST(vf)->click_fd) return NULL;
398 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
400 return vf_selection_get_list(vf);
402 return vflist_selection_get_one(vf, VFLIST(vf)->click_fd);
406 void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
410 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
414 list = vf_selection_get_list(vf);
415 view_window_new_from_list(list);
420 view_window_new(VFLIST(vf)->click_fd);
424 void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
429 list = vf_pop_menu_file_list(vf);
430 if (options->file_ops.enable_in_place_rename &&
431 list && !list->next && VFLIST(vf)->click_fd)
438 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
439 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
443 tpath = gtk_tree_model_get_path(store, &iter);
444 tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
445 FILE_VIEW_COLUMN_FORMATTED, VFLIST(vf)->click_fd->name,
446 vflist_row_rename_cb, vf);
447 gtk_tree_path_free(tpath);
452 file_util_rename(NULL, list, vf->listview);
455 void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
459 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
462 layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
466 vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled);
470 void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
474 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
476 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
479 void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
482 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
483 VFLIST(vf)->click_fd = NULL;
489 *-----------------------------------------------------------------------------
491 *-----------------------------------------------------------------------------
494 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
499 if (!new || !new[0]) return FALSE;
501 new_path = g_build_filename(vf->dir_fd->path, new, NULL);
503 if (strchr(new, G_DIR_SEPARATOR) != NULL)
505 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
506 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
511 gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL);
512 FileData *fd = file_data_new_group(old_path); /* get the fd from cache */
513 file_util_rename_simple(fd, new_path, vf->listview);
523 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
531 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return;
532 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
533 tpath = gtk_tree_model_get_path(store, &iter);
534 tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
535 gtk_tree_path_free(tpath);
537 popup_menu_position_clamp(menu, x, y, 0);
540 gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
545 if (event->keyval != GDK_KEY_Menu) return FALSE;
547 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
553 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
554 gtk_tree_model_get_iter(store, &iter, tpath);
555 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
556 gtk_tree_path_free(tpath);
560 VFLIST(vf)->click_fd = NULL;
563 vf->popup = vf_pop_menu(vf);
564 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
569 gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
575 GtkTreeViewColumn *column;
577 vf->clicked_mark = 0;
579 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
580 &tpath, &column, NULL, NULL))
583 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
585 if (bevent->button == MOUSE_BUTTON_LEFT &&
586 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
589 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
590 vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
592 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
594 gtk_tree_model_get_iter(store, &iter, tpath);
595 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
597 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE);
599 gtk_tree_path_free(tpath);
602 VFLIST(vf)->click_fd = fd;
604 if (bevent->button == MOUSE_BUTTON_RIGHT)
606 vf->popup = vf_pop_menu(vf);
607 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
608 bevent->button, bevent->time);
612 if (!fd) return FALSE;
614 if (bevent->button == MOUSE_BUTTON_MIDDLE)
616 if (!vflist_row_is_selected(vf, fd))
618 vflist_color_set(vf, fd, TRUE);
624 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
625 !(bevent->state & GDK_SHIFT_MASK ) &&
626 !(bevent->state & GDK_CONTROL_MASK ) &&
627 vflist_row_is_selected(vf, fd))
629 GtkTreeSelection *selection;
631 gtk_widget_grab_focus(widget);
634 /* returning FALSE and further processing of the event is needed for
635 correct operation of the expander, to show the sidecar files.
636 It however resets the selection of multiple files. With this condition
637 it should work for both cases */
638 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
639 return (gtk_tree_selection_count_selected_rows(selection) > 1);
643 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
645 if (vf->layout) layout_image_full_screen_start(vf->layout);
652 gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
659 if (bevent->button == MOUSE_BUTTON_MIDDLE)
661 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
664 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
669 if ((bevent->x != 0 || bevent->y != 0) &&
670 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
671 &tpath, NULL, NULL, NULL))
675 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
676 gtk_tree_model_get_iter(store, &iter, tpath);
677 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
678 gtk_tree_path_free(tpath);
681 if (bevent->button == MOUSE_BUTTON_MIDDLE)
683 if (fd && VFLIST(vf)->click_fd == fd)
685 GtkTreeSelection *selection;
687 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
688 if (vflist_row_is_selected(vf, fd))
690 gtk_tree_selection_unselect_iter(selection, &iter);
694 gtk_tree_selection_select_iter(selection, &iter);
700 if (fd && VFLIST(vf)->click_fd == fd &&
701 !(bevent->state & GDK_SHIFT_MASK ) &&
702 !(bevent->state & GDK_CONTROL_MASK ) &&
703 vflist_row_is_selected(vf, fd))
705 GtkTreeSelection *selection;
707 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
708 gtk_tree_selection_unselect_all(selection);
709 gtk_tree_selection_select_iter(selection, &iter);
710 vflist_move_cursor(vf, &iter);
711 // return TRUE;// FIXME - expand
717 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
719 FileData *read_ahead_fd = NULL;
725 cur_fd = layout_image_get_fd(vf->layout);
726 if (sel_fd == cur_fd) return; /* no change */
728 row = g_list_index(vf->list, sel_fd);
729 // FIXME sidecar data
731 if (sel_fd && options->image.enable_read_ahead && row >= 0)
733 if (row > g_list_index(vf->list, cur_fd) &&
734 (guint) (row + 1) < vf_count(vf, NULL))
736 read_ahead_fd = vf_index_get_data(vf, row + 1);
740 read_ahead_fd = vf_index_get_data(vf, row - 1);
744 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
747 static gboolean vflist_select_idle_cb(gpointer data)
753 VFLIST(vf)->select_idle_id = 0;
759 if (VFLIST(vf)->select_fd)
761 vflist_select_image(vf, VFLIST(vf)->select_fd);
762 VFLIST(vf)->select_fd = NULL;
765 VFLIST(vf)->select_idle_id = 0;
769 static void vflist_select_idle_cancel(ViewFile *vf)
771 if (VFLIST(vf)->select_idle_id)
773 g_source_remove(VFLIST(vf)->select_idle_id);
774 VFLIST(vf)->select_idle_id = 0;
778 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
779 gboolean path_currently_selected, gpointer data)
784 if (!path_currently_selected &&
785 gtk_tree_model_get_iter(store, &iter, tpath))
787 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
791 VFLIST(vf)->select_fd = NULL;
795 !VFLIST(vf)->select_idle_id)
797 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
803 static void vflist_expand_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
806 vflist_set_expanded(vf, iter, TRUE);
809 static void vflist_collapse_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
812 vflist_set_expanded(vf, iter, FALSE);
816 *-----------------------------------------------------------------------------
818 *-----------------------------------------------------------------------------
822 static gboolean vflist_dummy_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
823 gboolean path_currently_selected, gpointer data)
829 static gchar* vflist_get_formatted(ViewFile *vf, const gchar *name, const gchar *sidecars, const gchar *size, const gchar *time, gboolean expanded)
831 gboolean multiline = vflist_is_multiline(vf);
836 text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time);
840 text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars);
845 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded)
854 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
856 gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
857 FILE_COLUMN_NAME, &name,
858 FILE_COLUMN_SIDECARS, &sidecars,
859 FILE_COLUMN_SIZE, &size,
860 FILE_COLUMN_DATE, &time,
862 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
864 gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED, formatted,
865 FILE_COLUMN_EXPANDED, expanded,
874 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
877 gchar *sidecars = NULL;
879 const gchar *time = text_from_time(fd->date);
880 gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
881 const gchar *disabled_grouping;
883 gboolean expanded = FALSE;
885 if (fd->sidecar_files) /* expanded has no effect on files without sidecars */
887 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_EXPANDED, &expanded, -1);
890 sidecars = file_data_sc_list_to_string(fd);
892 disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
893 name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
894 size = text_from_size(fd->size);
896 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
898 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
899 FILE_COLUMN_VERSION, fd->version,
900 FILE_COLUMN_THUMB, fd->thumb_pixbuf,
901 FILE_COLUMN_FORMATTED, formatted,
902 FILE_COLUMN_SIDECARS, sidecars,
903 FILE_COLUMN_NAME, name,
904 FILE_COLUMN_SIZE, size,
905 FILE_COLUMN_DATE, time,
906 #define STORE_SET_IS_SLOW 1
907 #if STORE_SET_IS_SLOW
908 /* this is 3x faster on a directory with 20000 files */
909 FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
910 FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
911 FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
912 FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
913 FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
914 FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
915 #if FILEDATA_MARKS_SIZE != 6
916 #error this needs to be updated
919 FILE_COLUMN_COLOR, FALSE, -1);
921 #if !STORE_SET_IS_SLOW
924 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
925 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
934 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected, gboolean force)
939 gint num_ordered = 0;
940 gint num_prepended = 0;
942 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
948 FileData *fd = work->data;
949 gboolean done = FALSE;
953 FileData *old_fd = NULL;
954 gint old_version = 0;
958 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
959 FILE_COLUMN_POINTER, &old_fd,
960 FILE_COLUMN_VERSION, &old_version,
970 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
972 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
974 if (match == 0) g_warning("multiple fd for the same path");
990 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
995 here should be used gtk_tree_store_append, but this function seems to be O(n)
996 and it seems to be much faster to add new entries to the beginning and reorder later
999 gtk_tree_store_prepend(store, &new, parent_iter);
1002 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
1003 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected, force);
1005 if (g_list_find(selected, fd))
1007 /* renamed files - the same fd appears at different position - select it again*/
1008 GtkTreeSelection *selection;
1009 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1010 gtk_tree_selection_select_iter(selection, &new);
1017 file_data_unref(old_fd);
1018 valid = gtk_tree_store_remove(store, &iter);
1023 if (fd->version != old_version || force)
1025 vflist_setup_iter(vf, store, &iter, fd);
1026 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected, force);
1029 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1040 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1041 file_data_unref(old_fd);
1043 valid = gtk_tree_store_remove(store, &iter);
1046 /* move the prepended entries to the correct position */
1050 gint num_total = num_prepended + num_ordered;
1051 gint *new_order = g_malloc(num_total * sizeof(gint));
1053 for (i = 0; i < num_total; i++)
1055 if (i < num_ordered)
1056 new_order[i] = num_prepended + i;
1058 new_order[i] = num_total - 1 - i;
1060 gtk_tree_store_reorder(store, parent_iter, new_order);
1066 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
1069 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
1071 GtkTreeStore *store;
1074 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1075 if (!vf->list) return;
1081 FileData *fd = work->data;
1082 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1087 vf->sort_method = type;
1088 vf->sort_ascend = ascend;
1090 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1092 new_order = g_malloc(i * sizeof(gint));
1098 FileData *fd = work->data;
1099 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1104 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1105 gtk_tree_store_reorder(store, NULL, new_order);
1108 g_hash_table_destroy(fd_idx_hash);
1112 *-----------------------------------------------------------------------------
1114 *-----------------------------------------------------------------------------
1118 void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1123 FileData *fd = work->data;
1126 if (fd->thumb_pixbuf) (*done)++;
1128 if (fd->sidecar_files)
1130 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1136 void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
1138 GtkTreeStore *store;
1141 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1143 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1144 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1147 FileData *vflist_thumb_next_fd(ViewFile *vf)
1150 FileData *fd = NULL;
1152 /* first check the visible files */
1154 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1156 GtkTreeModel *store;
1158 gboolean valid = TRUE;
1160 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1161 gtk_tree_model_get_iter(store, &iter, tpath);
1162 gtk_tree_path_free(tpath);
1164 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1168 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1170 if (!nfd->thumb_pixbuf) fd = nfd;
1172 valid = gtk_tree_model_iter_next(store, &iter);
1176 /* then find first undone */
1180 GList *work = vf->list;
1183 FileData *fd_p = work->data;
1184 if (!fd_p->thumb_pixbuf)
1188 GList *work2 = fd_p->sidecar_files;
1190 while (work2 && !fd)
1193 if (!fd_p->thumb_pixbuf) fd = fd_p;
1194 work2 = work2->next;
1205 void vflist_thumb_reset_all(ViewFile *vf)
1207 GList *work = vf->list;
1210 FileData *fd = work->data;
1211 if (fd->thumb_pixbuf)
1213 g_object_unref(fd->thumb_pixbuf);
1214 fd->thumb_pixbuf = NULL;
1221 *-----------------------------------------------------------------------------
1223 *-----------------------------------------------------------------------------
1226 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1228 return g_list_nth_data(vf->list, row);
1231 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1234 GList *work, *work2;
1239 FileData *list_fd = work->data;
1240 if (list_fd == fd) return p;
1242 work2 = list_fd->sidecar_files;
1245 /* FIXME: return the same index also for sidecars
1246 it is sufficient for next/prev navigation but it should be rewritten
1247 without using indexes at all
1249 FileData *sidecar_fd = work2->data;
1250 if (sidecar_fd == fd) return p;
1251 work2 = work2->next;
1261 guint vflist_count(ViewFile *vf, gint64 *bytes)
1271 FileData *fd = work->data;
1279 return g_list_length(vf->list);
1282 GList *vflist_get_list(ViewFile *vf)
1290 FileData *fd = work->data;
1293 list = g_list_prepend(list, file_data_ref(fd));
1296 return g_list_reverse(list);
1300 *-----------------------------------------------------------------------------
1302 *-----------------------------------------------------------------------------
1305 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1307 GtkTreeModel *store;
1308 GtkTreeSelection *selection;
1311 gboolean found = FALSE;
1313 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1314 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1316 while (!found && work)
1318 GtkTreePath *tpath = work->data;
1322 gtk_tree_model_get_iter(store, &iter, tpath);
1323 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1324 if (fd_n == fd) found = TRUE;
1327 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1333 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1337 fd = vf_index_get_data(vf, row);
1338 return vflist_row_is_selected(vf, fd);
1341 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1343 GtkTreeModel *store;
1344 GtkTreeSelection *selection;
1348 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1349 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1359 GtkTreePath *tpath = work->data;
1363 gtk_tree_model_get_iter(store, &iter, tpath);
1364 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1373 count = g_list_length(slist);
1374 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1380 GList *vflist_selection_get_list(ViewFile *vf)
1382 GtkTreeModel *store;
1383 GtkTreeSelection *selection;
1388 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1389 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1393 GtkTreePath *tpath = work->data;
1397 gtk_tree_model_get_iter(store, &iter, tpath);
1398 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1400 list = g_list_prepend(list, file_data_ref(fd));
1402 if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
1404 /* unexpanded - add whole group */
1405 GList *work2 = fd->sidecar_files;
1408 FileData *sfd = work2->data;
1409 list = g_list_prepend(list, file_data_ref(sfd));
1410 work2 = work2->next;
1416 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1419 return g_list_reverse(list);
1422 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1424 GtkTreeModel *store;
1425 GtkTreeSelection *selection;
1430 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1431 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1435 GtkTreePath *tpath = work->data;
1439 gtk_tree_model_get_iter(store, &iter, tpath);
1440 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1442 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1446 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1449 return g_list_reverse(list);
1452 void vflist_select_all(ViewFile *vf)
1454 GtkTreeSelection *selection;
1456 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1457 gtk_tree_selection_select_all(selection);
1459 VFLIST(vf)->select_fd = NULL;
1462 void vflist_select_none(ViewFile *vf)
1464 GtkTreeSelection *selection;
1466 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1467 gtk_tree_selection_unselect_all(selection);
1470 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1475 tpath = gtk_tree_model_get_path(store, iter);
1476 result = gtk_tree_path_prev(tpath);
1478 gtk_tree_model_get_iter(store, iter, tpath);
1480 gtk_tree_path_free(tpath);
1485 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1487 if (!gtk_tree_model_get_iter_first(store, iter))
1492 GtkTreeIter next = *iter;
1494 if (gtk_tree_model_iter_next(store, &next))
1503 void vflist_select_invert(ViewFile *vf)
1506 GtkTreeSelection *selection;
1507 GtkTreeModel *store;
1510 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1511 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1513 /* Backward iteration prevents scrolling to the end of the list,
1514 * it scrolls to the first selected row instead. */
1515 valid = tree_model_get_iter_last(store, &iter);
1519 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1522 gtk_tree_selection_unselect_iter(selection, &iter);
1524 gtk_tree_selection_select_iter(selection, &iter);
1526 valid = tree_model_iter_prev(store, &iter);
1530 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1534 if (vflist_find_row(vf, fd, &iter) < 0) return;
1536 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1538 if (!vflist_row_is_selected(vf, fd))
1540 GtkTreeSelection *selection;
1541 GtkTreeModel *store;
1544 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1545 gtk_tree_selection_unselect_all(selection);
1546 gtk_tree_selection_select_iter(selection, &iter);
1547 vflist_move_cursor(vf, &iter);
1549 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1550 tpath = gtk_tree_model_get_path(store, &iter);
1551 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1552 gtk_tree_path_free(tpath);
1556 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1559 FileData *fd = NULL;
1561 if (sel_fd->parent) sel_fd = sel_fd->parent;
1570 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1572 if (match >= 0) break;
1575 if (fd) vflist_select_by_fd(vf, fd);
1579 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1581 GtkTreeModel *store;
1583 GtkTreeSelection *selection;
1587 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1589 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1590 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1592 valid = gtk_tree_model_get_iter_first(store, &iter);
1596 gboolean mark_val, selected;
1597 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1599 mark_val = file_data_get_mark(fd, n);
1600 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1604 case MTS_MODE_SET: selected = mark_val;
1606 case MTS_MODE_OR: selected = mark_val || selected;
1608 case MTS_MODE_AND: selected = mark_val && selected;
1610 case MTS_MODE_MINUS: selected = !mark_val && selected;
1615 gtk_tree_selection_select_iter(selection, &iter);
1617 gtk_tree_selection_unselect_iter(selection, &iter);
1619 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1623 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1625 GtkTreeModel *store;
1626 GtkTreeSelection *selection;
1631 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1633 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1634 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1638 GtkTreePath *tpath = work->data;
1642 gtk_tree_model_get_iter(store, &iter, tpath);
1643 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1645 /* the change has a very limited range and the standard notification would trigger
1646 complete re-read of the directory - try to do only minimal update instead */
1647 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1651 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1653 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1655 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1659 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1661 vf_refresh_idle(vf);
1665 /* mark functions can have various side effects - update all columns to be sure */
1666 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1667 /* mark functions can change sidecars too */
1668 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1672 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1676 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1681 *-----------------------------------------------------------------------------
1683 *-----------------------------------------------------------------------------
1686 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
1688 GtkTreeViewColumn *column;
1689 GtkCellRenderer *cell;
1692 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1693 if (!column) return;
1695 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1697 list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1702 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1703 gtk_tree_view_column_set_visible(column, thumb);
1705 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1706 if (!column) return;
1707 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1709 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1710 if (!column) return;
1711 gtk_tree_view_column_set_visible(column, !multiline);
1713 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1714 if (!column) return;
1715 gtk_tree_view_column_set_visible(column, !multiline);
1718 static gboolean vflist_is_multiline(ViewFile *vf)
1720 return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
1724 static void vflist_populate_view(ViewFile *vf, gboolean force)
1726 GtkTreeStore *store;
1729 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1735 vflist_store_clear(vf);
1740 vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
1742 selected = vflist_selection_get_list(vf);
1744 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force);
1746 if (selected && vflist_selection_count(vf, NULL) == 0)
1748 /* all selected files disappeared */
1749 vflist_select_closest(vf, selected->data);
1752 filelist_free(selected);
1755 vf_thumb_update(vf);
1758 gboolean vflist_refresh(ViewFile *vf)
1761 gboolean ret = TRUE;
1763 old_list = vf->list;
1766 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1769 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1771 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1772 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1773 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1775 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1776 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1779 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1781 vflist_populate_view(vf, FALSE);
1783 filelist_free(old_list);
1784 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1791 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1793 #define CELL_HEIGHT_OVERRIDE 512
1795 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1799 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1800 if (spec && G_IS_PARAM_SPEC_INT(spec))
1802 GParamSpecInt *spec_int;
1804 spec_int = G_PARAM_SPEC_INT(spec);
1805 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1809 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1811 static GdkColor color;
1812 static GtkWidget *done = NULL;
1818 style = gtk_widget_get_style(widget);
1819 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1820 shift_color(&color, -1, 0);
1827 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1828 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1830 ViewFile *vf = data;
1833 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1834 g_object_set(G_OBJECT(cell),
1835 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1836 "cell-background-set", set, NULL);
1839 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1841 GtkTreeViewColumn *column;
1842 GtkCellRenderer *renderer;
1844 column = gtk_tree_view_column_new();
1845 gtk_tree_view_column_set_title(column, title);
1846 gtk_tree_view_column_set_min_width(column, 4);
1850 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1851 renderer = gtk_cell_renderer_text_new();
1854 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1856 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1857 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1859 gtk_tree_view_column_set_expand(column, TRUE);
1863 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1864 renderer = gtk_cell_renderer_pixbuf_new();
1865 cell_renderer_height_override(renderer);
1866 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1867 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1870 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1871 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1872 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1874 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1877 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1879 ViewFile *vf = data;
1880 GtkTreeStore *store;
1881 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1887 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1888 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1891 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1893 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1895 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1898 /* the change has a very limited range and the standard notification would trigger
1899 complete re-read of the directory - try to do only minimal update instead */
1900 file_data_unregister_notify_func(vf_notify_cb, vf);
1901 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1902 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1904 vf_refresh_idle(vf);
1908 /* mark functions can have various side effects - update all columns to be sure */
1909 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1910 /* mark functions can change sidecars too */
1911 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1913 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1915 gtk_tree_path_free(path);
1918 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1920 GtkTreeViewColumn *column;
1921 GtkCellRenderer *renderer;
1922 GtkTreeStore *store;
1925 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1927 renderer = gtk_cell_renderer_toggle_new();
1928 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1930 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1931 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1932 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1934 index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1935 gtk_tree_view_column_set_fixed_width(column, 22);
1936 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1939 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1943 *-----------------------------------------------------------------------------
1945 *-----------------------------------------------------------------------------
1948 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1951 if (!dir_fd) return FALSE;
1952 if (vf->dir_fd == dir_fd) return TRUE;
1954 file_data_unref(vf->dir_fd);
1955 vf->dir_fd = file_data_ref(dir_fd);
1957 /* force complete reload */
1958 vflist_store_clear(vf);
1960 filelist_free(vf->list);
1963 ret = vf_refresh(vf);
1964 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
1968 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1970 ViewFile *vf = data;
1972 file_data_unregister_notify_func(vf_notify_cb, vf);
1974 vflist_select_idle_cancel(vf);
1975 vf_refresh_idle_cancel(vf);
1978 filelist_free(vf->list);
1981 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1983 GtkTreeStore *store;
1984 GtkTreeSelection *selection;
1985 GType flist_types[FILE_COLUMN_COUNT];
1989 vf->info = g_new0(ViewFileInfoList, 1);
1991 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1992 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1993 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1994 flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING;
1995 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1996 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
1997 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1998 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1999 flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN;
2000 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
2001 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
2002 flist_types[i] = G_TYPE_BOOLEAN;
2004 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
2006 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2007 g_object_unref(store);
2009 g_signal_connect(G_OBJECT(vf->listview), "row-expanded",
2010 G_CALLBACK(vflist_expand_cb), vf);
2012 g_signal_connect(G_OBJECT(vf->listview), "row-collapsed",
2013 G_CALLBACK(vflist_collapse_cb), vf);
2015 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2016 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
2017 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
2019 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2020 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2024 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2026 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2027 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
2031 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2032 g_assert(column == FILE_VIEW_COLUMN_THUMB);
2035 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE);
2036 g_assert(column == FILE_VIEW_COLUMN_FORMATTED);
2039 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2040 g_assert(column == FILE_VIEW_COLUMN_SIZE);
2043 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2044 g_assert(column == FILE_VIEW_COLUMN_DATE);
2047 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2051 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2053 if (VFLIST(vf)->thumbs_enabled == enable) return;
2055 VFLIST(vf)->thumbs_enabled = enable;
2057 /* vflist_populate_view is better than vf_refresh:
2058 - no need to re-read the directory
2059 - force update because the formatted string has changed
2063 vflist_populate_view(vf, TRUE);
2064 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
2068 void vflist_marks_set(ViewFile *vf, gboolean enable)
2070 GList *columns, *work;
2072 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2077 GtkTreeViewColumn *column = work->data;
2078 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2081 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2082 gtk_tree_view_column_set_visible(column, enable);
2085 g_list_free(columns);
2089 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */