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);
139 static gboolean vflist_store_clear_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
142 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
144 /* it seems that gtk_tree_store_clear may call some callbacks
145 that use the column. Set the pointer to NULL to be safe. */
146 gtk_tree_store_set(GTK_TREE_STORE(model), iter, FILE_COLUMN_POINTER, NULL, -1);
151 static void vflist_store_clear(ViewFile *vf)
154 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
155 gtk_tree_model_foreach(store, vflist_store_clear_cb, NULL);
156 gtk_tree_store_clear(GTK_TREE_STORE(store));
159 void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set)
164 if (vflist_find_row(vf, fd, &iter) < 0) return;
165 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
166 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
169 static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
174 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
176 tpath = gtk_tree_model_get_path(store, iter);
177 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
178 gtk_tree_path_free(tpath);
183 *-----------------------------------------------------------------------------
185 *-----------------------------------------------------------------------------
188 static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
189 GtkSelectionData *selection_data, guint info,
190 guint time, gpointer data)
195 if (!VFLIST(vf)->click_fd) return;
197 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
199 list = vf_selection_get_list(vf);
203 list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
207 uri_selection_data_set_uris_from_filelist(selection_data, list);
211 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
215 vflist_color_set(vf, VFLIST(vf)->click_fd, TRUE);
217 if (VFLIST(vf)->thumbs_enabled &&
218 VFLIST(vf)->click_fd && VFLIST(vf)->click_fd->thumb_pixbuf)
222 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
223 items = vf_selection_count(vf, NULL);
227 dnd_set_drag_icon(widget, context, VFLIST(vf)->click_fd->thumb_pixbuf, items);
231 static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
235 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
237 if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE)
243 static void vflist_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context,
244 int x, int y, GtkSelectionData *selection,
245 guint info, guint time, gpointer data)
249 if (info == TARGET_TEXT_PLAIN) {
250 FileData *fd = vflist_find_data_by_coord(vf, x, y, NULL);
253 /* Add keywords to file */
254 gchar *str = gtk_selection_data_get_text(selection);
255 GList *kw_list = string_to_keywords_list(str);
257 metadata_append_list(fd, KEYWORD_KEY, kw_list);
258 string_list_free(kw_list);
264 void vflist_dnd_init(ViewFile *vf)
266 gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
267 dnd_file_drag_types, dnd_file_drag_types_count,
268 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
269 gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL,
270 dnd_file_drag_types, dnd_file_drag_types_count,
271 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
273 g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
274 G_CALLBACK(vflist_dnd_get), vf);
275 g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
276 G_CALLBACK(vflist_dnd_begin), vf);
277 g_signal_connect(G_OBJECT(vf->listview), "drag_end",
278 G_CALLBACK(vflist_dnd_end), vf);
279 g_signal_connect(G_OBJECT(vf->listview), "drag_data_received",
280 G_CALLBACK(vflist_drag_data_received), vf);
284 *-----------------------------------------------------------------------------
286 *-----------------------------------------------------------------------------
289 GList *vflist_selection_get_one(ViewFile *vf, FileData *fd)
291 GList *list = g_list_append(NULL, file_data_ref(fd));
293 if (fd->sidecar_files)
295 /* check if the row is expanded */
299 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
300 if (vflist_find_row(vf, fd, &iter) >= 0)
304 tpath = gtk_tree_model_get_path(store, &iter);
305 if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
307 /* unexpanded - add whole group */
308 GList *work = fd->sidecar_files;
311 FileData *sfd = work->data;
312 list = g_list_prepend(list, file_data_ref(sfd));
316 gtk_tree_path_free(tpath);
318 list = g_list_reverse(list);
324 GList *vflist_pop_menu_file_list(ViewFile *vf)
326 if (!VFLIST(vf)->click_fd) return NULL;
328 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
330 return vf_selection_get_list(vf);
332 return vflist_selection_get_one(vf, VFLIST(vf)->click_fd);
336 void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
340 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
344 list = vf_selection_get_list(vf);
345 view_window_new_from_list(list);
350 view_window_new(VFLIST(vf)->click_fd);
354 void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
359 list = vf_pop_menu_file_list(vf);
360 if (options->file_ops.enable_in_place_rename &&
361 list && !list->next && VFLIST(vf)->click_fd)
368 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
369 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
373 tpath = gtk_tree_model_get_path(store, &iter);
374 tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
375 FILE_VIEW_COLUMN_FORMATTED, VFLIST(vf)->click_fd->name,
376 vflist_row_rename_cb, vf);
377 gtk_tree_path_free(tpath);
382 file_util_rename(NULL, list, vf->listview);
385 void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
389 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
392 layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
396 vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled);
400 void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
404 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
406 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
409 void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
412 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
413 VFLIST(vf)->click_fd = NULL;
419 *-----------------------------------------------------------------------------
421 *-----------------------------------------------------------------------------
424 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
429 if (!new || !new[0]) return FALSE;
431 new_path = g_build_filename(vf->dir_fd->path, new, NULL);
433 if (strchr(new, G_DIR_SEPARATOR) != NULL)
435 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
436 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
441 gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL);
442 FileData *fd = file_data_new_group(old_path); /* get the fd from cache */
443 file_util_rename_simple(fd, new_path, vf->listview);
453 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
461 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return;
462 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
463 tpath = gtk_tree_model_get_path(store, &iter);
464 tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
465 gtk_tree_path_free(tpath);
467 popup_menu_position_clamp(menu, x, y, 0);
470 gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
475 if (event->keyval != GDK_KEY_Menu) return FALSE;
477 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
483 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
484 gtk_tree_model_get_iter(store, &iter, tpath);
485 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
486 gtk_tree_path_free(tpath);
490 VFLIST(vf)->click_fd = NULL;
493 vf->popup = vf_pop_menu(vf);
494 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
499 gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
505 GtkTreeViewColumn *column;
507 vf->clicked_mark = 0;
509 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
510 &tpath, &column, NULL, NULL))
513 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
515 if (bevent->button == MOUSE_BUTTON_LEFT &&
516 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
519 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
520 vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
522 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
524 gtk_tree_model_get_iter(store, &iter, tpath);
525 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
526 gtk_tree_path_free(tpath);
529 VFLIST(vf)->click_fd = fd;
531 if (bevent->button == MOUSE_BUTTON_RIGHT)
533 vf->popup = vf_pop_menu(vf);
534 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
535 bevent->button, bevent->time);
539 if (!fd) return FALSE;
541 if (bevent->button == MOUSE_BUTTON_MIDDLE)
543 if (!vflist_row_is_selected(vf, fd))
545 vflist_color_set(vf, fd, TRUE);
551 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
552 !(bevent->state & GDK_SHIFT_MASK ) &&
553 !(bevent->state & GDK_CONTROL_MASK ) &&
554 vflist_row_is_selected(vf, fd))
556 GtkTreeSelection *selection;
558 gtk_widget_grab_focus(widget);
561 /* returning FALSE and further processing of the event is needed for
562 correct operation of the expander, to show the sidecar files.
563 It however resets the selection of multiple files. With this condition
564 it should work for both cases */
565 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
566 return (gtk_tree_selection_count_selected_rows(selection) > 1);
569 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
571 if (vf->layout) layout_image_full_screen_start(vf->layout);
577 gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
584 if (bevent->button == MOUSE_BUTTON_MIDDLE)
586 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
589 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
594 if ((bevent->x != 0 || bevent->y != 0) &&
595 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
596 &tpath, NULL, NULL, NULL))
600 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
601 gtk_tree_model_get_iter(store, &iter, tpath);
602 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
603 gtk_tree_path_free(tpath);
606 if (bevent->button == MOUSE_BUTTON_MIDDLE)
608 if (fd && VFLIST(vf)->click_fd == fd)
610 GtkTreeSelection *selection;
612 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
613 if (vflist_row_is_selected(vf, fd))
615 gtk_tree_selection_unselect_iter(selection, &iter);
619 gtk_tree_selection_select_iter(selection, &iter);
625 if (fd && VFLIST(vf)->click_fd == fd &&
626 !(bevent->state & GDK_SHIFT_MASK ) &&
627 !(bevent->state & GDK_CONTROL_MASK ) &&
628 vflist_row_is_selected(vf, fd))
630 GtkTreeSelection *selection;
632 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
633 gtk_tree_selection_unselect_all(selection);
634 gtk_tree_selection_select_iter(selection, &iter);
635 vflist_move_cursor(vf, &iter);
641 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
643 FileData *read_ahead_fd = NULL;
649 cur_fd = layout_image_get_fd(vf->layout);
650 if (sel_fd == cur_fd) return; /* no change */
652 row = g_list_index(vf->list, sel_fd);
653 // FIXME sidecar data
655 if (sel_fd && options->image.enable_read_ahead && row >= 0)
657 if (row > g_list_index(vf->list, cur_fd) &&
658 (guint) (row + 1) < vf_count(vf, NULL))
660 read_ahead_fd = vf_index_get_data(vf, row + 1);
664 read_ahead_fd = vf_index_get_data(vf, row - 1);
668 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
671 static gboolean vflist_select_idle_cb(gpointer data)
677 VFLIST(vf)->select_idle_id = 0;
683 if (VFLIST(vf)->select_fd)
685 vflist_select_image(vf, VFLIST(vf)->select_fd);
686 VFLIST(vf)->select_fd = NULL;
689 VFLIST(vf)->select_idle_id = 0;
693 static void vflist_select_idle_cancel(ViewFile *vf)
695 if (VFLIST(vf)->select_idle_id)
697 g_source_remove(VFLIST(vf)->select_idle_id);
698 VFLIST(vf)->select_idle_id = 0;
702 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
703 gboolean path_currently_selected, gpointer data)
708 if (!path_currently_selected &&
709 gtk_tree_model_get_iter(store, &iter, tpath))
711 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
715 VFLIST(vf)->select_fd = NULL;
719 !VFLIST(vf)->select_idle_id)
721 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
727 static void vflist_expand_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
730 vflist_set_expanded(vf, iter, TRUE);
733 static void vflist_collapse_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
736 vflist_set_expanded(vf, iter, FALSE);
740 *-----------------------------------------------------------------------------
742 *-----------------------------------------------------------------------------
746 static gchar* vflist_get_formatted(ViewFile *vf, const gchar *name, const gchar *sidecars, const gchar *size, const gchar *time, gboolean expanded)
748 gboolean multiline = vflist_is_multiline(vf);
753 text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time);
757 text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars);
762 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded)
771 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
773 gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
774 FILE_COLUMN_NAME, &name,
775 FILE_COLUMN_SIDECARS, &sidecars,
776 FILE_COLUMN_SIZE, &size,
777 FILE_COLUMN_DATE, &time,
779 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
781 gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED, formatted,
782 FILE_COLUMN_EXPANDED, expanded,
791 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
794 gchar *sidecars = NULL;
796 const gchar *time = text_from_time(fd->date);
797 gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
798 const gchar *disabled_grouping;
800 gboolean expanded = FALSE;
802 if (fd->sidecar_files) /* expanded has no effect on files without sidecars */
804 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_EXPANDED, &expanded, -1);
807 sidecars = file_data_sc_list_to_string(fd);
809 disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
810 name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
811 size = text_from_size(fd->size);
813 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
815 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
816 FILE_COLUMN_VERSION, fd->version,
817 FILE_COLUMN_THUMB, fd->thumb_pixbuf,
818 FILE_COLUMN_FORMATTED, formatted,
819 FILE_COLUMN_SIDECARS, sidecars,
820 FILE_COLUMN_NAME, name,
821 FILE_COLUMN_SIZE, size,
822 FILE_COLUMN_DATE, time,
823 #define STORE_SET_IS_SLOW 1
824 #if STORE_SET_IS_SLOW
825 /* this is 3x faster on a directory with 20000 files */
826 FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
827 FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
828 FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
829 FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
830 FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
831 FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
832 #if FILEDATA_MARKS_SIZE != 6
833 #error this needs to be updated
836 FILE_COLUMN_COLOR, FALSE, -1);
838 #if !STORE_SET_IS_SLOW
841 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
842 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
851 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected, gboolean force)
856 gint num_ordered = 0;
857 gint num_prepended = 0;
859 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
865 FileData *fd = work->data;
866 gboolean done = FALSE;
870 FileData *old_fd = NULL;
871 gint old_version = 0;
875 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
876 FILE_COLUMN_POINTER, &old_fd,
877 FILE_COLUMN_VERSION, &old_version,
887 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
889 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
891 if (match == 0) g_warning("multiple fd for the same path");
907 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
912 here should be used gtk_tree_store_append, but this function seems to be O(n)
913 and it seems to be much faster to add new entries to the beginning and reorder later
916 gtk_tree_store_prepend(store, &new, parent_iter);
919 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
920 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected, force);
922 if (g_list_find(selected, fd))
924 /* renamed files - the same fd appears at different position - select it again*/
925 GtkTreeSelection *selection;
926 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
927 gtk_tree_selection_select_iter(selection, &new);
934 file_data_unref(old_fd);
935 valid = gtk_tree_store_remove(store, &iter);
940 if (fd->version != old_version || force)
942 vflist_setup_iter(vf, store, &iter, fd);
943 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected, force);
946 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
957 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
958 file_data_unref(old_fd);
960 valid = gtk_tree_store_remove(store, &iter);
963 /* move the prepended entries to the correct position */
967 gint num_total = num_prepended + num_ordered;
968 gint *new_order = g_malloc(num_total * sizeof(gint));
970 for (i = 0; i < num_total; i++)
973 new_order[i] = num_prepended + i;
975 new_order[i] = num_total - 1 - i;
977 gtk_tree_store_reorder(store, parent_iter, new_order);
983 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
986 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
991 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
992 if (!vf->list) return;
998 FileData *fd = work->data;
999 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1004 vf->sort_method = type;
1005 vf->sort_ascend = ascend;
1007 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1009 new_order = g_malloc(i * sizeof(gint));
1015 FileData *fd = work->data;
1016 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1021 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1022 gtk_tree_store_reorder(store, NULL, new_order);
1025 g_hash_table_destroy(fd_idx_hash);
1029 *-----------------------------------------------------------------------------
1031 *-----------------------------------------------------------------------------
1035 void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1040 FileData *fd = work->data;
1043 if (fd->thumb_pixbuf) (*done)++;
1045 if (fd->sidecar_files)
1047 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1053 void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
1055 GtkTreeStore *store;
1058 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1060 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1061 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1064 FileData *vflist_thumb_next_fd(ViewFile *vf)
1067 FileData *fd = NULL;
1069 /* first check the visible files */
1071 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1073 GtkTreeModel *store;
1075 gboolean valid = TRUE;
1077 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1078 gtk_tree_model_get_iter(store, &iter, tpath);
1079 gtk_tree_path_free(tpath);
1081 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1085 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1087 if (!nfd->thumb_pixbuf) fd = nfd;
1089 valid = gtk_tree_model_iter_next(store, &iter);
1093 /* then find first undone */
1097 GList *work = vf->list;
1100 FileData *fd_p = work->data;
1101 if (!fd_p->thumb_pixbuf)
1105 GList *work2 = fd_p->sidecar_files;
1107 while (work2 && !fd)
1110 if (!fd_p->thumb_pixbuf) fd = fd_p;
1111 work2 = work2->next;
1122 void vflist_thumb_reset_all(ViewFile *vf)
1124 GList *work = vf->list;
1127 FileData *fd = work->data;
1128 if (fd->thumb_pixbuf)
1130 g_object_unref(fd->thumb_pixbuf);
1131 fd->thumb_pixbuf = NULL;
1138 *-----------------------------------------------------------------------------
1140 *-----------------------------------------------------------------------------
1143 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1145 return g_list_nth_data(vf->list, row);
1148 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1151 GList *work, *work2;
1156 FileData *list_fd = work->data;
1157 if (list_fd == fd) return p;
1159 work2 = list_fd->sidecar_files;
1162 /* FIXME: return the same index also for sidecars
1163 it is sufficient for next/prev navigation but it should be rewritten
1164 without using indexes at all
1166 FileData *sidecar_fd = work2->data;
1167 if (sidecar_fd == fd) return p;
1168 work2 = work2->next;
1178 guint vflist_count(ViewFile *vf, gint64 *bytes)
1188 FileData *fd = work->data;
1196 return g_list_length(vf->list);
1199 GList *vflist_get_list(ViewFile *vf)
1207 FileData *fd = work->data;
1210 list = g_list_prepend(list, file_data_ref(fd));
1213 return g_list_reverse(list);
1217 *-----------------------------------------------------------------------------
1219 *-----------------------------------------------------------------------------
1222 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1224 GtkTreeModel *store;
1225 GtkTreeSelection *selection;
1228 gboolean found = FALSE;
1230 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1231 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1233 while (!found && work)
1235 GtkTreePath *tpath = work->data;
1239 gtk_tree_model_get_iter(store, &iter, tpath);
1240 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1241 if (fd_n == fd) found = TRUE;
1244 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1250 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1254 fd = vf_index_get_data(vf, row);
1255 return vflist_row_is_selected(vf, fd);
1258 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1260 GtkTreeModel *store;
1261 GtkTreeSelection *selection;
1265 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1266 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1276 GtkTreePath *tpath = work->data;
1280 gtk_tree_model_get_iter(store, &iter, tpath);
1281 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1290 count = g_list_length(slist);
1291 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1297 GList *vflist_selection_get_list(ViewFile *vf)
1299 GtkTreeModel *store;
1300 GtkTreeSelection *selection;
1305 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1306 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1310 GtkTreePath *tpath = work->data;
1314 gtk_tree_model_get_iter(store, &iter, tpath);
1315 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1317 list = g_list_prepend(list, file_data_ref(fd));
1319 if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
1321 /* unexpanded - add whole group */
1322 GList *work2 = fd->sidecar_files;
1325 FileData *sfd = work2->data;
1326 list = g_list_prepend(list, file_data_ref(sfd));
1327 work2 = work2->next;
1333 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1336 return g_list_reverse(list);
1339 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1341 GtkTreeModel *store;
1342 GtkTreeSelection *selection;
1347 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1348 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1352 GtkTreePath *tpath = work->data;
1356 gtk_tree_model_get_iter(store, &iter, tpath);
1357 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1359 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1363 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1366 return g_list_reverse(list);
1369 void vflist_select_all(ViewFile *vf)
1371 GtkTreeSelection *selection;
1373 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1374 gtk_tree_selection_select_all(selection);
1376 VFLIST(vf)->select_fd = NULL;
1379 void vflist_select_none(ViewFile *vf)
1381 GtkTreeSelection *selection;
1383 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1384 gtk_tree_selection_unselect_all(selection);
1387 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1392 tpath = gtk_tree_model_get_path(store, iter);
1393 result = gtk_tree_path_prev(tpath);
1395 gtk_tree_model_get_iter(store, iter, tpath);
1397 gtk_tree_path_free(tpath);
1402 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1404 if (!gtk_tree_model_get_iter_first(store, iter))
1409 GtkTreeIter next = *iter;
1411 if (gtk_tree_model_iter_next(store, &next))
1420 void vflist_select_invert(ViewFile *vf)
1423 GtkTreeSelection *selection;
1424 GtkTreeModel *store;
1427 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1428 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1430 /* Backward iteration prevents scrolling to the end of the list,
1431 * it scrolls to the first selected row instead. */
1432 valid = tree_model_get_iter_last(store, &iter);
1436 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1439 gtk_tree_selection_unselect_iter(selection, &iter);
1441 gtk_tree_selection_select_iter(selection, &iter);
1443 valid = tree_model_iter_prev(store, &iter);
1447 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1451 if (vflist_find_row(vf, fd, &iter) < 0) return;
1453 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1455 if (!vflist_row_is_selected(vf, fd))
1457 GtkTreeSelection *selection;
1458 GtkTreeModel *store;
1461 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1462 gtk_tree_selection_unselect_all(selection);
1463 gtk_tree_selection_select_iter(selection, &iter);
1464 vflist_move_cursor(vf, &iter);
1466 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1467 tpath = gtk_tree_model_get_path(store, &iter);
1468 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1469 gtk_tree_path_free(tpath);
1473 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1476 FileData *fd = NULL;
1478 if (sel_fd->parent) sel_fd = sel_fd->parent;
1487 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1489 if (match >= 0) break;
1492 if (fd) vflist_select_by_fd(vf, fd);
1496 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1498 GtkTreeModel *store;
1500 GtkTreeSelection *selection;
1504 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1506 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1507 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1509 valid = gtk_tree_model_get_iter_first(store, &iter);
1513 gboolean mark_val, selected;
1514 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1516 mark_val = file_data_get_mark(fd, n);
1517 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1521 case MTS_MODE_SET: selected = mark_val;
1523 case MTS_MODE_OR: selected = mark_val || selected;
1525 case MTS_MODE_AND: selected = mark_val && selected;
1527 case MTS_MODE_MINUS: selected = !mark_val && selected;
1532 gtk_tree_selection_select_iter(selection, &iter);
1534 gtk_tree_selection_unselect_iter(selection, &iter);
1536 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1540 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1542 GtkTreeModel *store;
1543 GtkTreeSelection *selection;
1548 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1550 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1551 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1555 GtkTreePath *tpath = work->data;
1559 gtk_tree_model_get_iter(store, &iter, tpath);
1560 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1562 /* the change has a very limited range and the standard notification would trigger
1563 complete re-read of the directory - try to do only minimal update instead */
1564 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1568 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1570 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1572 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1576 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1578 vf_refresh_idle(vf);
1582 /* mark functions can have various side effects - update all columns to be sure */
1583 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1584 /* mark functions can change sidecars too */
1585 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1589 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1593 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1598 *-----------------------------------------------------------------------------
1600 *-----------------------------------------------------------------------------
1603 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
1605 GtkTreeViewColumn *column;
1606 GtkCellRenderer *cell;
1609 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1610 if (!column) return;
1612 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1614 list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1619 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1620 gtk_tree_view_column_set_visible(column, thumb);
1622 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1623 if (!column) return;
1624 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1626 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1627 if (!column) return;
1628 gtk_tree_view_column_set_visible(column, !multiline);
1630 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1631 if (!column) return;
1632 gtk_tree_view_column_set_visible(column, !multiline);
1635 static gboolean vflist_is_multiline(ViewFile *vf)
1637 return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
1641 static void vflist_populate_view(ViewFile *vf, gboolean force)
1643 GtkTreeStore *store;
1646 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1652 vflist_store_clear(vf);
1657 vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
1659 selected = vflist_selection_get_list(vf);
1661 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force);
1663 if (selected && vflist_selection_count(vf, NULL) == 0)
1665 /* all selected files disappeared */
1666 vflist_select_closest(vf, selected->data);
1669 filelist_free(selected);
1672 vf_thumb_update(vf);
1675 gboolean vflist_refresh(ViewFile *vf)
1678 gboolean ret = TRUE;
1680 old_list = vf->list;
1683 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1686 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1688 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1689 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1690 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1692 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1693 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1696 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1698 vflist_populate_view(vf, FALSE);
1700 filelist_free(old_list);
1701 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1708 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1710 #define CELL_HEIGHT_OVERRIDE 512
1712 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1716 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1717 if (spec && G_IS_PARAM_SPEC_INT(spec))
1719 GParamSpecInt *spec_int;
1721 spec_int = G_PARAM_SPEC_INT(spec);
1722 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1726 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1728 static GdkColor color;
1729 static GtkWidget *done = NULL;
1735 style = gtk_widget_get_style(widget);
1736 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1737 shift_color(&color, -1, 0);
1744 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1745 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1747 ViewFile *vf = data;
1750 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1751 g_object_set(G_OBJECT(cell),
1752 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1753 "cell-background-set", set, NULL);
1756 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1758 GtkTreeViewColumn *column;
1759 GtkCellRenderer *renderer;
1761 column = gtk_tree_view_column_new();
1762 gtk_tree_view_column_set_title(column, title);
1763 gtk_tree_view_column_set_min_width(column, 4);
1767 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1768 renderer = gtk_cell_renderer_text_new();
1771 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1773 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1774 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1776 gtk_tree_view_column_set_expand(column, TRUE);
1780 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1781 renderer = gtk_cell_renderer_pixbuf_new();
1782 cell_renderer_height_override(renderer);
1783 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1784 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1787 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1788 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1789 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1791 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1794 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1796 ViewFile *vf = data;
1797 GtkTreeStore *store;
1798 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1804 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1805 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1808 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1810 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1812 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1815 /* the change has a very limited range and the standard notification would trigger
1816 complete re-read of the directory - try to do only minimal update instead */
1817 file_data_unregister_notify_func(vf_notify_cb, vf);
1818 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1819 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1821 vf_refresh_idle(vf);
1825 /* mark functions can have various side effects - update all columns to be sure */
1826 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1827 /* mark functions can change sidecars too */
1828 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1830 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1832 gtk_tree_path_free(path);
1835 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1837 GtkTreeViewColumn *column;
1838 GtkCellRenderer *renderer;
1839 GtkTreeStore *store;
1842 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1844 renderer = gtk_cell_renderer_toggle_new();
1845 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1847 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1848 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1849 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1851 index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1852 gtk_tree_view_column_set_fixed_width(column, 22);
1853 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1856 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1860 *-----------------------------------------------------------------------------
1862 *-----------------------------------------------------------------------------
1865 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1868 if (!dir_fd) return FALSE;
1869 if (vf->dir_fd == dir_fd) return TRUE;
1871 file_data_unref(vf->dir_fd);
1872 vf->dir_fd = file_data_ref(dir_fd);
1874 /* force complete reload */
1875 vflist_store_clear(vf);
1877 filelist_free(vf->list);
1880 ret = vf_refresh(vf);
1881 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
1885 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1887 ViewFile *vf = data;
1889 file_data_unregister_notify_func(vf_notify_cb, vf);
1891 vflist_select_idle_cancel(vf);
1892 vf_refresh_idle_cancel(vf);
1895 filelist_free(vf->list);
1898 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1900 GtkTreeStore *store;
1901 GtkTreeSelection *selection;
1902 GType flist_types[FILE_COLUMN_COUNT];
1906 vf->info = g_new0(ViewFileInfoList, 1);
1908 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1909 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1910 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1911 flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING;
1912 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1913 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
1914 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1915 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1916 flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN;
1917 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1918 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1919 flist_types[i] = G_TYPE_BOOLEAN;
1921 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
1923 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1924 g_object_unref(store);
1926 g_signal_connect(G_OBJECT(vf->listview), "row-expanded",
1927 G_CALLBACK(vflist_expand_cb), vf);
1929 g_signal_connect(G_OBJECT(vf->listview), "row-collapsed",
1930 G_CALLBACK(vflist_collapse_cb), vf);
1932 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1933 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1934 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
1936 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
1937 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
1941 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
1943 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
1944 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
1948 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
1949 g_assert(column == FILE_VIEW_COLUMN_THUMB);
1952 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE);
1953 g_assert(column == FILE_VIEW_COLUMN_FORMATTED);
1956 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
1957 g_assert(column == FILE_VIEW_COLUMN_SIZE);
1960 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
1961 g_assert(column == FILE_VIEW_COLUMN_DATE);
1964 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1968 void vflist_thumb_set(ViewFile *vf, gboolean enable)
1970 if (VFLIST(vf)->thumbs_enabled == enable) return;
1972 VFLIST(vf)->thumbs_enabled = enable;
1974 /* vflist_populate_view is better than vf_refresh:
1975 - no need to re-read the directory
1976 - force update because the formatted string has changed
1980 vflist_populate_view(vf, TRUE);
1981 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
1985 void vflist_marks_set(ViewFile *vf, gboolean enable)
1987 GList *columns, *work;
1989 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
1994 GtkTreeViewColumn *column = work->data;
1995 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
1998 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
1999 gtk_tree_view_column_set_visible(column, enable);
2002 g_list_free(columns);
2005 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */