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);
189 /* it seems that gtk_tree_store_clear may call some callbacks
190 that use the column. Set the pointer to NULL to be safe. */
191 gtk_tree_store_set(GTK_TREE_STORE(model), iter, FILE_COLUMN_POINTER, NULL, -1);
196 static void vflist_store_clear(ViewFile *vf)
199 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
200 gtk_tree_model_foreach(store, vflist_store_clear_cb, NULL);
201 gtk_tree_store_clear(GTK_TREE_STORE(store));
204 void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set)
209 if (vflist_find_row(vf, fd, &iter) < 0) return;
210 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
211 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
214 static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
219 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
221 tpath = gtk_tree_model_get_path(store, iter);
222 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
223 gtk_tree_path_free(tpath);
227 static gint vflist_column_idx(ViewFile *vf, gint store_idx)
229 GList *columns, *work;
232 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
236 GtkTreeViewColumn *column = work->data;
237 if (store_idx == GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx")))
243 g_list_free(columns);
249 *-----------------------------------------------------------------------------
251 *-----------------------------------------------------------------------------
254 static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
255 GtkSelectionData *selection_data, guint info,
256 guint time, gpointer data)
261 if (!VFLIST(vf)->click_fd) return;
263 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
265 list = vf_selection_get_list(vf);
269 list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
273 uri_selection_data_set_uris_from_filelist(selection_data, list);
277 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
281 vflist_color_set(vf, VFLIST(vf)->click_fd, TRUE);
283 if (VFLIST(vf)->thumbs_enabled &&
284 VFLIST(vf)->click_fd && VFLIST(vf)->click_fd->thumb_pixbuf)
288 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
289 items = vf_selection_count(vf, NULL);
293 dnd_set_drag_icon(widget, context, VFLIST(vf)->click_fd->thumb_pixbuf, items);
297 static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
301 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
303 if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE)
309 static void vflist_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context,
310 int x, int y, GtkSelectionData *selection,
311 guint info, guint time, gpointer data)
315 if (info == TARGET_TEXT_PLAIN) {
316 FileData *fd = vflist_find_data_by_coord(vf, x, y, NULL);
319 /* Add keywords to file */
320 gchar *str = gtk_selection_data_get_text(selection);
321 GList *kw_list = string_to_keywords_list(str);
323 metadata_append_list(fd, KEYWORD_KEY, kw_list);
324 string_list_free(kw_list);
327 file notification should handle this automatically
328 if (vf->layout && vf->layout->bar_info) {
329 bar_set_fd(vf->layout->bar_info, fd);
336 void vflist_dnd_init(ViewFile *vf)
338 gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
339 dnd_file_drag_types, dnd_file_drag_types_count,
340 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
341 gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL,
342 dnd_file_drag_types, dnd_file_drag_types_count,
343 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
345 g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
346 G_CALLBACK(vflist_dnd_get), vf);
347 g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
348 G_CALLBACK(vflist_dnd_begin), vf);
349 g_signal_connect(G_OBJECT(vf->listview), "drag_end",
350 G_CALLBACK(vflist_dnd_end), vf);
351 g_signal_connect(G_OBJECT(vf->listview), "drag_data_received",
352 G_CALLBACK(vflist_drag_data_received), vf);
356 *-----------------------------------------------------------------------------
358 *-----------------------------------------------------------------------------
361 GList *vflist_selection_get_one(ViewFile *vf, FileData *fd)
363 GList *list = g_list_append(NULL, file_data_ref(fd));
365 if (fd->sidecar_files)
367 /* check if the row is expanded */
371 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
372 if (vflist_find_row(vf, fd, &iter) >= 0)
376 tpath = gtk_tree_model_get_path(store, &iter);
377 if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
379 /* unexpanded - add whole group */
380 GList *work = fd->sidecar_files;
383 FileData *sfd = work->data;
384 list = g_list_prepend(list, file_data_ref(sfd));
388 gtk_tree_path_free(tpath);
390 list = g_list_reverse(list);
396 GList *vflist_pop_menu_file_list(ViewFile *vf)
398 if (!VFLIST(vf)->click_fd) return NULL;
400 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
402 return vf_selection_get_list(vf);
404 return vflist_selection_get_one(vf, VFLIST(vf)->click_fd);
408 void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
412 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
416 list = vf_selection_get_list(vf);
417 view_window_new_from_list(list);
422 view_window_new(VFLIST(vf)->click_fd);
426 void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
431 list = vf_pop_menu_file_list(vf);
432 if (options->file_ops.enable_in_place_rename &&
433 list && !list->next && VFLIST(vf)->click_fd)
440 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
441 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
445 tpath = gtk_tree_model_get_path(store, &iter);
446 tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
447 FILE_VIEW_COLUMN_FORMATTED, VFLIST(vf)->click_fd->name,
448 vflist_row_rename_cb, vf);
449 gtk_tree_path_free(tpath);
454 file_util_rename(NULL, list, vf->listview);
457 void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
461 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
464 layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
468 vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled);
472 void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
476 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
478 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
481 void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
484 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
485 VFLIST(vf)->click_fd = NULL;
491 *-----------------------------------------------------------------------------
493 *-----------------------------------------------------------------------------
496 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
501 if (!new || !new[0]) return FALSE;
503 new_path = g_build_filename(vf->dir_fd->path, new, NULL);
505 if (strchr(new, G_DIR_SEPARATOR) != NULL)
507 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
508 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
513 gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL);
514 FileData *fd = file_data_new_group(old_path); /* get the fd from cache */
515 file_util_rename_simple(fd, new_path, vf->listview);
525 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
533 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return;
534 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
535 tpath = gtk_tree_model_get_path(store, &iter);
536 tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
537 gtk_tree_path_free(tpath);
539 popup_menu_position_clamp(menu, x, y, 0);
542 gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
547 if (event->keyval != GDK_KEY_Menu) return FALSE;
549 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
555 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
556 gtk_tree_model_get_iter(store, &iter, tpath);
557 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
558 gtk_tree_path_free(tpath);
562 VFLIST(vf)->click_fd = NULL;
565 vf->popup = vf_pop_menu(vf);
566 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
571 gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
577 GtkTreeViewColumn *column;
579 vf->clicked_mark = 0;
581 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
582 &tpath, &column, NULL, NULL))
585 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
587 if (bevent->button == MOUSE_BUTTON_LEFT &&
588 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
591 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
592 vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
594 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
596 gtk_tree_model_get_iter(store, &iter, tpath);
597 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
599 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE);
601 gtk_tree_path_free(tpath);
604 VFLIST(vf)->click_fd = fd;
606 if (bevent->button == MOUSE_BUTTON_RIGHT)
608 vf->popup = vf_pop_menu(vf);
609 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
610 bevent->button, bevent->time);
614 if (!fd) return FALSE;
616 if (bevent->button == MOUSE_BUTTON_MIDDLE)
618 if (!vflist_row_is_selected(vf, fd))
620 vflist_color_set(vf, fd, TRUE);
626 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
627 !(bevent->state & GDK_SHIFT_MASK ) &&
628 !(bevent->state & GDK_CONTROL_MASK ) &&
629 vflist_row_is_selected(vf, fd))
631 GtkTreeSelection *selection;
633 gtk_widget_grab_focus(widget);
636 /* returning FALSE and further processing of the event is needed for
637 correct operation of the expander, to show the sidecar files.
638 It however resets the selection of multiple files. With this condition
639 it should work for both cases */
640 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
641 return (gtk_tree_selection_count_selected_rows(selection) > 1);
645 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
647 if (vf->layout) layout_image_full_screen_start(vf->layout);
654 gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
661 if (bevent->button == MOUSE_BUTTON_MIDDLE)
663 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
666 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
671 if ((bevent->x != 0 || bevent->y != 0) &&
672 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
673 &tpath, NULL, NULL, NULL))
677 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
678 gtk_tree_model_get_iter(store, &iter, tpath);
679 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
680 gtk_tree_path_free(tpath);
683 if (bevent->button == MOUSE_BUTTON_MIDDLE)
685 if (fd && VFLIST(vf)->click_fd == fd)
687 GtkTreeSelection *selection;
689 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
690 if (vflist_row_is_selected(vf, fd))
692 gtk_tree_selection_unselect_iter(selection, &iter);
696 gtk_tree_selection_select_iter(selection, &iter);
702 if (fd && VFLIST(vf)->click_fd == fd &&
703 !(bevent->state & GDK_SHIFT_MASK ) &&
704 !(bevent->state & GDK_CONTROL_MASK ) &&
705 vflist_row_is_selected(vf, fd))
707 GtkTreeSelection *selection;
709 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
710 gtk_tree_selection_unselect_all(selection);
711 gtk_tree_selection_select_iter(selection, &iter);
712 vflist_move_cursor(vf, &iter);
713 // return TRUE;// FIXME - expand
719 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
721 FileData *read_ahead_fd = NULL;
727 cur_fd = layout_image_get_fd(vf->layout);
728 if (sel_fd == cur_fd) return; /* no change */
730 row = g_list_index(vf->list, sel_fd);
731 // FIXME sidecar data
733 if (sel_fd && options->image.enable_read_ahead && row >= 0)
735 if (row > g_list_index(vf->list, cur_fd) &&
736 (guint) (row + 1) < vf_count(vf, NULL))
738 read_ahead_fd = vf_index_get_data(vf, row + 1);
742 read_ahead_fd = vf_index_get_data(vf, row - 1);
746 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
749 static gboolean vflist_select_idle_cb(gpointer data)
755 VFLIST(vf)->select_idle_id = 0;
761 if (VFLIST(vf)->select_fd)
763 vflist_select_image(vf, VFLIST(vf)->select_fd);
764 VFLIST(vf)->select_fd = NULL;
767 VFLIST(vf)->select_idle_id = 0;
771 static void vflist_select_idle_cancel(ViewFile *vf)
773 if (VFLIST(vf)->select_idle_id)
775 g_source_remove(VFLIST(vf)->select_idle_id);
776 VFLIST(vf)->select_idle_id = 0;
780 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
781 gboolean path_currently_selected, gpointer data)
786 if (!path_currently_selected &&
787 gtk_tree_model_get_iter(store, &iter, tpath))
789 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
793 VFLIST(vf)->select_fd = NULL;
797 !VFLIST(vf)->select_idle_id)
799 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
805 static void vflist_expand_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
808 vflist_set_expanded(vf, iter, TRUE);
811 static void vflist_collapse_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
814 vflist_set_expanded(vf, iter, FALSE);
818 *-----------------------------------------------------------------------------
820 *-----------------------------------------------------------------------------
824 static gboolean vflist_dummy_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
825 gboolean path_currently_selected, gpointer data)
831 static gchar* vflist_get_formatted(ViewFile *vf, const gchar *name, const gchar *sidecars, const gchar *size, const gchar *time, gboolean expanded)
833 gboolean multiline = vflist_is_multiline(vf);
838 text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time);
842 text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars);
847 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded)
856 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
858 gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
859 FILE_COLUMN_NAME, &name,
860 FILE_COLUMN_SIDECARS, &sidecars,
861 FILE_COLUMN_SIZE, &size,
862 FILE_COLUMN_DATE, &time,
864 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
866 gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED, formatted,
867 FILE_COLUMN_EXPANDED, expanded,
876 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
879 gchar *sidecars = NULL;
881 const gchar *time = text_from_time(fd->date);
882 gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
883 const gchar *disabled_grouping;
885 gboolean expanded = FALSE;
887 if (fd->sidecar_files) /* expanded has no effect on files without sidecars */
889 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_EXPANDED, &expanded, -1);
892 sidecars = file_data_sc_list_to_string(fd);
894 disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
895 name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
896 size = text_from_size(fd->size);
898 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
900 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
901 FILE_COLUMN_VERSION, fd->version,
902 FILE_COLUMN_THUMB, fd->thumb_pixbuf,
903 FILE_COLUMN_FORMATTED, formatted,
904 FILE_COLUMN_SIDECARS, sidecars,
905 FILE_COLUMN_NAME, name,
906 FILE_COLUMN_SIZE, size,
907 FILE_COLUMN_DATE, time,
908 #define STORE_SET_IS_SLOW 1
909 #if STORE_SET_IS_SLOW
910 /* this is 3x faster on a directory with 20000 files */
911 FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
912 FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
913 FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
914 FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
915 FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
916 FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
917 #if FILEDATA_MARKS_SIZE != 6
918 #error this needs to be updated
921 FILE_COLUMN_COLOR, FALSE, -1);
923 #if !STORE_SET_IS_SLOW
926 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
927 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
936 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected, gboolean force)
941 gint num_ordered = 0;
942 gint num_prepended = 0;
944 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
950 FileData *fd = work->data;
951 gboolean done = FALSE;
955 FileData *old_fd = NULL;
956 gint old_version = 0;
960 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
961 FILE_COLUMN_POINTER, &old_fd,
962 FILE_COLUMN_VERSION, &old_version,
972 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
974 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
976 if (match == 0) g_warning("multiple fd for the same path");
992 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
997 here should be used gtk_tree_store_append, but this function seems to be O(n)
998 and it seems to be much faster to add new entries to the beginning and reorder later
1001 gtk_tree_store_prepend(store, &new, parent_iter);
1004 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
1005 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected, force);
1007 if (g_list_find(selected, fd))
1009 /* renamed files - the same fd appears at different position - select it again*/
1010 GtkTreeSelection *selection;
1011 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1012 gtk_tree_selection_select_iter(selection, &new);
1019 file_data_unref(old_fd);
1020 valid = gtk_tree_store_remove(store, &iter);
1025 if (fd->version != old_version || force)
1027 vflist_setup_iter(vf, store, &iter, fd);
1028 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected, force);
1031 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1042 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1043 file_data_unref(old_fd);
1045 valid = gtk_tree_store_remove(store, &iter);
1048 /* move the prepended entries to the correct position */
1052 gint num_total = num_prepended + num_ordered;
1053 gint *new_order = g_malloc(num_total * sizeof(gint));
1055 for (i = 0; i < num_total; i++)
1057 if (i < num_ordered)
1058 new_order[i] = num_prepended + i;
1060 new_order[i] = num_total - 1 - i;
1062 gtk_tree_store_reorder(store, parent_iter, new_order);
1068 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
1071 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
1073 GtkTreeStore *store;
1076 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1077 if (!vf->list) return;
1083 FileData *fd = work->data;
1084 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1089 vf->sort_method = type;
1090 vf->sort_ascend = ascend;
1092 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1094 new_order = g_malloc(i * sizeof(gint));
1100 FileData *fd = work->data;
1101 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1106 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1107 gtk_tree_store_reorder(store, NULL, new_order);
1110 g_hash_table_destroy(fd_idx_hash);
1114 *-----------------------------------------------------------------------------
1116 *-----------------------------------------------------------------------------
1120 void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1125 FileData *fd = work->data;
1128 if (fd->thumb_pixbuf) (*done)++;
1130 if (fd->sidecar_files)
1132 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1138 void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
1140 GtkTreeStore *store;
1143 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1145 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1146 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1149 FileData *vflist_thumb_next_fd(ViewFile *vf)
1152 FileData *fd = NULL;
1154 /* first check the visible files */
1156 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1158 GtkTreeModel *store;
1160 gboolean valid = TRUE;
1162 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1163 gtk_tree_model_get_iter(store, &iter, tpath);
1164 gtk_tree_path_free(tpath);
1166 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1170 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1172 if (!nfd->thumb_pixbuf) fd = nfd;
1174 valid = gtk_tree_model_iter_next(store, &iter);
1178 /* then find first undone */
1182 GList *work = vf->list;
1185 FileData *fd_p = work->data;
1186 if (!fd_p->thumb_pixbuf)
1190 GList *work2 = fd_p->sidecar_files;
1192 while (work2 && !fd)
1195 if (!fd_p->thumb_pixbuf) fd = fd_p;
1196 work2 = work2->next;
1207 void vflist_thumb_reset_all(ViewFile *vf)
1209 GList *work = vf->list;
1212 FileData *fd = work->data;
1213 if (fd->thumb_pixbuf)
1215 g_object_unref(fd->thumb_pixbuf);
1216 fd->thumb_pixbuf = NULL;
1223 *-----------------------------------------------------------------------------
1225 *-----------------------------------------------------------------------------
1228 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1230 return g_list_nth_data(vf->list, row);
1233 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1236 GList *work, *work2;
1241 FileData *list_fd = work->data;
1242 if (list_fd == fd) return p;
1244 work2 = list_fd->sidecar_files;
1247 /* FIXME: return the same index also for sidecars
1248 it is sufficient for next/prev navigation but it should be rewritten
1249 without using indexes at all
1251 FileData *sidecar_fd = work2->data;
1252 if (sidecar_fd == fd) return p;
1253 work2 = work2->next;
1263 guint vflist_count(ViewFile *vf, gint64 *bytes)
1273 FileData *fd = work->data;
1281 return g_list_length(vf->list);
1284 GList *vflist_get_list(ViewFile *vf)
1292 FileData *fd = work->data;
1295 list = g_list_prepend(list, file_data_ref(fd));
1298 return g_list_reverse(list);
1302 *-----------------------------------------------------------------------------
1304 *-----------------------------------------------------------------------------
1307 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1309 GtkTreeModel *store;
1310 GtkTreeSelection *selection;
1313 gboolean found = FALSE;
1315 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1316 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1318 while (!found && work)
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_n, -1);
1326 if (fd_n == fd) found = TRUE;
1329 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1335 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1339 fd = vf_index_get_data(vf, row);
1340 return vflist_row_is_selected(vf, fd);
1343 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1345 GtkTreeModel *store;
1346 GtkTreeSelection *selection;
1350 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1351 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1361 GtkTreePath *tpath = work->data;
1365 gtk_tree_model_get_iter(store, &iter, tpath);
1366 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1375 count = g_list_length(slist);
1376 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1382 GList *vflist_selection_get_list(ViewFile *vf)
1384 GtkTreeModel *store;
1385 GtkTreeSelection *selection;
1390 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1391 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1395 GtkTreePath *tpath = work->data;
1399 gtk_tree_model_get_iter(store, &iter, tpath);
1400 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1402 list = g_list_prepend(list, file_data_ref(fd));
1404 if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
1406 /* unexpanded - add whole group */
1407 GList *work2 = fd->sidecar_files;
1410 FileData *sfd = work2->data;
1411 list = g_list_prepend(list, file_data_ref(sfd));
1412 work2 = work2->next;
1418 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1421 return g_list_reverse(list);
1424 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1426 GtkTreeModel *store;
1427 GtkTreeSelection *selection;
1432 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1433 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1437 GtkTreePath *tpath = work->data;
1441 gtk_tree_model_get_iter(store, &iter, tpath);
1442 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1444 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1448 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1451 return g_list_reverse(list);
1454 void vflist_select_all(ViewFile *vf)
1456 GtkTreeSelection *selection;
1458 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1459 gtk_tree_selection_select_all(selection);
1461 VFLIST(vf)->select_fd = NULL;
1464 void vflist_select_none(ViewFile *vf)
1466 GtkTreeSelection *selection;
1468 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1469 gtk_tree_selection_unselect_all(selection);
1472 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1477 tpath = gtk_tree_model_get_path(store, iter);
1478 result = gtk_tree_path_prev(tpath);
1480 gtk_tree_model_get_iter(store, iter, tpath);
1482 gtk_tree_path_free(tpath);
1487 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1489 if (!gtk_tree_model_get_iter_first(store, iter))
1494 GtkTreeIter next = *iter;
1496 if (gtk_tree_model_iter_next(store, &next))
1505 void vflist_select_invert(ViewFile *vf)
1508 GtkTreeSelection *selection;
1509 GtkTreeModel *store;
1512 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1513 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1515 /* Backward iteration prevents scrolling to the end of the list,
1516 * it scrolls to the first selected row instead. */
1517 valid = tree_model_get_iter_last(store, &iter);
1521 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1524 gtk_tree_selection_unselect_iter(selection, &iter);
1526 gtk_tree_selection_select_iter(selection, &iter);
1528 valid = tree_model_iter_prev(store, &iter);
1532 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1536 if (vflist_find_row(vf, fd, &iter) < 0) return;
1538 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1540 if (!vflist_row_is_selected(vf, fd))
1542 GtkTreeSelection *selection;
1543 GtkTreeModel *store;
1546 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1547 gtk_tree_selection_unselect_all(selection);
1548 gtk_tree_selection_select_iter(selection, &iter);
1549 vflist_move_cursor(vf, &iter);
1551 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1552 tpath = gtk_tree_model_get_path(store, &iter);
1553 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1554 gtk_tree_path_free(tpath);
1558 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1561 FileData *fd = NULL;
1563 if (sel_fd->parent) sel_fd = sel_fd->parent;
1572 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1574 if (match >= 0) break;
1577 if (fd) vflist_select_by_fd(vf, fd);
1581 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1583 GtkTreeModel *store;
1585 GtkTreeSelection *selection;
1589 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1591 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1592 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1594 valid = gtk_tree_model_get_iter_first(store, &iter);
1598 gboolean mark_val, selected;
1599 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1601 mark_val = file_data_get_mark(fd, n);
1602 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1606 case MTS_MODE_SET: selected = mark_val;
1608 case MTS_MODE_OR: selected = mark_val || selected;
1610 case MTS_MODE_AND: selected = mark_val && selected;
1612 case MTS_MODE_MINUS: selected = !mark_val && selected;
1617 gtk_tree_selection_select_iter(selection, &iter);
1619 gtk_tree_selection_unselect_iter(selection, &iter);
1621 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1625 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1627 GtkTreeModel *store;
1628 GtkTreeSelection *selection;
1633 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1635 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1636 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1640 GtkTreePath *tpath = work->data;
1644 gtk_tree_model_get_iter(store, &iter, tpath);
1645 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1647 /* the change has a very limited range and the standard notification would trigger
1648 complete re-read of the directory - try to do only minimal update instead */
1649 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1653 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1655 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1657 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1661 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1663 vf_refresh_idle(vf);
1667 /* mark functions can have various side effects - update all columns to be sure */
1668 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1669 /* mark functions can change sidecars too */
1670 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1674 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1678 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1683 *-----------------------------------------------------------------------------
1685 *-----------------------------------------------------------------------------
1688 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
1690 GtkTreeViewColumn *column;
1691 GtkCellRenderer *cell;
1694 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1695 if (!column) return;
1697 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1699 list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1704 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1705 gtk_tree_view_column_set_visible(column, thumb);
1707 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1708 if (!column) return;
1709 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1711 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1712 if (!column) return;
1713 gtk_tree_view_column_set_visible(column, !multiline);
1715 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1716 if (!column) return;
1717 gtk_tree_view_column_set_visible(column, !multiline);
1720 static gboolean vflist_is_multiline(ViewFile *vf)
1722 return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
1726 static void vflist_populate_view(ViewFile *vf, gboolean force)
1728 GtkTreeStore *store;
1731 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1737 vflist_store_clear(vf);
1742 vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
1744 selected = vflist_selection_get_list(vf);
1746 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force);
1748 if (selected && vflist_selection_count(vf, NULL) == 0)
1750 /* all selected files disappeared */
1751 vflist_select_closest(vf, selected->data);
1754 filelist_free(selected);
1757 vf_thumb_update(vf);
1760 gboolean vflist_refresh(ViewFile *vf)
1763 gboolean ret = TRUE;
1765 old_list = vf->list;
1768 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1771 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1773 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1774 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1775 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1777 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1778 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1781 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1783 vflist_populate_view(vf, FALSE);
1785 filelist_free(old_list);
1786 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1793 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1795 #define CELL_HEIGHT_OVERRIDE 512
1797 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1801 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1802 if (spec && G_IS_PARAM_SPEC_INT(spec))
1804 GParamSpecInt *spec_int;
1806 spec_int = G_PARAM_SPEC_INT(spec);
1807 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1811 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1813 static GdkColor color;
1814 static GtkWidget *done = NULL;
1820 style = gtk_widget_get_style(widget);
1821 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1822 shift_color(&color, -1, 0);
1829 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1830 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1832 ViewFile *vf = data;
1835 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1836 g_object_set(G_OBJECT(cell),
1837 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1838 "cell-background-set", set, NULL);
1841 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1843 GtkTreeViewColumn *column;
1844 GtkCellRenderer *renderer;
1846 column = gtk_tree_view_column_new();
1847 gtk_tree_view_column_set_title(column, title);
1848 gtk_tree_view_column_set_min_width(column, 4);
1852 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1853 renderer = gtk_cell_renderer_text_new();
1856 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1858 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1859 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1861 gtk_tree_view_column_set_expand(column, TRUE);
1865 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1866 renderer = gtk_cell_renderer_pixbuf_new();
1867 cell_renderer_height_override(renderer);
1868 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1869 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1872 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1873 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1874 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1876 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1879 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1881 ViewFile *vf = data;
1882 GtkTreeStore *store;
1883 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1889 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1890 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1893 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1895 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1897 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1900 /* the change has a very limited range and the standard notification would trigger
1901 complete re-read of the directory - try to do only minimal update instead */
1902 file_data_unregister_notify_func(vf_notify_cb, vf);
1903 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1904 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1906 vf_refresh_idle(vf);
1910 /* mark functions can have various side effects - update all columns to be sure */
1911 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1912 /* mark functions can change sidecars too */
1913 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1915 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1917 gtk_tree_path_free(path);
1920 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1922 GtkTreeViewColumn *column;
1923 GtkCellRenderer *renderer;
1924 GtkTreeStore *store;
1927 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1929 renderer = gtk_cell_renderer_toggle_new();
1930 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1932 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1933 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1934 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1936 index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1937 gtk_tree_view_column_set_fixed_width(column, 22);
1938 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1941 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1945 *-----------------------------------------------------------------------------
1947 *-----------------------------------------------------------------------------
1950 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1953 if (!dir_fd) return FALSE;
1954 if (vf->dir_fd == dir_fd) return TRUE;
1956 file_data_unref(vf->dir_fd);
1957 vf->dir_fd = file_data_ref(dir_fd);
1959 /* force complete reload */
1960 vflist_store_clear(vf);
1962 filelist_free(vf->list);
1965 ret = vf_refresh(vf);
1966 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
1970 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1972 ViewFile *vf = data;
1974 file_data_unregister_notify_func(vf_notify_cb, vf);
1976 vflist_select_idle_cancel(vf);
1977 vf_refresh_idle_cancel(vf);
1980 filelist_free(vf->list);
1983 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1985 GtkTreeStore *store;
1986 GtkTreeSelection *selection;
1987 GType flist_types[FILE_COLUMN_COUNT];
1991 vf->info = g_new0(ViewFileInfoList, 1);
1993 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1994 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1995 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1996 flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING;
1997 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1998 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
1999 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
2000 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
2001 flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN;
2002 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
2003 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
2004 flist_types[i] = G_TYPE_BOOLEAN;
2006 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
2008 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2009 g_object_unref(store);
2011 g_signal_connect(G_OBJECT(vf->listview), "row-expanded",
2012 G_CALLBACK(vflist_expand_cb), vf);
2014 g_signal_connect(G_OBJECT(vf->listview), "row-collapsed",
2015 G_CALLBACK(vflist_collapse_cb), vf);
2017 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2018 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
2019 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
2021 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2022 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2026 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2028 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2029 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
2033 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2034 g_assert(column == FILE_VIEW_COLUMN_THUMB);
2037 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE);
2038 g_assert(column == FILE_VIEW_COLUMN_FORMATTED);
2041 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2042 g_assert(column == FILE_VIEW_COLUMN_SIZE);
2045 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2046 g_assert(column == FILE_VIEW_COLUMN_DATE);
2049 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2053 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2055 if (VFLIST(vf)->thumbs_enabled == enable) return;
2057 VFLIST(vf)->thumbs_enabled = enable;
2059 /* vflist_populate_view is better than vf_refresh:
2060 - no need to re-read the directory
2061 - force update because the formatted string has changed
2065 vflist_populate_view(vf, TRUE);
2066 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
2070 void vflist_marks_set(ViewFile *vf, gboolean enable)
2072 GList *columns, *work;
2074 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2079 GtkTreeViewColumn *column = work->data;
2080 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2083 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2084 gtk_tree_view_column_set_visible(column, enable);
2087 g_list_free(columns);
2091 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */