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, gboolean unlock_files)
156 if (unlock_files && vf->marks_enabled)
158 // unlock locked files in this directory
159 filelist_read(vf->dir_fd, &files, NULL);
162 FileData *fd = files->data;
164 file_data_unlock(fd);
165 file_data_unref(fd); // undo the ref that got added in filelist_read
170 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
171 gtk_tree_model_foreach(store, vflist_store_clear_cb, NULL);
172 gtk_tree_store_clear(GTK_TREE_STORE(store));
175 void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set)
180 if (vflist_find_row(vf, fd, &iter) < 0) return;
181 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
182 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
185 static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
190 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
192 tpath = gtk_tree_model_get_path(store, iter);
193 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
194 gtk_tree_path_free(tpath);
199 *-----------------------------------------------------------------------------
201 *-----------------------------------------------------------------------------
204 static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
205 GtkSelectionData *selection_data, guint info,
206 guint time, gpointer data)
211 if (!VFLIST(vf)->click_fd) return;
213 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
215 list = vf_selection_get_list(vf);
219 list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
223 uri_selection_data_set_uris_from_filelist(selection_data, list);
227 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
231 vflist_color_set(vf, VFLIST(vf)->click_fd, TRUE);
233 if (VFLIST(vf)->thumbs_enabled &&
234 VFLIST(vf)->click_fd && VFLIST(vf)->click_fd->thumb_pixbuf)
238 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
239 items = vf_selection_count(vf, NULL);
243 dnd_set_drag_icon(widget, context, VFLIST(vf)->click_fd->thumb_pixbuf, items);
247 static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
251 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
253 if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE)
259 static void vflist_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context,
260 int x, int y, GtkSelectionData *selection,
261 guint info, guint time, gpointer data)
265 if (info == TARGET_TEXT_PLAIN) {
266 FileData *fd = vflist_find_data_by_coord(vf, x, y, NULL);
269 /* Add keywords to file */
270 gchar *str = (gchar *) gtk_selection_data_get_text(selection);
271 GList *kw_list = string_to_keywords_list(str);
273 metadata_append_list(fd, KEYWORD_KEY, kw_list);
274 string_list_free(kw_list);
280 void vflist_dnd_init(ViewFile *vf)
282 gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
283 dnd_file_drag_types, dnd_file_drag_types_count,
284 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
285 gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL,
286 dnd_file_drag_types, dnd_file_drag_types_count,
287 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
289 g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
290 G_CALLBACK(vflist_dnd_get), vf);
291 g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
292 G_CALLBACK(vflist_dnd_begin), vf);
293 g_signal_connect(G_OBJECT(vf->listview), "drag_end",
294 G_CALLBACK(vflist_dnd_end), vf);
295 g_signal_connect(G_OBJECT(vf->listview), "drag_data_received",
296 G_CALLBACK(vflist_drag_data_received), vf);
300 *-----------------------------------------------------------------------------
302 *-----------------------------------------------------------------------------
305 GList *vflist_selection_get_one(ViewFile *vf, FileData *fd)
307 GList *list = g_list_append(NULL, file_data_ref(fd));
309 if (fd->sidecar_files)
311 /* check if the row is expanded */
315 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
316 if (vflist_find_row(vf, fd, &iter) >= 0)
320 tpath = gtk_tree_model_get_path(store, &iter);
321 if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
323 /* unexpanded - add whole group */
324 GList *work = fd->sidecar_files;
327 FileData *sfd = work->data;
328 list = g_list_prepend(list, file_data_ref(sfd));
332 gtk_tree_path_free(tpath);
334 list = g_list_reverse(list);
340 GList *vflist_pop_menu_file_list(ViewFile *vf)
342 if (!VFLIST(vf)->click_fd) return NULL;
344 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
346 return vf_selection_get_list(vf);
348 return vflist_selection_get_one(vf, VFLIST(vf)->click_fd);
352 void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
356 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
360 list = vf_selection_get_list(vf);
361 view_window_new_from_list(list);
366 view_window_new(VFLIST(vf)->click_fd);
370 void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
375 list = vf_pop_menu_file_list(vf);
376 if (options->file_ops.enable_in_place_rename &&
377 list && !list->next && VFLIST(vf)->click_fd)
384 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
385 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
389 tpath = gtk_tree_model_get_path(store, &iter);
390 tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
391 FILE_VIEW_COLUMN_FORMATTED, VFLIST(vf)->click_fd->name,
392 vflist_row_rename_cb, vf);
393 gtk_tree_path_free(tpath);
398 file_util_rename(NULL, list, vf->listview);
401 void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
405 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
408 layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
412 vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled);
416 void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
420 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
422 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
425 void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
428 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
429 VFLIST(vf)->click_fd = NULL;
435 *-----------------------------------------------------------------------------
437 *-----------------------------------------------------------------------------
440 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
445 if (!new || !new[0]) return FALSE;
447 new_path = g_build_filename(vf->dir_fd->path, new, NULL);
449 if (strchr(new, G_DIR_SEPARATOR) != NULL)
451 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
452 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
457 gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL);
458 FileData *fd = file_data_new_group(old_path); /* get the fd from cache */
459 file_util_rename_simple(fd, new_path, vf->listview);
469 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
477 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return;
478 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
479 tpath = gtk_tree_model_get_path(store, &iter);
480 tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
481 gtk_tree_path_free(tpath);
483 popup_menu_position_clamp(menu, x, y, 0);
486 gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
491 if (event->keyval != GDK_KEY_Menu) return FALSE;
493 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
499 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
500 gtk_tree_model_get_iter(store, &iter, tpath);
501 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
502 gtk_tree_path_free(tpath);
506 VFLIST(vf)->click_fd = NULL;
509 vf->popup = vf_pop_menu(vf);
510 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
515 gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
521 GtkTreeViewColumn *column;
523 vf->clicked_mark = 0;
525 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
526 &tpath, &column, NULL, NULL))
529 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
531 if (bevent->button == MOUSE_BUTTON_LEFT &&
532 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
535 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
536 vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
538 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
540 gtk_tree_model_get_iter(store, &iter, tpath);
541 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
542 gtk_tree_path_free(tpath);
545 VFLIST(vf)->click_fd = fd;
547 if (bevent->button == MOUSE_BUTTON_RIGHT)
549 vf->popup = vf_pop_menu(vf);
550 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
551 bevent->button, bevent->time);
555 if (!fd) return FALSE;
557 if (bevent->button == MOUSE_BUTTON_MIDDLE)
559 if (!vflist_row_is_selected(vf, fd))
561 vflist_color_set(vf, fd, TRUE);
567 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
568 !(bevent->state & GDK_SHIFT_MASK ) &&
569 !(bevent->state & GDK_CONTROL_MASK ) &&
570 vflist_row_is_selected(vf, fd))
572 GtkTreeSelection *selection;
574 gtk_widget_grab_focus(widget);
577 /* returning FALSE and further processing of the event is needed for
578 correct operation of the expander, to show the sidecar files.
579 It however resets the selection of multiple files. With this condition
580 it should work for both cases */
581 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
582 return (gtk_tree_selection_count_selected_rows(selection) > 1);
585 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
587 if (vf->layout) layout_image_full_screen_start(vf->layout);
593 gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
600 if (bevent->button == MOUSE_BUTTON_MIDDLE)
602 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
605 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
610 if ((bevent->x != 0 || bevent->y != 0) &&
611 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
612 &tpath, NULL, NULL, NULL))
616 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
617 gtk_tree_model_get_iter(store, &iter, tpath);
618 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
619 gtk_tree_path_free(tpath);
622 if (bevent->button == MOUSE_BUTTON_MIDDLE)
624 if (fd && VFLIST(vf)->click_fd == fd)
626 GtkTreeSelection *selection;
628 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
629 if (vflist_row_is_selected(vf, fd))
631 gtk_tree_selection_unselect_iter(selection, &iter);
635 gtk_tree_selection_select_iter(selection, &iter);
641 if (fd && VFLIST(vf)->click_fd == fd &&
642 !(bevent->state & GDK_SHIFT_MASK ) &&
643 !(bevent->state & GDK_CONTROL_MASK ) &&
644 vflist_row_is_selected(vf, fd))
646 GtkTreeSelection *selection;
648 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
649 gtk_tree_selection_unselect_all(selection);
650 gtk_tree_selection_select_iter(selection, &iter);
651 vflist_move_cursor(vf, &iter);
657 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
659 FileData *read_ahead_fd = NULL;
665 cur_fd = layout_image_get_fd(vf->layout);
666 if (sel_fd == cur_fd) return; /* no change */
668 row = g_list_index(vf->list, sel_fd);
669 // FIXME sidecar data
671 if (sel_fd && options->image.enable_read_ahead && row >= 0)
673 if (row > g_list_index(vf->list, cur_fd) &&
674 (guint) (row + 1) < vf_count(vf, NULL))
676 read_ahead_fd = vf_index_get_data(vf, row + 1);
680 read_ahead_fd = vf_index_get_data(vf, row - 1);
684 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
687 static gboolean vflist_select_idle_cb(gpointer data)
693 VFLIST(vf)->select_idle_id = 0;
699 if (VFLIST(vf)->select_fd)
701 vflist_select_image(vf, VFLIST(vf)->select_fd);
702 VFLIST(vf)->select_fd = NULL;
705 VFLIST(vf)->select_idle_id = 0;
709 static void vflist_select_idle_cancel(ViewFile *vf)
711 if (VFLIST(vf)->select_idle_id)
713 g_source_remove(VFLIST(vf)->select_idle_id);
714 VFLIST(vf)->select_idle_id = 0;
718 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
719 gboolean path_currently_selected, gpointer data)
724 if (!path_currently_selected &&
725 gtk_tree_model_get_iter(store, &iter, tpath))
727 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
731 VFLIST(vf)->select_fd = NULL;
735 !VFLIST(vf)->select_idle_id)
737 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
743 static void vflist_expand_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
746 vflist_set_expanded(vf, iter, TRUE);
749 static void vflist_collapse_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
752 vflist_set_expanded(vf, iter, FALSE);
756 *-----------------------------------------------------------------------------
758 *-----------------------------------------------------------------------------
762 static gchar* vflist_get_formatted(ViewFile *vf, const gchar *name, const gchar *sidecars, const gchar *size, const gchar *time, gboolean expanded)
764 gboolean multiline = vflist_is_multiline(vf);
769 text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time);
773 text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars);
778 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded)
787 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
789 gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
790 FILE_COLUMN_NAME, &name,
791 FILE_COLUMN_SIDECARS, &sidecars,
792 FILE_COLUMN_SIZE, &size,
793 FILE_COLUMN_DATE, &time,
795 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
797 gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED, formatted,
798 FILE_COLUMN_EXPANDED, expanded,
807 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
810 gchar *sidecars = NULL;
812 const gchar *time = text_from_time(fd->date);
813 gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
814 const gchar *disabled_grouping;
816 gboolean expanded = FALSE;
818 if (fd->sidecar_files) /* expanded has no effect on files without sidecars */
820 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_EXPANDED, &expanded, -1);
823 sidecars = file_data_sc_list_to_string(fd);
825 disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
826 name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
827 size = text_from_size(fd->size);
829 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
831 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
832 FILE_COLUMN_VERSION, fd->version,
833 FILE_COLUMN_THUMB, fd->thumb_pixbuf,
834 FILE_COLUMN_FORMATTED, formatted,
835 FILE_COLUMN_SIDECARS, sidecars,
836 FILE_COLUMN_NAME, name,
837 FILE_COLUMN_SIZE, size,
838 FILE_COLUMN_DATE, time,
839 #define STORE_SET_IS_SLOW 1
840 #if STORE_SET_IS_SLOW
841 /* this is 3x faster on a directory with 20000 files */
842 FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
843 FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
844 FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
845 FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
846 FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
847 FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
848 #if FILEDATA_MARKS_SIZE != 6
849 #error this needs to be updated
852 FILE_COLUMN_COLOR, FALSE, -1);
854 #if !STORE_SET_IS_SLOW
857 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
858 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
867 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected, gboolean force)
872 gint num_ordered = 0;
873 gint num_prepended = 0;
875 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
881 FileData *fd = work->data;
882 gboolean done = FALSE;
886 FileData *old_fd = NULL;
887 gint old_version = 0;
891 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
892 FILE_COLUMN_POINTER, &old_fd,
893 FILE_COLUMN_VERSION, &old_version,
903 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
905 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
907 if (match == 0) g_warning("multiple fd for the same path");
923 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
928 here should be used gtk_tree_store_append, but this function seems to be O(n)
929 and it seems to be much faster to add new entries to the beginning and reorder later
932 gtk_tree_store_prepend(store, &new, parent_iter);
935 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
936 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected, force);
938 if (g_list_find(selected, fd))
940 /* renamed files - the same fd appears at different position - select it again*/
941 GtkTreeSelection *selection;
942 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
943 gtk_tree_selection_select_iter(selection, &new);
950 file_data_unref(old_fd);
951 valid = gtk_tree_store_remove(store, &iter);
956 if (fd->version != old_version || force)
958 vflist_setup_iter(vf, store, &iter, fd);
959 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected, force);
962 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
973 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
974 file_data_unref(old_fd);
976 valid = gtk_tree_store_remove(store, &iter);
979 /* move the prepended entries to the correct position */
983 gint num_total = num_prepended + num_ordered;
984 gint *new_order = g_malloc(num_total * sizeof(gint));
986 for (i = 0; i < num_total; i++)
989 new_order[i] = num_prepended + i;
991 new_order[i] = num_total - 1 - i;
993 gtk_tree_store_reorder(store, parent_iter, new_order);
999 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
1002 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
1004 GtkTreeStore *store;
1007 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1008 if (!vf->list) return;
1014 FileData *fd = work->data;
1015 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1020 vf->sort_method = type;
1021 vf->sort_ascend = ascend;
1023 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1025 new_order = g_malloc(i * sizeof(gint));
1031 FileData *fd = work->data;
1032 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1037 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1038 gtk_tree_store_reorder(store, NULL, new_order);
1041 g_hash_table_destroy(fd_idx_hash);
1045 *-----------------------------------------------------------------------------
1047 *-----------------------------------------------------------------------------
1051 void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1056 FileData *fd = work->data;
1059 if (fd->thumb_pixbuf) (*done)++;
1061 if (fd->sidecar_files)
1063 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1069 void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
1071 GtkTreeStore *store;
1074 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1076 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1077 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1080 FileData *vflist_thumb_next_fd(ViewFile *vf)
1083 FileData *fd = NULL;
1085 /* first check the visible files */
1087 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1089 GtkTreeModel *store;
1091 gboolean valid = TRUE;
1093 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1094 gtk_tree_model_get_iter(store, &iter, tpath);
1095 gtk_tree_path_free(tpath);
1097 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1101 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1103 if (!nfd->thumb_pixbuf) fd = nfd;
1105 valid = gtk_tree_model_iter_next(store, &iter);
1109 /* then find first undone */
1113 GList *work = vf->list;
1116 FileData *fd_p = work->data;
1117 if (!fd_p->thumb_pixbuf)
1121 GList *work2 = fd_p->sidecar_files;
1123 while (work2 && !fd)
1126 if (!fd_p->thumb_pixbuf) fd = fd_p;
1127 work2 = work2->next;
1138 void vflist_thumb_reset_all(ViewFile *vf)
1140 GList *work = vf->list;
1143 FileData *fd = work->data;
1144 if (fd->thumb_pixbuf)
1146 g_object_unref(fd->thumb_pixbuf);
1147 fd->thumb_pixbuf = NULL;
1154 *-----------------------------------------------------------------------------
1156 *-----------------------------------------------------------------------------
1159 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1161 return g_list_nth_data(vf->list, row);
1164 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1167 GList *work, *work2;
1172 FileData *list_fd = work->data;
1173 if (list_fd == fd) return p;
1175 work2 = list_fd->sidecar_files;
1178 /* FIXME: return the same index also for sidecars
1179 it is sufficient for next/prev navigation but it should be rewritten
1180 without using indexes at all
1182 FileData *sidecar_fd = work2->data;
1183 if (sidecar_fd == fd) return p;
1184 work2 = work2->next;
1194 guint vflist_count(ViewFile *vf, gint64 *bytes)
1204 FileData *fd = work->data;
1212 return g_list_length(vf->list);
1215 GList *vflist_get_list(ViewFile *vf)
1223 FileData *fd = work->data;
1226 list = g_list_prepend(list, file_data_ref(fd));
1229 return g_list_reverse(list);
1233 *-----------------------------------------------------------------------------
1235 *-----------------------------------------------------------------------------
1238 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1240 GtkTreeModel *store;
1241 GtkTreeSelection *selection;
1244 gboolean found = FALSE;
1246 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1247 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1249 while (!found && work)
1251 GtkTreePath *tpath = work->data;
1255 gtk_tree_model_get_iter(store, &iter, tpath);
1256 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1257 if (fd_n == fd) found = TRUE;
1260 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1266 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1270 fd = vf_index_get_data(vf, row);
1271 return vflist_row_is_selected(vf, fd);
1274 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1276 GtkTreeModel *store;
1277 GtkTreeSelection *selection;
1281 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1282 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1292 GtkTreePath *tpath = work->data;
1296 gtk_tree_model_get_iter(store, &iter, tpath);
1297 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1306 count = g_list_length(slist);
1307 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1313 GList *vflist_selection_get_list(ViewFile *vf)
1315 GtkTreeModel *store;
1316 GtkTreeSelection *selection;
1321 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1322 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1326 GtkTreePath *tpath = work->data;
1330 gtk_tree_model_get_iter(store, &iter, tpath);
1331 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1333 list = g_list_prepend(list, file_data_ref(fd));
1335 if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
1337 /* unexpanded - add whole group */
1338 GList *work2 = fd->sidecar_files;
1341 FileData *sfd = work2->data;
1342 list = g_list_prepend(list, file_data_ref(sfd));
1343 work2 = work2->next;
1349 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1352 return g_list_reverse(list);
1355 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1357 GtkTreeModel *store;
1358 GtkTreeSelection *selection;
1363 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1364 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1368 GtkTreePath *tpath = work->data;
1372 gtk_tree_model_get_iter(store, &iter, tpath);
1373 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1375 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1379 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1382 return g_list_reverse(list);
1385 void vflist_select_all(ViewFile *vf)
1387 GtkTreeSelection *selection;
1389 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1390 gtk_tree_selection_select_all(selection);
1392 VFLIST(vf)->select_fd = NULL;
1395 void vflist_select_none(ViewFile *vf)
1397 GtkTreeSelection *selection;
1399 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1400 gtk_tree_selection_unselect_all(selection);
1403 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1408 tpath = gtk_tree_model_get_path(store, iter);
1409 result = gtk_tree_path_prev(tpath);
1411 gtk_tree_model_get_iter(store, iter, tpath);
1413 gtk_tree_path_free(tpath);
1418 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1420 if (!gtk_tree_model_get_iter_first(store, iter))
1425 GtkTreeIter next = *iter;
1427 if (gtk_tree_model_iter_next(store, &next))
1436 void vflist_select_invert(ViewFile *vf)
1439 GtkTreeSelection *selection;
1440 GtkTreeModel *store;
1443 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1444 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1446 /* Backward iteration prevents scrolling to the end of the list,
1447 * it scrolls to the first selected row instead. */
1448 valid = tree_model_get_iter_last(store, &iter);
1452 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1455 gtk_tree_selection_unselect_iter(selection, &iter);
1457 gtk_tree_selection_select_iter(selection, &iter);
1459 valid = tree_model_iter_prev(store, &iter);
1463 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1467 if (vflist_find_row(vf, fd, &iter) < 0) return;
1469 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1471 if (!vflist_row_is_selected(vf, fd))
1473 GtkTreeSelection *selection;
1474 GtkTreeModel *store;
1477 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1478 gtk_tree_selection_unselect_all(selection);
1479 gtk_tree_selection_select_iter(selection, &iter);
1480 vflist_move_cursor(vf, &iter);
1482 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1483 tpath = gtk_tree_model_get_path(store, &iter);
1484 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1485 gtk_tree_path_free(tpath);
1489 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1492 FileData *fd = NULL;
1494 if (sel_fd->parent) sel_fd = sel_fd->parent;
1503 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1505 if (match >= 0) break;
1508 if (fd) vflist_select_by_fd(vf, fd);
1512 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1514 GtkTreeModel *store;
1516 GtkTreeSelection *selection;
1520 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1522 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1523 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1525 valid = gtk_tree_model_get_iter_first(store, &iter);
1529 gboolean mark_val, selected;
1530 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1532 mark_val = file_data_get_mark(fd, n);
1533 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1537 case MTS_MODE_SET: selected = mark_val;
1539 case MTS_MODE_OR: selected = mark_val || selected;
1541 case MTS_MODE_AND: selected = mark_val && selected;
1543 case MTS_MODE_MINUS: selected = !mark_val && selected;
1548 gtk_tree_selection_select_iter(selection, &iter);
1550 gtk_tree_selection_unselect_iter(selection, &iter);
1552 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1556 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1558 GtkTreeModel *store;
1559 GtkTreeSelection *selection;
1564 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1566 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1567 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1571 GtkTreePath *tpath = work->data;
1575 gtk_tree_model_get_iter(store, &iter, tpath);
1576 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1578 /* the change has a very limited range and the standard notification would trigger
1579 complete re-read of the directory - try to do only minimal update instead */
1580 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1584 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1586 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1588 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1592 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1594 vf_refresh_idle(vf);
1598 /* mark functions can have various side effects - update all columns to be sure */
1599 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1600 /* mark functions can change sidecars too */
1601 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1605 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1609 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1614 *-----------------------------------------------------------------------------
1616 *-----------------------------------------------------------------------------
1619 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
1621 GtkTreeViewColumn *column;
1622 GtkCellRenderer *cell;
1625 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1626 if (!column) return;
1628 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1630 list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1635 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1636 gtk_tree_view_column_set_visible(column, thumb);
1638 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1639 if (!column) return;
1640 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1642 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1643 if (!column) return;
1644 gtk_tree_view_column_set_visible(column, !multiline);
1646 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1647 if (!column) return;
1648 gtk_tree_view_column_set_visible(column, !multiline);
1651 static gboolean vflist_is_multiline(ViewFile *vf)
1653 return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
1657 static void vflist_populate_view(ViewFile *vf, gboolean force)
1659 GtkTreeStore *store;
1662 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1668 vflist_store_clear(vf, FALSE);
1673 vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
1675 selected = vflist_selection_get_list(vf);
1677 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force);
1679 if (selected && vflist_selection_count(vf, NULL) == 0)
1681 /* all selected files disappeared */
1682 vflist_select_closest(vf, selected->data);
1685 filelist_free(selected);
1688 vf_thumb_update(vf);
1691 gboolean vflist_refresh(ViewFile *vf)
1694 gboolean ret = TRUE;
1696 old_list = vf->list;
1699 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1702 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1704 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1706 if (vf->marks_enabled)
1708 // When marks are enabled, lock FileDatas so that we don't end up re-parsing XML
1709 // each time a mark is changed.
1710 file_data_lock_list(vf->list);
1714 // FIXME: only do this when needed (aka when we just switched from
1715 // FIXME: marks-enabled to marks-disabled)
1716 file_data_unlock_list(vf->list);
1719 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1720 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1722 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1723 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1726 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1728 vflist_populate_view(vf, FALSE);
1730 DEBUG_1("%s vflist_refresh: free filelist", get_exec_time());
1732 filelist_free(old_list);
1733 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1740 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1742 #define CELL_HEIGHT_OVERRIDE 512
1744 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1748 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1749 if (spec && G_IS_PARAM_SPEC_INT(spec))
1751 GParamSpecInt *spec_int;
1753 spec_int = G_PARAM_SPEC_INT(spec);
1754 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1758 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1760 static GdkColor color;
1761 static GtkWidget *done = NULL;
1767 style = gtk_widget_get_style(widget);
1768 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1769 shift_color(&color, -1, 0);
1776 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1777 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1779 ViewFile *vf = data;
1782 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1783 g_object_set(G_OBJECT(cell),
1784 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1785 "cell-background-set", set, NULL);
1788 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1790 GtkTreeViewColumn *column;
1791 GtkCellRenderer *renderer;
1793 column = gtk_tree_view_column_new();
1794 gtk_tree_view_column_set_title(column, title);
1795 gtk_tree_view_column_set_min_width(column, 4);
1799 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1800 renderer = gtk_cell_renderer_text_new();
1803 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1805 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1806 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1808 gtk_tree_view_column_set_expand(column, TRUE);
1812 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1813 renderer = gtk_cell_renderer_pixbuf_new();
1814 cell_renderer_height_override(renderer);
1815 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1816 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1819 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1820 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1821 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1823 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1826 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1828 ViewFile *vf = data;
1829 GtkTreeStore *store;
1830 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1836 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1837 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1840 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1842 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1844 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1847 /* the change has a very limited range and the standard notification would trigger
1848 complete re-read of the directory - try to do only minimal update instead */
1849 file_data_unregister_notify_func(vf_notify_cb, vf);
1850 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1851 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1853 vf_refresh_idle(vf);
1857 /* mark functions can have various side effects - update all columns to be sure */
1858 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1859 /* mark functions can change sidecars too */
1860 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1862 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1864 gtk_tree_path_free(path);
1867 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1869 GtkTreeViewColumn *column;
1870 GtkCellRenderer *renderer;
1872 renderer = gtk_cell_renderer_toggle_new();
1873 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1875 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1876 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1877 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1879 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1880 gtk_tree_view_column_set_fixed_width(column, 22);
1881 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1884 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1888 *-----------------------------------------------------------------------------
1890 *-----------------------------------------------------------------------------
1893 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1896 if (!dir_fd) return FALSE;
1897 if (vf->dir_fd == dir_fd) return TRUE;
1899 file_data_unref(vf->dir_fd);
1900 vf->dir_fd = file_data_ref(dir_fd);
1902 /* force complete reload */
1903 vflist_store_clear(vf, TRUE);
1905 filelist_free(vf->list);
1908 ret = vf_refresh(vf);
1909 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
1913 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1915 ViewFile *vf = data;
1917 file_data_unregister_notify_func(vf_notify_cb, vf);
1919 vflist_select_idle_cancel(vf);
1920 vf_refresh_idle_cancel(vf);
1923 filelist_free(vf->list);
1926 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1928 GtkTreeStore *store;
1929 GtkTreeSelection *selection;
1930 GType flist_types[FILE_COLUMN_COUNT];
1934 vf->info = g_new0(ViewFileInfoList, 1);
1936 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1937 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1938 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1939 flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING;
1940 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1941 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
1942 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1943 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1944 flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN;
1945 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1946 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1947 flist_types[i] = G_TYPE_BOOLEAN;
1949 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
1951 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1952 g_object_unref(store);
1954 g_signal_connect(G_OBJECT(vf->listview), "row-expanded",
1955 G_CALLBACK(vflist_expand_cb), vf);
1957 g_signal_connect(G_OBJECT(vf->listview), "row-collapsed",
1958 G_CALLBACK(vflist_collapse_cb), vf);
1960 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1961 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1962 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
1964 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
1965 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
1969 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
1971 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
1972 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
1976 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
1977 g_assert(column == FILE_VIEW_COLUMN_THUMB);
1980 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE);
1981 g_assert(column == FILE_VIEW_COLUMN_FORMATTED);
1984 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
1985 g_assert(column == FILE_VIEW_COLUMN_SIZE);
1988 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
1989 g_assert(column == FILE_VIEW_COLUMN_DATE);
1992 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1996 void vflist_thumb_set(ViewFile *vf, gboolean enable)
1998 if (VFLIST(vf)->thumbs_enabled == enable) return;
2000 VFLIST(vf)->thumbs_enabled = enable;
2002 /* vflist_populate_view is better than vf_refresh:
2003 - no need to re-read the directory
2004 - force update because the formatted string has changed
2008 vflist_populate_view(vf, TRUE);
2009 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
2013 void vflist_marks_set(ViewFile *vf, gboolean enable)
2015 GList *columns, *work;
2017 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2022 GtkTreeViewColumn *column = work->data;
2023 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2026 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2027 gtk_tree_view_column_set_visible(column, enable);
2032 // Previously disabled, which means that vf->list is complete
2033 file_data_lock_list(vf->list);
2037 // Previously enabled, which means that vf->list is incomplete
2040 g_list_free(columns);
2043 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */