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 #if FILEDATA_MARKS_SIZE != 6
858 #error this needs to be updated
861 FILE_COLUMN_COLOR, FALSE, -1);
863 #if !STORE_SET_IS_SLOW
866 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
867 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
876 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected, gboolean force)
881 gint num_ordered = 0;
882 gint num_prepended = 0;
884 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
890 FileData *fd = work->data;
891 gboolean done = FALSE;
895 FileData *old_fd = NULL;
896 gint old_version = 0;
900 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
901 FILE_COLUMN_POINTER, &old_fd,
902 FILE_COLUMN_VERSION, &old_version,
912 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
914 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
916 if (match == 0) g_warning("multiple fd for the same path");
932 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
937 here should be used gtk_tree_store_append, but this function seems to be O(n)
938 and it seems to be much faster to add new entries to the beginning and reorder later
941 gtk_tree_store_prepend(store, &new, parent_iter);
944 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
945 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected, force);
947 if (g_list_find(selected, fd))
949 /* renamed files - the same fd appears at different position - select it again*/
950 GtkTreeSelection *selection;
951 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
952 gtk_tree_selection_select_iter(selection, &new);
959 file_data_unref(old_fd);
960 valid = gtk_tree_store_remove(store, &iter);
965 if (fd->version != old_version || force)
967 vflist_setup_iter(vf, store, &iter, fd);
968 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected, force);
971 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
982 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
983 file_data_unref(old_fd);
985 valid = gtk_tree_store_remove(store, &iter);
988 /* move the prepended entries to the correct position */
992 gint num_total = num_prepended + num_ordered;
993 gint *new_order = g_malloc(num_total * sizeof(gint));
995 for (i = 0; i < num_total; i++)
998 new_order[i] = num_prepended + i;
1000 new_order[i] = num_total - 1 - i;
1002 gtk_tree_store_reorder(store, parent_iter, new_order);
1008 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
1011 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
1013 GtkTreeStore *store;
1016 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1017 if (!vf->list) return;
1023 FileData *fd = work->data;
1024 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1029 vf->sort_method = type;
1030 vf->sort_ascend = ascend;
1032 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1034 new_order = g_malloc(i * sizeof(gint));
1040 FileData *fd = work->data;
1041 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1046 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1047 gtk_tree_store_reorder(store, NULL, new_order);
1050 g_hash_table_destroy(fd_idx_hash);
1054 *-----------------------------------------------------------------------------
1056 *-----------------------------------------------------------------------------
1060 void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1065 FileData *fd = work->data;
1068 if (fd->thumb_pixbuf) (*done)++;
1070 if (fd->sidecar_files)
1072 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1078 void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
1080 GtkTreeStore *store;
1083 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1085 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1086 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1089 FileData *vflist_thumb_next_fd(ViewFile *vf)
1092 FileData *fd = NULL;
1094 /* first check the visible files */
1096 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1098 GtkTreeModel *store;
1100 gboolean valid = TRUE;
1102 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1103 gtk_tree_model_get_iter(store, &iter, tpath);
1104 gtk_tree_path_free(tpath);
1106 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1110 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1112 if (!nfd->thumb_pixbuf) fd = nfd;
1114 valid = gtk_tree_model_iter_next(store, &iter);
1118 /* then find first undone */
1122 GList *work = vf->list;
1125 FileData *fd_p = work->data;
1126 if (!fd_p->thumb_pixbuf)
1130 GList *work2 = fd_p->sidecar_files;
1132 while (work2 && !fd)
1135 if (!fd_p->thumb_pixbuf) fd = fd_p;
1136 work2 = work2->next;
1147 void vflist_thumb_reset_all(ViewFile *vf)
1149 GList *work = vf->list;
1152 FileData *fd = work->data;
1153 if (fd->thumb_pixbuf)
1155 g_object_unref(fd->thumb_pixbuf);
1156 fd->thumb_pixbuf = NULL;
1163 *-----------------------------------------------------------------------------
1165 *-----------------------------------------------------------------------------
1168 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1170 return g_list_nth_data(vf->list, row);
1173 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1176 GList *work, *work2;
1181 FileData *list_fd = work->data;
1182 if (list_fd == fd) return p;
1184 work2 = list_fd->sidecar_files;
1187 /* FIXME: return the same index also for sidecars
1188 it is sufficient for next/prev navigation but it should be rewritten
1189 without using indexes at all
1191 FileData *sidecar_fd = work2->data;
1192 if (sidecar_fd == fd) return p;
1193 work2 = work2->next;
1203 guint vflist_count(ViewFile *vf, gint64 *bytes)
1213 FileData *fd = work->data;
1221 return g_list_length(vf->list);
1224 GList *vflist_get_list(ViewFile *vf)
1232 FileData *fd = work->data;
1235 list = g_list_prepend(list, file_data_ref(fd));
1238 return g_list_reverse(list);
1242 *-----------------------------------------------------------------------------
1244 *-----------------------------------------------------------------------------
1247 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1249 GtkTreeModel *store;
1250 GtkTreeSelection *selection;
1253 gboolean found = FALSE;
1255 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1256 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1258 while (!found && work)
1260 GtkTreePath *tpath = work->data;
1264 gtk_tree_model_get_iter(store, &iter, tpath);
1265 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1266 if (fd_n == fd) found = TRUE;
1269 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1275 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1279 fd = vf_index_get_data(vf, row);
1280 return vflist_row_is_selected(vf, fd);
1283 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1285 GtkTreeModel *store;
1286 GtkTreeSelection *selection;
1290 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1291 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1301 GtkTreePath *tpath = work->data;
1305 gtk_tree_model_get_iter(store, &iter, tpath);
1306 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1315 count = g_list_length(slist);
1316 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1322 GList *vflist_selection_get_list(ViewFile *vf)
1324 GtkTreeModel *store;
1325 GtkTreeSelection *selection;
1330 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1331 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1335 GtkTreePath *tpath = work->data;
1339 gtk_tree_model_get_iter(store, &iter, tpath);
1340 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1342 list = g_list_prepend(list, file_data_ref(fd));
1344 if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
1346 /* unexpanded - add whole group */
1347 GList *work2 = fd->sidecar_files;
1350 FileData *sfd = work2->data;
1351 list = g_list_prepend(list, file_data_ref(sfd));
1352 work2 = work2->next;
1358 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1361 return g_list_reverse(list);
1364 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1366 GtkTreeModel *store;
1367 GtkTreeSelection *selection;
1372 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1373 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1377 GtkTreePath *tpath = work->data;
1381 gtk_tree_model_get_iter(store, &iter, tpath);
1382 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1384 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1388 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1391 return g_list_reverse(list);
1394 void vflist_select_all(ViewFile *vf)
1396 GtkTreeSelection *selection;
1398 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1399 gtk_tree_selection_select_all(selection);
1401 VFLIST(vf)->select_fd = NULL;
1404 void vflist_select_none(ViewFile *vf)
1406 GtkTreeSelection *selection;
1408 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1409 gtk_tree_selection_unselect_all(selection);
1412 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1417 tpath = gtk_tree_model_get_path(store, iter);
1418 result = gtk_tree_path_prev(tpath);
1420 gtk_tree_model_get_iter(store, iter, tpath);
1422 gtk_tree_path_free(tpath);
1427 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1429 if (!gtk_tree_model_get_iter_first(store, iter))
1434 GtkTreeIter next = *iter;
1436 if (gtk_tree_model_iter_next(store, &next))
1445 void vflist_select_invert(ViewFile *vf)
1448 GtkTreeSelection *selection;
1449 GtkTreeModel *store;
1452 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1453 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1455 /* Backward iteration prevents scrolling to the end of the list,
1456 * it scrolls to the first selected row instead. */
1457 valid = tree_model_get_iter_last(store, &iter);
1461 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1464 gtk_tree_selection_unselect_iter(selection, &iter);
1466 gtk_tree_selection_select_iter(selection, &iter);
1468 valid = tree_model_iter_prev(store, &iter);
1472 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1476 if (vflist_find_row(vf, fd, &iter) < 0) return;
1478 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1480 if (!vflist_row_is_selected(vf, fd))
1482 GtkTreeSelection *selection;
1483 GtkTreeModel *store;
1486 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1487 gtk_tree_selection_unselect_all(selection);
1488 gtk_tree_selection_select_iter(selection, &iter);
1489 vflist_move_cursor(vf, &iter);
1491 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1492 tpath = gtk_tree_model_get_path(store, &iter);
1493 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1494 gtk_tree_path_free(tpath);
1498 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1501 FileData *fd = NULL;
1503 if (sel_fd->parent) sel_fd = sel_fd->parent;
1512 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1514 if (match >= 0) break;
1517 if (fd) vflist_select_by_fd(vf, fd);
1521 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1523 GtkTreeModel *store;
1525 GtkTreeSelection *selection;
1529 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1531 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1532 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1534 valid = gtk_tree_model_get_iter_first(store, &iter);
1538 gboolean mark_val, selected;
1539 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1541 mark_val = file_data_get_mark(fd, n);
1542 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1546 case MTS_MODE_SET: selected = mark_val;
1548 case MTS_MODE_OR: selected = mark_val || selected;
1550 case MTS_MODE_AND: selected = mark_val && selected;
1552 case MTS_MODE_MINUS: selected = !mark_val && selected;
1557 gtk_tree_selection_select_iter(selection, &iter);
1559 gtk_tree_selection_unselect_iter(selection, &iter);
1561 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1565 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1567 GtkTreeModel *store;
1568 GtkTreeSelection *selection;
1573 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1575 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1576 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1580 GtkTreePath *tpath = work->data;
1584 gtk_tree_model_get_iter(store, &iter, tpath);
1585 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1587 /* the change has a very limited range and the standard notification would trigger
1588 complete re-read of the directory - try to do only minimal update instead */
1589 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1593 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1595 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1597 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1601 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1603 vf_refresh_idle(vf);
1607 /* mark functions can have various side effects - update all columns to be sure */
1608 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1609 /* mark functions can change sidecars too */
1610 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1614 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1618 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1623 *-----------------------------------------------------------------------------
1625 *-----------------------------------------------------------------------------
1628 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
1630 GtkTreeViewColumn *column;
1631 GtkCellRenderer *cell;
1634 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1635 if (!column) return;
1637 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1639 list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1644 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1645 gtk_tree_view_column_set_visible(column, thumb);
1647 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1648 if (!column) return;
1649 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1651 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1652 if (!column) return;
1653 gtk_tree_view_column_set_visible(column, !multiline);
1655 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1656 if (!column) return;
1657 gtk_tree_view_column_set_visible(column, !multiline);
1660 static gboolean vflist_is_multiline(ViewFile *vf)
1662 return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
1666 static void vflist_populate_view(ViewFile *vf, gboolean force)
1668 GtkTreeStore *store;
1671 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1677 vflist_store_clear(vf, FALSE);
1682 vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
1684 selected = vflist_selection_get_list(vf);
1686 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force);
1688 if (selected && vflist_selection_count(vf, NULL) == 0)
1690 /* all selected files disappeared */
1691 vflist_select_closest(vf, selected->data);
1694 filelist_free(selected);
1697 vf_thumb_update(vf);
1700 gboolean vflist_refresh(ViewFile *vf)
1703 gboolean ret = TRUE;
1705 old_list = vf->list;
1708 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1711 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1713 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1715 if (vf->marks_enabled)
1717 // When marks are enabled, lock FileDatas so that we don't end up re-parsing XML
1718 // each time a mark is changed.
1719 file_data_lock_list(vf->list);
1723 // FIXME: only do this when needed (aka when we just switched from
1724 // FIXME: marks-enabled to marks-disabled)
1725 file_data_unlock_list(vf->list);
1728 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1729 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1731 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1732 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1735 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1737 vflist_populate_view(vf, FALSE);
1739 DEBUG_1("%s vflist_refresh: free filelist", get_exec_time());
1741 filelist_free(old_list);
1742 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1749 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1751 #define CELL_HEIGHT_OVERRIDE 512
1753 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1757 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1758 if (spec && G_IS_PARAM_SPEC_INT(spec))
1760 GParamSpecInt *spec_int;
1762 spec_int = G_PARAM_SPEC_INT(spec);
1763 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1767 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1769 static GdkColor color;
1770 static GtkWidget *done = NULL;
1776 style = gtk_widget_get_style(widget);
1777 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1778 shift_color(&color, -1, 0);
1785 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1786 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1788 ViewFile *vf = data;
1791 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1792 g_object_set(G_OBJECT(cell),
1793 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1794 "cell-background-set", set, NULL);
1797 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1799 GtkTreeViewColumn *column;
1800 GtkCellRenderer *renderer;
1802 column = gtk_tree_view_column_new();
1803 gtk_tree_view_column_set_title(column, title);
1804 gtk_tree_view_column_set_min_width(column, 4);
1808 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1809 renderer = gtk_cell_renderer_text_new();
1812 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1814 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1815 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1817 gtk_tree_view_column_set_expand(column, TRUE);
1821 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1822 renderer = gtk_cell_renderer_pixbuf_new();
1823 cell_renderer_height_override(renderer);
1824 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1825 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1828 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1829 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1830 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1832 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1835 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1837 ViewFile *vf = data;
1838 GtkTreeStore *store;
1839 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1845 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1846 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1849 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1851 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1853 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1856 /* the change has a very limited range and the standard notification would trigger
1857 complete re-read of the directory - try to do only minimal update instead */
1858 file_data_unregister_notify_func(vf_notify_cb, vf);
1859 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1860 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1862 vf_refresh_idle(vf);
1866 /* mark functions can have various side effects - update all columns to be sure */
1867 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1868 /* mark functions can change sidecars too */
1869 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1871 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1873 gtk_tree_path_free(path);
1876 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1878 GtkTreeViewColumn *column;
1879 GtkCellRenderer *renderer;
1881 renderer = gtk_cell_renderer_toggle_new();
1882 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1884 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1885 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1886 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1888 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1889 gtk_tree_view_column_set_fixed_width(column, 22);
1890 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1893 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1897 *-----------------------------------------------------------------------------
1899 *-----------------------------------------------------------------------------
1902 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1905 if (!dir_fd) return FALSE;
1906 if (vf->dir_fd == dir_fd) return TRUE;
1908 file_data_unref(vf->dir_fd);
1909 vf->dir_fd = file_data_ref(dir_fd);
1911 /* force complete reload */
1912 vflist_store_clear(vf, TRUE);
1914 filelist_free(vf->list);
1917 ret = vf_refresh(vf);
1918 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
1922 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1924 ViewFile *vf = data;
1926 file_data_unregister_notify_func(vf_notify_cb, vf);
1928 vflist_select_idle_cancel(vf);
1929 vf_refresh_idle_cancel(vf);
1932 filelist_free(vf->list);
1935 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1937 GtkTreeStore *store;
1938 GtkTreeSelection *selection;
1939 GType flist_types[FILE_COLUMN_COUNT];
1943 vf->info = g_new0(ViewFileInfoList, 1);
1945 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1946 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1947 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1948 flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING;
1949 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1950 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
1951 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1952 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1953 flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN;
1954 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1955 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1956 flist_types[i] = G_TYPE_BOOLEAN;
1958 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
1960 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1961 g_object_unref(store);
1963 g_signal_connect(G_OBJECT(vf->listview), "row-expanded",
1964 G_CALLBACK(vflist_expand_cb), vf);
1966 g_signal_connect(G_OBJECT(vf->listview), "row-collapsed",
1967 G_CALLBACK(vflist_collapse_cb), vf);
1969 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1970 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1971 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
1973 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
1974 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
1978 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
1980 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
1981 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
1985 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
1986 g_assert(column == FILE_VIEW_COLUMN_THUMB);
1989 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE);
1990 g_assert(column == FILE_VIEW_COLUMN_FORMATTED);
1993 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
1994 g_assert(column == FILE_VIEW_COLUMN_SIZE);
1997 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
1998 g_assert(column == FILE_VIEW_COLUMN_DATE);
2001 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2005 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2007 if (VFLIST(vf)->thumbs_enabled == enable) return;
2009 VFLIST(vf)->thumbs_enabled = enable;
2011 /* vflist_populate_view is better than vf_refresh:
2012 - no need to re-read the directory
2013 - force update because the formatted string has changed
2017 vflist_populate_view(vf, TRUE);
2018 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
2022 void vflist_marks_set(ViewFile *vf, gboolean enable)
2024 GList *columns, *work;
2026 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2031 GtkTreeViewColumn *column = work->data;
2032 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2035 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2036 gtk_tree_view_column_set_visible(column, enable);
2041 // Previously disabled, which means that vf->list is complete
2042 file_data_lock_list(vf->list);
2046 // Previously enabled, which means that vf->list is incomplete
2049 g_list_free(columns);
2052 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */