2 * Copyright (C) 2004 John Ellis
3 * Copyright (C) 2008 - 2016 The Geeqie Team
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "view_file_list.h"
26 #include "cache_maint.h"
31 #include "layout_image.h"
36 #include "ui_fileops.h"
38 #include "ui_tree_edit.h"
39 #include "uri_utils.h"
40 #include "view_file.h"
42 #include <gdk/gdkkeysyms.h> /* for keyboard values */
44 /* Index to tree store */
46 FILE_COLUMN_POINTER = 0,
49 FILE_COLUMN_FORMATTED,
57 FILE_COLUMN_MARKS_LAST = FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
62 /* Index to tree view */
64 FILE_VIEW_COLUMN_MARKS = 0,
65 FILE_VIEW_COLUMN_MARKS_LAST = FILE_VIEW_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
66 FILE_VIEW_COLUMN_THUMB,
67 FILE_VIEW_COLUMN_FORMATTED,
68 FILE_VIEW_COLUMN_SIZE,
69 FILE_VIEW_COLUMN_DATE,
70 FILE_VIEW_COLUMN_COUNT
75 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd);
76 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data);
77 static void vflist_populate_view(ViewFile *vf, gboolean force);
78 static gboolean vflist_is_multiline(ViewFile *vf);
79 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded);
83 *-----------------------------------------------------------------------------
85 *-----------------------------------------------------------------------------
92 } ViewFileFindRowData;
94 static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
96 ViewFileFindRowData *find = data;
98 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
109 static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter)
112 ViewFileFindRowData data = {fd, iter, FALSE, 0};
114 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
115 gtk_tree_model_foreach(store, vflist_find_row_cb, &data);
125 static FileData *vflist_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter)
128 GtkTreeViewColumn *column;
130 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), x, y,
131 &tpath, &column, NULL, NULL))
137 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
138 gtk_tree_model_get_iter(store, &row, tpath);
139 gtk_tree_path_free(tpath);
140 gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &fd, -1);
148 static gboolean vflist_store_clear_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
151 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
153 /* it seems that gtk_tree_store_clear may call some callbacks
154 that use the column. Set the pointer to NULL to be safe. */
155 gtk_tree_store_set(GTK_TREE_STORE(model), iter, FILE_COLUMN_POINTER, NULL, -1);
160 static void vflist_store_clear(ViewFile *vf, gboolean unlock_files)
165 if (unlock_files && vf->marks_enabled)
167 // unlock locked files in this directory
168 filelist_read(vf->dir_fd, &files, NULL);
171 FileData *fd = files->data;
173 file_data_unlock(fd);
174 file_data_unref(fd); // undo the ref that got added in filelist_read
179 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
180 gtk_tree_model_foreach(store, vflist_store_clear_cb, NULL);
181 gtk_tree_store_clear(GTK_TREE_STORE(store));
184 void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set)
189 if (vflist_find_row(vf, fd, &iter) < 0) return;
190 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
191 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
194 static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
199 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
201 tpath = gtk_tree_model_get_path(store, iter);
202 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
203 gtk_tree_path_free(tpath);
208 *-----------------------------------------------------------------------------
210 *-----------------------------------------------------------------------------
213 static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
214 GtkSelectionData *selection_data, guint info,
215 guint time, gpointer data)
220 if (!VFLIST(vf)->click_fd) return;
222 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
224 list = vf_selection_get_list(vf);
228 list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
232 uri_selection_data_set_uris_from_filelist(selection_data, list);
236 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
240 vflist_color_set(vf, VFLIST(vf)->click_fd, TRUE);
242 if (VFLIST(vf)->thumbs_enabled &&
243 VFLIST(vf)->click_fd && VFLIST(vf)->click_fd->thumb_pixbuf)
247 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
248 items = vf_selection_count(vf, NULL);
252 dnd_set_drag_icon(widget, context, VFLIST(vf)->click_fd->thumb_pixbuf, items);
256 static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
260 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
262 if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE)
268 static void vflist_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context,
269 int x, int y, GtkSelectionData *selection,
270 guint info, guint time, gpointer data)
274 if (info == TARGET_TEXT_PLAIN) {
275 FileData *fd = vflist_find_data_by_coord(vf, x, y, NULL);
278 /* Add keywords to file */
279 gchar *str = (gchar *) gtk_selection_data_get_text(selection);
280 GList *kw_list = string_to_keywords_list(str);
282 metadata_append_list(fd, KEYWORD_KEY, kw_list);
283 string_list_free(kw_list);
289 void vflist_dnd_init(ViewFile *vf)
291 gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
292 dnd_file_drag_types, dnd_file_drag_types_count,
293 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
294 gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL,
295 dnd_file_drag_types, dnd_file_drag_types_count,
296 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
298 g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
299 G_CALLBACK(vflist_dnd_get), vf);
300 g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
301 G_CALLBACK(vflist_dnd_begin), vf);
302 g_signal_connect(G_OBJECT(vf->listview), "drag_end",
303 G_CALLBACK(vflist_dnd_end), vf);
304 g_signal_connect(G_OBJECT(vf->listview), "drag_data_received",
305 G_CALLBACK(vflist_drag_data_received), vf);
309 *-----------------------------------------------------------------------------
311 *-----------------------------------------------------------------------------
314 GList *vflist_selection_get_one(ViewFile *vf, FileData *fd)
316 GList *list = g_list_append(NULL, file_data_ref(fd));
318 if (fd->sidecar_files)
320 /* check if the row is expanded */
324 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
325 if (vflist_find_row(vf, fd, &iter) >= 0)
329 tpath = gtk_tree_model_get_path(store, &iter);
330 if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
332 /* unexpanded - add whole group */
333 GList *work = fd->sidecar_files;
336 FileData *sfd = work->data;
337 list = g_list_prepend(list, file_data_ref(sfd));
341 gtk_tree_path_free(tpath);
343 list = g_list_reverse(list);
349 GList *vflist_pop_menu_file_list(ViewFile *vf)
351 if (!VFLIST(vf)->click_fd) return NULL;
353 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
355 return vf_selection_get_list(vf);
357 return vflist_selection_get_one(vf, VFLIST(vf)->click_fd);
361 void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
365 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
369 list = vf_selection_get_list(vf);
370 view_window_new_from_list(list);
375 view_window_new(VFLIST(vf)->click_fd);
379 void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
384 list = vf_pop_menu_file_list(vf);
385 if (options->file_ops.enable_in_place_rename &&
386 list && !list->next && VFLIST(vf)->click_fd)
393 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
394 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
398 tpath = gtk_tree_model_get_path(store, &iter);
399 tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
400 FILE_VIEW_COLUMN_FORMATTED, VFLIST(vf)->click_fd->name,
401 vflist_row_rename_cb, vf);
402 gtk_tree_path_free(tpath);
407 file_util_rename(NULL, list, vf->listview);
410 void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
414 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
417 layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
421 vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled);
425 void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
429 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
431 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
434 void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
437 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
438 VFLIST(vf)->click_fd = NULL;
444 *-----------------------------------------------------------------------------
446 *-----------------------------------------------------------------------------
449 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
454 if (!new || !new[0]) return FALSE;
456 new_path = g_build_filename(vf->dir_fd->path, new, NULL);
458 if (strchr(new, G_DIR_SEPARATOR) != NULL)
460 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
461 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
466 gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL);
467 FileData *fd = file_data_new_group(old_path); /* get the fd from cache */
468 file_util_rename_simple(fd, new_path, vf->listview);
478 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
486 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return;
487 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
488 tpath = gtk_tree_model_get_path(store, &iter);
489 tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
490 gtk_tree_path_free(tpath);
492 popup_menu_position_clamp(menu, x, y, 0);
495 gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
500 if (event->keyval != GDK_KEY_Menu) return FALSE;
502 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
508 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
509 gtk_tree_model_get_iter(store, &iter, tpath);
510 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
511 gtk_tree_path_free(tpath);
515 VFLIST(vf)->click_fd = NULL;
518 vf->popup = vf_pop_menu(vf);
519 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
524 gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
530 GtkTreeViewColumn *column;
532 vf->clicked_mark = 0;
534 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
535 &tpath, &column, NULL, NULL))
538 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
540 if (bevent->button == MOUSE_BUTTON_LEFT &&
541 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
544 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
545 vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
547 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
549 gtk_tree_model_get_iter(store, &iter, tpath);
550 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
551 gtk_tree_path_free(tpath);
554 VFLIST(vf)->click_fd = fd;
556 if (bevent->button == MOUSE_BUTTON_RIGHT)
558 vf->popup = vf_pop_menu(vf);
559 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
560 bevent->button, bevent->time);
564 if (!fd) return FALSE;
566 if (bevent->button == MOUSE_BUTTON_MIDDLE)
568 if (!vflist_row_is_selected(vf, fd))
570 vflist_color_set(vf, fd, TRUE);
576 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
577 !(bevent->state & GDK_SHIFT_MASK ) &&
578 !(bevent->state & GDK_CONTROL_MASK ) &&
579 vflist_row_is_selected(vf, fd))
581 GtkTreeSelection *selection;
583 gtk_widget_grab_focus(widget);
586 /* returning FALSE and further processing of the event is needed for
587 correct operation of the expander, to show the sidecar files.
588 It however resets the selection of multiple files. With this condition
589 it should work for both cases */
590 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
591 return (gtk_tree_selection_count_selected_rows(selection) > 1);
594 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
596 if (vf->layout) layout_image_full_screen_start(vf->layout);
602 gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
609 if (bevent->button == MOUSE_BUTTON_MIDDLE)
611 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
614 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
619 if ((bevent->x != 0 || bevent->y != 0) &&
620 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
621 &tpath, NULL, NULL, NULL))
625 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
626 gtk_tree_model_get_iter(store, &iter, tpath);
627 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
628 gtk_tree_path_free(tpath);
631 if (bevent->button == MOUSE_BUTTON_MIDDLE)
633 if (fd && VFLIST(vf)->click_fd == fd)
635 GtkTreeSelection *selection;
637 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
638 if (vflist_row_is_selected(vf, fd))
640 gtk_tree_selection_unselect_iter(selection, &iter);
644 gtk_tree_selection_select_iter(selection, &iter);
650 if (fd && VFLIST(vf)->click_fd == fd &&
651 !(bevent->state & GDK_SHIFT_MASK ) &&
652 !(bevent->state & GDK_CONTROL_MASK ) &&
653 vflist_row_is_selected(vf, fd))
655 GtkTreeSelection *selection;
657 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
658 gtk_tree_selection_unselect_all(selection);
659 gtk_tree_selection_select_iter(selection, &iter);
660 vflist_move_cursor(vf, &iter);
666 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
668 FileData *read_ahead_fd = NULL;
674 cur_fd = layout_image_get_fd(vf->layout);
675 if (sel_fd == cur_fd) return; /* no change */
677 row = g_list_index(vf->list, sel_fd);
678 // FIXME sidecar data
680 if (sel_fd && options->image.enable_read_ahead && row >= 0)
682 if (row > g_list_index(vf->list, cur_fd) &&
683 (guint) (row + 1) < vf_count(vf, NULL))
685 read_ahead_fd = vf_index_get_data(vf, row + 1);
689 read_ahead_fd = vf_index_get_data(vf, row - 1);
693 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
696 static gboolean vflist_select_idle_cb(gpointer data)
702 VFLIST(vf)->select_idle_id = 0;
708 if (VFLIST(vf)->select_fd)
710 vflist_select_image(vf, VFLIST(vf)->select_fd);
711 VFLIST(vf)->select_fd = NULL;
714 VFLIST(vf)->select_idle_id = 0;
718 static void vflist_select_idle_cancel(ViewFile *vf)
720 if (VFLIST(vf)->select_idle_id)
722 g_source_remove(VFLIST(vf)->select_idle_id);
723 VFLIST(vf)->select_idle_id = 0;
727 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
728 gboolean path_currently_selected, gpointer data)
733 if (!path_currently_selected &&
734 gtk_tree_model_get_iter(store, &iter, tpath))
736 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
740 VFLIST(vf)->select_fd = NULL;
744 !VFLIST(vf)->select_idle_id)
746 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
752 static void vflist_expand_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
755 vflist_set_expanded(vf, iter, TRUE);
758 static void vflist_collapse_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
761 vflist_set_expanded(vf, iter, FALSE);
765 *-----------------------------------------------------------------------------
767 *-----------------------------------------------------------------------------
771 static gchar* vflist_get_formatted(ViewFile *vf, const gchar *name, const gchar *sidecars, const gchar *size, const gchar *time, gboolean expanded)
773 gboolean multiline = vflist_is_multiline(vf);
778 text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time);
782 text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars);
787 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded)
796 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
798 gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
799 FILE_COLUMN_NAME, &name,
800 FILE_COLUMN_SIDECARS, &sidecars,
801 FILE_COLUMN_SIZE, &size,
802 FILE_COLUMN_DATE, &time,
804 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
806 gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED, formatted,
807 FILE_COLUMN_EXPANDED, expanded,
816 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
819 gchar *sidecars = NULL;
821 const gchar *time = text_from_time(fd->date);
822 gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
823 const gchar *disabled_grouping;
825 gboolean expanded = FALSE;
827 if (fd->sidecar_files) /* expanded has no effect on files without sidecars */
829 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_EXPANDED, &expanded, -1);
832 sidecars = file_data_sc_list_to_string(fd);
834 disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
835 name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
836 size = text_from_size(fd->size);
838 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
840 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
841 FILE_COLUMN_VERSION, fd->version,
842 FILE_COLUMN_THUMB, fd->thumb_pixbuf,
843 FILE_COLUMN_FORMATTED, formatted,
844 FILE_COLUMN_SIDECARS, sidecars,
845 FILE_COLUMN_NAME, name,
846 FILE_COLUMN_SIZE, size,
847 FILE_COLUMN_DATE, time,
848 #define STORE_SET_IS_SLOW 1
849 #if STORE_SET_IS_SLOW
850 /* this is 3x faster on a directory with 20000 files */
851 FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
852 FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
853 FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
854 FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
855 FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
856 FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
857 FILE_COLUMN_MARKS + 6, file_data_get_mark(fd, 6),
858 FILE_COLUMN_MARKS + 7, file_data_get_mark(fd, 7),
859 FILE_COLUMN_MARKS + 8, file_data_get_mark(fd, 8),
860 FILE_COLUMN_MARKS + 9, file_data_get_mark(fd, 9),
861 #if FILEDATA_MARKS_SIZE != 10
862 #error this needs to be updated
865 FILE_COLUMN_COLOR, FALSE, -1);
867 #if !STORE_SET_IS_SLOW
870 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
871 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
880 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected, gboolean force)
885 gint num_ordered = 0;
886 gint num_prepended = 0;
888 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
894 FileData *fd = work->data;
895 gboolean done = FALSE;
899 FileData *old_fd = NULL;
900 gint old_version = 0;
904 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
905 FILE_COLUMN_POINTER, &old_fd,
906 FILE_COLUMN_VERSION, &old_version,
916 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
918 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
920 if (match == 0) g_warning("multiple fd for the same path");
936 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
941 here should be used gtk_tree_store_append, but this function seems to be O(n)
942 and it seems to be much faster to add new entries to the beginning and reorder later
945 gtk_tree_store_prepend(store, &new, parent_iter);
948 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
949 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected, force);
951 if (g_list_find(selected, fd))
953 /* renamed files - the same fd appears at different position - select it again*/
954 GtkTreeSelection *selection;
955 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
956 gtk_tree_selection_select_iter(selection, &new);
963 file_data_unref(old_fd);
964 valid = gtk_tree_store_remove(store, &iter);
969 if (fd->version != old_version || force)
971 vflist_setup_iter(vf, store, &iter, fd);
972 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected, force);
975 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
986 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
987 file_data_unref(old_fd);
989 valid = gtk_tree_store_remove(store, &iter);
992 /* move the prepended entries to the correct position */
996 gint num_total = num_prepended + num_ordered;
997 gint *new_order = g_malloc(num_total * sizeof(gint));
999 for (i = 0; i < num_total; i++)
1001 if (i < num_ordered)
1002 new_order[i] = num_prepended + i;
1004 new_order[i] = num_total - 1 - i;
1006 gtk_tree_store_reorder(store, parent_iter, new_order);
1012 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
1015 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
1017 GtkTreeStore *store;
1020 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1021 if (!vf->list) return;
1027 FileData *fd = work->data;
1028 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1033 vf->sort_method = type;
1034 vf->sort_ascend = ascend;
1036 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1038 new_order = g_malloc(i * sizeof(gint));
1044 FileData *fd = work->data;
1045 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1050 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1051 gtk_tree_store_reorder(store, NULL, new_order);
1054 g_hash_table_destroy(fd_idx_hash);
1058 *-----------------------------------------------------------------------------
1060 *-----------------------------------------------------------------------------
1064 void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1069 FileData *fd = work->data;
1072 if (fd->thumb_pixbuf) (*done)++;
1074 if (fd->sidecar_files)
1076 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1082 void vflist_read_metadata_progress_count(GList *list, gint *count, gint *done)
1087 FileData *fd = work->data;
1090 if (fd->metadata_in_idle_loaded) (*done)++;
1092 if (fd->sidecar_files)
1094 vflist_read_metadata_progress_count(fd->sidecar_files, count, done);
1100 void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
1102 GtkTreeStore *store;
1105 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1107 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1108 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1111 FileData *vflist_thumb_next_fd(ViewFile *vf)
1114 FileData *fd = NULL;
1116 /* first check the visible files */
1118 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1120 GtkTreeModel *store;
1122 gboolean valid = TRUE;
1124 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1125 gtk_tree_model_get_iter(store, &iter, tpath);
1126 gtk_tree_path_free(tpath);
1129 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1133 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1135 if (!nfd->thumb_pixbuf) fd = nfd;
1137 valid = gtk_tree_model_iter_next(store, &iter);
1141 /* then find first undone */
1145 GList *work = vf->list;
1148 FileData *fd_p = work->data;
1149 if (!fd_p->thumb_pixbuf)
1153 GList *work2 = fd_p->sidecar_files;
1155 while (work2 && !fd)
1158 if (!fd_p->thumb_pixbuf) fd = fd_p;
1159 work2 = work2->next;
1170 *-----------------------------------------------------------------------------
1172 *-----------------------------------------------------------------------------
1175 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1178 GList *work, *work2;
1183 FileData *list_fd = work->data;
1184 if (list_fd == fd) return p;
1186 work2 = list_fd->sidecar_files;
1189 /* FIXME: return the same index also for sidecars
1190 it is sufficient for next/prev navigation but it should be rewritten
1191 without using indexes at all
1193 FileData *sidecar_fd = work2->data;
1194 if (sidecar_fd == fd) return p;
1195 work2 = work2->next;
1206 *-----------------------------------------------------------------------------
1208 *-----------------------------------------------------------------------------
1211 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1213 GtkTreeModel *store;
1214 GtkTreeSelection *selection;
1217 gboolean found = FALSE;
1219 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1220 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1222 while (!found && work)
1224 GtkTreePath *tpath = work->data;
1228 gtk_tree_model_get_iter(store, &iter, tpath);
1229 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1230 if (fd_n == fd) found = TRUE;
1233 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1239 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1243 fd = vf_index_get_data(vf, row);
1244 return vflist_row_is_selected(vf, fd);
1247 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1249 GtkTreeModel *store;
1250 GtkTreeSelection *selection;
1254 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1255 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1265 GtkTreePath *tpath = work->data;
1269 gtk_tree_model_get_iter(store, &iter, tpath);
1270 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1279 count = g_list_length(slist);
1280 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1286 GList *vflist_selection_get_list(ViewFile *vf)
1288 GtkTreeModel *store;
1289 GtkTreeSelection *selection;
1294 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1295 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1299 GtkTreePath *tpath = work->data;
1303 gtk_tree_model_get_iter(store, &iter, tpath);
1304 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1306 list = g_list_prepend(list, file_data_ref(fd));
1308 if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
1310 /* unexpanded - add whole group */
1311 GList *work2 = fd->sidecar_files;
1314 FileData *sfd = work2->data;
1315 list = g_list_prepend(list, file_data_ref(sfd));
1316 work2 = work2->next;
1322 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1325 return g_list_reverse(list);
1328 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1330 GtkTreeModel *store;
1331 GtkTreeSelection *selection;
1336 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1337 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1341 GtkTreePath *tpath = work->data;
1345 gtk_tree_model_get_iter(store, &iter, tpath);
1346 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1348 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1352 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1355 return g_list_reverse(list);
1358 void vflist_select_all(ViewFile *vf)
1360 GtkTreeSelection *selection;
1362 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1363 gtk_tree_selection_select_all(selection);
1365 VFLIST(vf)->select_fd = NULL;
1368 void vflist_select_none(ViewFile *vf)
1370 GtkTreeSelection *selection;
1372 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1373 gtk_tree_selection_unselect_all(selection);
1376 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1381 tpath = gtk_tree_model_get_path(store, iter);
1382 result = gtk_tree_path_prev(tpath);
1384 gtk_tree_model_get_iter(store, iter, tpath);
1386 gtk_tree_path_free(tpath);
1391 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1393 if (!gtk_tree_model_get_iter_first(store, iter))
1398 GtkTreeIter next = *iter;
1400 if (gtk_tree_model_iter_next(store, &next))
1409 void vflist_select_invert(ViewFile *vf)
1412 GtkTreeSelection *selection;
1413 GtkTreeModel *store;
1416 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1417 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1419 /* Backward iteration prevents scrolling to the end of the list,
1420 * it scrolls to the first selected row instead. */
1421 valid = tree_model_get_iter_last(store, &iter);
1425 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1428 gtk_tree_selection_unselect_iter(selection, &iter);
1430 gtk_tree_selection_select_iter(selection, &iter);
1432 valid = tree_model_iter_prev(store, &iter);
1436 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1440 if (vflist_find_row(vf, fd, &iter) < 0) return;
1442 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1444 if (!vflist_row_is_selected(vf, fd))
1446 GtkTreeSelection *selection;
1447 GtkTreeModel *store;
1450 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1451 gtk_tree_selection_unselect_all(selection);
1452 gtk_tree_selection_select_iter(selection, &iter);
1453 vflist_move_cursor(vf, &iter);
1455 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1456 tpath = gtk_tree_model_get_path(store, &iter);
1457 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1458 gtk_tree_path_free(tpath);
1462 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1465 FileData *fd = NULL;
1467 if (sel_fd->parent) sel_fd = sel_fd->parent;
1476 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1478 if (match >= 0) break;
1481 if (fd) vflist_select_by_fd(vf, fd);
1485 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1487 GtkTreeModel *store;
1489 GtkTreeSelection *selection;
1493 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1495 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1496 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1498 valid = gtk_tree_model_get_iter_first(store, &iter);
1502 gboolean mark_val, selected;
1503 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1505 mark_val = file_data_get_mark(fd, n);
1506 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1510 case MTS_MODE_SET: selected = mark_val;
1512 case MTS_MODE_OR: selected = mark_val || selected;
1514 case MTS_MODE_AND: selected = mark_val && selected;
1516 case MTS_MODE_MINUS: selected = !mark_val && selected;
1521 gtk_tree_selection_select_iter(selection, &iter);
1523 gtk_tree_selection_unselect_iter(selection, &iter);
1525 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1529 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1531 GtkTreeModel *store;
1532 GtkTreeSelection *selection;
1537 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1539 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1540 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1544 GtkTreePath *tpath = work->data;
1548 gtk_tree_model_get_iter(store, &iter, tpath);
1549 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1551 /* the change has a very limited range and the standard notification would trigger
1552 complete re-read of the directory - try to do only minimal update instead */
1553 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1557 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1559 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1561 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1565 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1567 vf_refresh_idle(vf);
1571 /* mark functions can have various side effects - update all columns to be sure */
1572 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1573 /* mark functions can change sidecars too */
1574 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1578 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1582 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1587 *-----------------------------------------------------------------------------
1589 *-----------------------------------------------------------------------------
1592 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
1594 GtkTreeViewColumn *column;
1595 GtkCellRenderer *cell;
1598 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1599 if (!column) return;
1601 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1603 list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1608 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1609 gtk_tree_view_column_set_visible(column, thumb);
1611 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1612 if (!column) return;
1613 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1615 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1616 if (!column) return;
1617 gtk_tree_view_column_set_visible(column, !multiline);
1619 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1620 if (!column) return;
1621 gtk_tree_view_column_set_visible(column, !multiline);
1624 static gboolean vflist_is_multiline(ViewFile *vf)
1626 return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
1630 static void vflist_populate_view(ViewFile *vf, gboolean force)
1632 GtkTreeStore *store;
1635 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1641 vflist_store_clear(vf, FALSE);
1646 vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
1648 selected = vflist_selection_get_list(vf);
1650 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force);
1652 if (selected && vflist_selection_count(vf, NULL) == 0)
1654 /* all selected files disappeared */
1655 vflist_select_closest(vf, selected->data);
1658 filelist_free(selected);
1661 vf_thumb_update(vf);
1664 gboolean vflist_refresh(ViewFile *vf)
1667 gboolean ret = TRUE;
1669 old_list = vf->list;
1672 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1675 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1677 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1679 if (vf->marks_enabled)
1681 // When marks are enabled, lock FileDatas so that we don't end up re-parsing XML
1682 // each time a mark is changed.
1683 file_data_lock_list(vf->list);
1687 // FIXME: only do this when needed (aka when we just switched from
1688 // FIXME: marks-enabled to marks-disabled)
1689 file_data_unlock_list(vf->list);
1692 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1693 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1695 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1696 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1699 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1701 vflist_populate_view(vf, FALSE);
1703 DEBUG_1("%s vflist_refresh: free filelist", get_exec_time());
1705 filelist_free(old_list);
1706 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1713 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1715 #define CELL_HEIGHT_OVERRIDE 512
1717 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1721 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1722 if (spec && G_IS_PARAM_SPEC_INT(spec))
1724 GParamSpecInt *spec_int;
1726 spec_int = G_PARAM_SPEC_INT(spec);
1727 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1731 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1733 static GdkColor color;
1734 static GtkWidget *done = NULL;
1740 style = gtk_widget_get_style(widget);
1741 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1742 shift_color(&color, -1, 0);
1749 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1750 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1752 ViewFile *vf = data;
1755 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1756 g_object_set(G_OBJECT(cell),
1757 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1758 "cell-background-set", set, NULL);
1761 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1763 GtkTreeViewColumn *column;
1764 GtkCellRenderer *renderer;
1766 column = gtk_tree_view_column_new();
1767 gtk_tree_view_column_set_title(column, title);
1768 gtk_tree_view_column_set_min_width(column, 4);
1772 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1773 renderer = gtk_cell_renderer_text_new();
1776 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1778 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1779 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1781 gtk_tree_view_column_set_expand(column, TRUE);
1785 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1786 renderer = gtk_cell_renderer_pixbuf_new();
1787 cell_renderer_height_override(renderer);
1788 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1789 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1792 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1793 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1794 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1796 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1799 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1801 ViewFile *vf = data;
1802 GtkTreeStore *store;
1803 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1809 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1810 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1813 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1815 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1817 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1820 /* the change has a very limited range and the standard notification would trigger
1821 complete re-read of the directory - try to do only minimal update instead */
1822 file_data_unregister_notify_func(vf_notify_cb, vf);
1823 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1824 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1826 vf_refresh_idle(vf);
1830 /* mark functions can have various side effects - update all columns to be sure */
1831 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1832 /* mark functions can change sidecars too */
1833 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1835 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1837 gtk_tree_path_free(path);
1840 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1842 GtkTreeViewColumn *column;
1843 GtkCellRenderer *renderer;
1845 renderer = gtk_cell_renderer_toggle_new();
1846 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1848 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1849 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1850 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1852 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1853 gtk_tree_view_column_set_fixed_width(column, 22);
1854 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1857 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1861 *-----------------------------------------------------------------------------
1863 *-----------------------------------------------------------------------------
1866 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1869 if (!dir_fd) return FALSE;
1870 if (vf->dir_fd == dir_fd) return TRUE;
1872 file_data_unref(vf->dir_fd);
1873 vf->dir_fd = file_data_ref(dir_fd);
1875 /* force complete reload */
1876 vflist_store_clear(vf, TRUE);
1878 filelist_free(vf->list);
1881 ret = vf_refresh(vf);
1882 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
1886 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1888 ViewFile *vf = data;
1890 file_data_unregister_notify_func(vf_notify_cb, vf);
1892 vflist_select_idle_cancel(vf);
1893 vf_refresh_idle_cancel(vf);
1896 filelist_free(vf->list);
1899 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1901 GtkTreeStore *store;
1902 GtkTreeSelection *selection;
1903 GType flist_types[FILE_COLUMN_COUNT];
1907 vf->info = g_new0(ViewFileInfoList, 1);
1909 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1910 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1911 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1912 flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING;
1913 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1914 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
1915 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1916 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1917 flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN;
1918 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1919 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1920 flist_types[i] = G_TYPE_BOOLEAN;
1922 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
1924 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1925 g_object_unref(store);
1927 g_signal_connect(G_OBJECT(vf->listview), "row-expanded",
1928 G_CALLBACK(vflist_expand_cb), vf);
1930 g_signal_connect(G_OBJECT(vf->listview), "row-collapsed",
1931 G_CALLBACK(vflist_collapse_cb), vf);
1933 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1934 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1935 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
1937 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
1938 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
1942 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
1944 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
1945 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
1949 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
1950 g_assert(column == FILE_VIEW_COLUMN_THUMB);
1953 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE);
1954 g_assert(column == FILE_VIEW_COLUMN_FORMATTED);
1957 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
1958 g_assert(column == FILE_VIEW_COLUMN_SIZE);
1961 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
1962 g_assert(column == FILE_VIEW_COLUMN_DATE);
1965 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1969 void vflist_thumb_set(ViewFile *vf, gboolean enable)
1971 if (VFLIST(vf)->thumbs_enabled == enable) return;
1973 VFLIST(vf)->thumbs_enabled = enable;
1975 /* vflist_populate_view is better than vf_refresh:
1976 - no need to re-read the directory
1977 - force update because the formatted string has changed
1981 vflist_populate_view(vf, TRUE);
1982 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
1986 void vflist_marks_set(ViewFile *vf, gboolean enable)
1988 GList *columns, *work;
1990 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
1995 GtkTreeViewColumn *column = work->data;
1996 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
1999 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2000 gtk_tree_view_column_set_visible(column, enable);
2005 // Previously disabled, which means that vf->list is complete
2006 file_data_lock_list(vf->list);
2010 // Previously enabled, which means that vf->list is incomplete
2013 g_list_free(columns);
2016 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */