4 * Copyright (C) 2008 - 2009 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);
69 static gboolean vflist_is_multiline(ViewFile *vf);
70 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded);
74 *-----------------------------------------------------------------------------
76 *-----------------------------------------------------------------------------
83 } ViewFileFindRowData;
85 static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
87 ViewFileFindRowData *find = data;
89 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
100 static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter)
103 ViewFileFindRowData data = {fd, iter, FALSE, 0};
105 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
106 gtk_tree_model_foreach(store, vflist_find_row_cb, &data);
116 static FileData *vflist_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter)
119 GtkTreeViewColumn *column;
121 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), x, y,
122 &tpath, &column, NULL, NULL))
128 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
129 gtk_tree_model_get_iter(store, &row, tpath);
130 gtk_tree_path_free(tpath);
131 gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &fd, -1);
140 static gint vflist_find_sidecar_list_idx(GList *work, FileData *fd)
145 FileData *fd_p = work->data;
146 if (fd == fd_p) return i;
150 GList *work2 = fd_p->sidecar_files;
154 if (fd == fd_p) return i;
164 static gint vflist_sidecar_list_count(GList *work)
169 FileData *fd = work->data;
172 GList *work2 = fd->sidecar_files;
184 static gboolean vflist_store_clear_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
187 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
192 static void vflist_store_clear(ViewFile *vf)
195 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
196 gtk_tree_model_foreach(store, vflist_store_clear_cb, NULL);
197 gtk_tree_store_clear(GTK_TREE_STORE(store));
200 void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set)
205 if (vflist_find_row(vf, fd, &iter) < 0) return;
206 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
207 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
210 static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
215 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
217 tpath = gtk_tree_model_get_path(store, iter);
218 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
219 gtk_tree_path_free(tpath);
223 static gint vflist_column_idx(ViewFile *vf, gint store_idx)
225 GList *columns, *work;
228 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
232 GtkTreeViewColumn *column = work->data;
233 if (store_idx == GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx")))
239 g_list_free(columns);
245 *-----------------------------------------------------------------------------
247 *-----------------------------------------------------------------------------
250 static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
251 GtkSelectionData *selection_data, guint info,
252 guint time, gpointer data)
256 gchar *uri_text = NULL;
259 if (!VFLIST(vf)->click_fd) return;
261 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
263 list = vf_selection_get_list(vf);
267 list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
272 uri_text = uri_text_from_filelist(list, &total, (info == TARGET_TEXT_PLAIN));
275 DEBUG_1("%s", uri_text);
277 gtk_selection_data_set(selection_data, selection_data->target,
278 8, (guchar *)uri_text, total);
282 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
286 vflist_color_set(vf, VFLIST(vf)->click_fd, TRUE);
288 if (VFLIST(vf)->thumbs_enabled &&
289 VFLIST(vf)->click_fd && VFLIST(vf)->click_fd->thumb_pixbuf)
293 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
294 items = vf_selection_count(vf, NULL);
298 dnd_set_drag_icon(widget, context, VFLIST(vf)->click_fd->thumb_pixbuf, items);
302 static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
306 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
308 if (context->action == GDK_ACTION_MOVE)
314 static void vflist_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context,
315 int x, int y, GtkSelectionData *selection,
316 guint info, guint time, gpointer data)
320 if (info == TARGET_TEXT_PLAIN) {
321 FileData *fd = vflist_find_data_by_coord(vf, x, y, NULL);
324 /* Add keywords to file */
325 gchar *str = g_strndup((gchar *)selection->data, selection->length);
326 GList *kw_list = string_to_keywords_list(str);
328 metadata_append_list(fd, KEYWORD_KEY, kw_list);
329 string_list_free(kw_list);
332 file notification should handle this automatically
333 if (vf->layout && vf->layout->bar_info) {
334 bar_set_fd(vf->layout->bar_info, fd);
341 void vflist_dnd_init(ViewFile *vf)
343 gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
344 dnd_file_drag_types, dnd_file_drag_types_count,
345 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
346 gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL,
347 dnd_file_drag_types, dnd_file_drag_types_count,
348 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
350 g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
351 G_CALLBACK(vflist_dnd_get), vf);
352 g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
353 G_CALLBACK(vflist_dnd_begin), vf);
354 g_signal_connect(G_OBJECT(vf->listview), "drag_end",
355 G_CALLBACK(vflist_dnd_end), vf);
356 g_signal_connect(G_OBJECT(vf->listview), "drag_data_received",
357 G_CALLBACK(vflist_drag_data_received), vf);
361 *-----------------------------------------------------------------------------
363 *-----------------------------------------------------------------------------
366 GList *vflist_pop_menu_file_list(ViewFile *vf)
369 if (!VFLIST(vf)->click_fd) return NULL;
371 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
373 return vf_selection_get_list(vf);
376 list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
378 if (VFLIST(vf)->click_fd->sidecar_files)
380 /* check if the row is expanded */
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 if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
392 /* unexpanded - add whole group */
393 GList *work = VFLIST(vf)->click_fd->sidecar_files;
396 FileData *sfd = work->data;
397 list = g_list_prepend(list, file_data_ref(sfd));
401 gtk_tree_path_free(tpath);
403 list = g_list_reverse(list);
409 void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
413 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
417 list = vf_selection_get_list(vf);
418 view_window_new_from_list(list);
423 view_window_new(VFLIST(vf)->click_fd);
427 void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
432 list = vf_pop_menu_file_list(vf);
433 if (options->file_ops.enable_in_place_rename &&
434 list && !list->next && VFLIST(vf)->click_fd)
441 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
442 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
446 tpath = gtk_tree_model_get_path(store, &iter);
447 tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
448 vflist_column_idx(vf, FILE_COLUMN_NAME), VFLIST(vf)->click_fd->name,
449 vflist_row_rename_cb, vf);
450 gtk_tree_path_free(tpath);
455 file_util_rename(NULL, list, vf->listview);
458 void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
462 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
465 layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
469 vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled);
473 void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
477 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
481 void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
484 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
485 VFLIST(vf)->click_fd = NULL;
491 *-----------------------------------------------------------------------------
493 *-----------------------------------------------------------------------------
496 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
501 if (!new || !new[0]) return FALSE;
503 new_path = g_build_filename(vf->dir_fd->path, new, NULL);
505 if (strchr(new, G_DIR_SEPARATOR) != NULL)
507 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
508 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
513 gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL);
514 FileData *fd = file_data_new_simple(old_path); /* get the fd from cache */
515 file_util_rename_simple(fd, new_path, vf->listview);
525 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
533 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return;
534 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
535 tpath = gtk_tree_model_get_path(store, &iter);
536 tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
537 gtk_tree_path_free(tpath);
539 popup_menu_position_clamp(menu, x, y, 0);
542 gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
547 if (event->keyval != GDK_Menu) return FALSE;
549 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
555 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
556 gtk_tree_model_get_iter(store, &iter, tpath);
557 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
558 gtk_tree_path_free(tpath);
562 VFLIST(vf)->click_fd = NULL;
565 vf->popup = vf_pop_menu(vf);
566 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
571 gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
577 GtkTreeViewColumn *column;
579 vf->clicked_mark = 0;
581 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
582 &tpath, &column, NULL, NULL))
585 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
587 if (bevent->button == MOUSE_BUTTON_LEFT &&
588 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
591 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
592 vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
594 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
596 gtk_tree_model_get_iter(store, &iter, tpath);
597 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
599 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE);
601 gtk_tree_path_free(tpath);
604 VFLIST(vf)->click_fd = fd;
606 if (bevent->button == MOUSE_BUTTON_RIGHT)
608 vf->popup = vf_pop_menu(vf);
609 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
610 bevent->button, bevent->time);
614 if (!fd) return FALSE;
616 if (bevent->button == MOUSE_BUTTON_MIDDLE)
618 if (!vflist_row_is_selected(vf, fd))
620 vflist_color_set(vf, fd, TRUE);
626 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
627 !(bevent->state & GDK_SHIFT_MASK ) &&
628 !(bevent->state & GDK_CONTROL_MASK ) &&
629 vflist_row_is_selected(vf, fd))
631 GtkTreeSelection *selection;
633 gtk_widget_grab_focus(widget);
636 /* returning FALSE and further processing of the event is needed for
637 correct operation of the expander, to show the sidecar files.
638 It however resets the selection of multiple files. With this condition
639 it should work for both cases */
640 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
641 return (gtk_tree_selection_count_selected_rows(selection) > 1);
645 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
647 if (vf->layout) layout_image_full_screen_start(vf->layout);
654 gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
661 if (bevent->button == MOUSE_BUTTON_MIDDLE)
663 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
666 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
671 if ((bevent->x != 0 || bevent->y != 0) &&
672 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
673 &tpath, NULL, NULL, NULL))
677 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
678 gtk_tree_model_get_iter(store, &iter, tpath);
679 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
680 gtk_tree_path_free(tpath);
683 if (bevent->button == MOUSE_BUTTON_MIDDLE)
685 if (fd && VFLIST(vf)->click_fd == fd)
687 GtkTreeSelection *selection;
689 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
690 if (vflist_row_is_selected(vf, fd))
692 gtk_tree_selection_unselect_iter(selection, &iter);
696 gtk_tree_selection_select_iter(selection, &iter);
702 if (fd && VFLIST(vf)->click_fd == fd &&
703 !(bevent->state & GDK_SHIFT_MASK ) &&
704 !(bevent->state & GDK_CONTROL_MASK ) &&
705 vflist_row_is_selected(vf, fd))
707 GtkTreeSelection *selection;
709 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
710 gtk_tree_selection_unselect_all(selection);
711 gtk_tree_selection_select_iter(selection, &iter);
712 vflist_move_cursor(vf, &iter);
713 // return TRUE;// FIXME - expand
719 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
721 FileData *read_ahead_fd = NULL;
727 cur_fd = layout_image_get_fd(vf->layout);
728 if (sel_fd == cur_fd) return; /* no change */
730 row = g_list_index(vf->list, sel_fd);
731 // FIXME sidecar data
733 if (sel_fd && options->image.enable_read_ahead && row >= 0)
735 if (row > g_list_index(vf->list, cur_fd) &&
736 (guint) (row + 1) < vf_count(vf, NULL))
738 read_ahead_fd = vf_index_get_data(vf, row + 1);
742 read_ahead_fd = vf_index_get_data(vf, row - 1);
746 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
749 static gboolean vflist_select_idle_cb(gpointer data)
755 VFLIST(vf)->select_idle_id = 0;
761 if (VFLIST(vf)->select_fd)
763 vflist_select_image(vf, VFLIST(vf)->select_fd);
764 VFLIST(vf)->select_fd = NULL;
767 VFLIST(vf)->select_idle_id = 0;
771 static void vflist_select_idle_cancel(ViewFile *vf)
773 if (VFLIST(vf)->select_idle_id)
775 g_source_remove(VFLIST(vf)->select_idle_id);
776 VFLIST(vf)->select_idle_id = 0;
780 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
781 gboolean path_currently_selected, gpointer data)
786 if (!path_currently_selected &&
787 gtk_tree_model_get_iter(store, &iter, tpath))
789 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
793 VFLIST(vf)->select_fd = NULL;
797 !VFLIST(vf)->select_idle_id)
799 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
805 static void vflist_expand_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
808 vflist_set_expanded(vf, iter, TRUE);
811 static void vflist_collapse_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
814 vflist_set_expanded(vf, iter, FALSE);
818 *-----------------------------------------------------------------------------
820 *-----------------------------------------------------------------------------
824 static gboolean vflist_dummy_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
825 gboolean path_currently_selected, gpointer data)
831 static gchar* vflist_get_formatted(ViewFile *vf, const gchar *name, const gchar *sidecars, const gchar *size, const gchar *time, gboolean expanded)
833 gboolean multiline = vflist_is_multiline(vf);
838 text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time);
842 text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars);
847 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded)
856 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
858 gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
859 FILE_COLUMN_NAME, &name,
860 FILE_COLUMN_SIDECARS, &sidecars,
861 FILE_COLUMN_SIZE, &size,
862 FILE_COLUMN_DATE, &time,
864 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
866 gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED, formatted,
867 FILE_COLUMN_EXPANDED, expanded,
876 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
879 gchar *sidecars = NULL;
881 const gchar *time = text_from_time(fd->date);
882 gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
883 const gchar *disabled_grouping;
885 gboolean expanded = FALSE;
887 if (fd->sidecar_files) /* expanded has no effect on files without sidecars */
889 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_EXPANDED, &expanded, -1);
892 sidecars = file_data_sc_list_to_string(fd);
894 disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
895 name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
896 size = text_from_size(fd->size);
898 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
900 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
901 FILE_COLUMN_VERSION, fd->version,
902 FILE_COLUMN_THUMB, fd->thumb_pixbuf,
903 FILE_COLUMN_FORMATTED, formatted,
904 FILE_COLUMN_SIDECARS, sidecars,
905 FILE_COLUMN_NAME, name,
906 FILE_COLUMN_SIZE, size,
907 FILE_COLUMN_DATE, time,
908 #define STORE_SET_IS_SLOW 1
909 #if STORE_SET_IS_SLOW
910 /* this is 3x faster on a directory with 20000 files */
911 FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
912 FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
913 FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
914 FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
915 FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
916 FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
917 #if FILEDATA_MARKS_SIZE != 6
918 #error this needs to be updated
921 FILE_COLUMN_COLOR, FALSE, -1);
923 #if !STORE_SET_IS_SLOW
926 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
927 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
936 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected)
941 gint num_ordered = 0;
942 gint num_prepended = 0;
944 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
950 FileData *fd = work->data;
951 gboolean done = FALSE;
955 FileData *old_fd = NULL;
956 gint old_version = 0;
961 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
962 FILE_COLUMN_POINTER, &old_fd,
963 FILE_COLUMN_VERSION, &old_version,
973 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
975 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
977 if (match == 0) g_warning("multiple fd for the same path");
992 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
997 here should be used gtk_tree_store_append, but this function seems to be O(n)
998 and it seems to be much faster to add new entries to the beginning and reorder later
1001 gtk_tree_store_prepend(store, &new, parent_iter);
1004 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
1005 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected);
1007 if (g_list_find(selected, fd))
1009 /* renamed files - the same fd appears at different position - select it again*/
1010 GtkTreeSelection *selection;
1011 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1012 gtk_tree_selection_select_iter(selection, &new);
1019 file_data_unref(old_fd);
1020 valid = gtk_tree_store_remove(store, &iter);
1024 if (fd->version != old_version)
1026 vflist_setup_iter(vf, store, &iter, fd);
1027 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected);
1030 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1041 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1042 file_data_unref(old_fd);
1044 valid = gtk_tree_store_remove(store, &iter);
1047 /* move the prepended entries to the correct position */
1051 gint num_total = num_prepended + num_ordered;
1052 gint *new_order = g_malloc(num_total * sizeof(gint));
1054 for (i = 0; i < num_total; i++)
1056 if (i < num_ordered)
1057 new_order[i] = num_prepended + i;
1059 new_order[i] = num_total - 1 - i;
1061 gtk_tree_store_reorder(store, parent_iter, new_order);
1067 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
1070 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
1072 GtkTreeStore *store;
1075 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1076 if (!vf->list) return;
1082 FileData *fd = work->data;
1083 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1088 vf->sort_method = type;
1089 vf->sort_ascend = ascend;
1091 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1093 new_order = g_malloc(i * sizeof(gint));
1099 FileData *fd = work->data;
1100 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1105 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1106 gtk_tree_store_reorder(store, NULL, new_order);
1109 g_hash_table_destroy(fd_idx_hash);
1113 *-----------------------------------------------------------------------------
1115 *-----------------------------------------------------------------------------
1119 void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1124 FileData *fd = work->data;
1127 if (fd->thumb_pixbuf) (*done)++;
1129 if (fd->sidecar_files)
1131 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1137 void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
1139 GtkTreeStore *store;
1142 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1144 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1145 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1148 FileData *vflist_thumb_next_fd(ViewFile *vf)
1151 FileData *fd = NULL;
1153 /* first check the visible files */
1155 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1157 GtkTreeModel *store;
1159 gboolean valid = TRUE;
1161 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1162 gtk_tree_model_get_iter(store, &iter, tpath);
1163 gtk_tree_path_free(tpath);
1165 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1169 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1171 if (!nfd->thumb_pixbuf) fd = nfd;
1173 valid = gtk_tree_model_iter_next(store, &iter);
1177 /* then find first undone */
1181 GList *work = vf->list;
1184 FileData *fd_p = work->data;
1185 if (!fd_p->thumb_pixbuf)
1189 GList *work2 = fd_p->sidecar_files;
1191 while (work2 && !fd)
1194 if (!fd_p->thumb_pixbuf) fd = fd_p;
1195 work2 = work2->next;
1206 void vflist_thumb_reset_all(ViewFile *vf)
1208 GList *work = vf->list;
1211 FileData *fd = work->data;
1212 if (fd->thumb_pixbuf)
1214 g_object_unref(fd->thumb_pixbuf);
1215 fd->thumb_pixbuf = NULL;
1222 *-----------------------------------------------------------------------------
1224 *-----------------------------------------------------------------------------
1227 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1229 return g_list_nth_data(vf->list, row);
1232 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1235 GList *work, *work2;
1240 FileData *list_fd = work->data;
1241 if (list_fd == fd) return p;
1243 work2 = list_fd->sidecar_files;
1246 /* FIXME: return the same index also for sidecars
1247 it is sufficient for next/prev navigation but it should be rewritten
1248 without using indexes at all
1250 FileData *sidecar_fd = work2->data;
1251 if (sidecar_fd == fd) return p;
1252 work2 = work2->next;
1262 guint vflist_count(ViewFile *vf, gint64 *bytes)
1272 FileData *fd = work->data;
1280 return g_list_length(vf->list);
1283 GList *vflist_get_list(ViewFile *vf)
1291 FileData *fd = work->data;
1294 list = g_list_prepend(list, file_data_ref(fd));
1297 return g_list_reverse(list);
1301 *-----------------------------------------------------------------------------
1303 *-----------------------------------------------------------------------------
1306 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1308 GtkTreeModel *store;
1309 GtkTreeSelection *selection;
1312 gboolean found = FALSE;
1314 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1315 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1317 while (!found && work)
1319 GtkTreePath *tpath = work->data;
1323 gtk_tree_model_get_iter(store, &iter, tpath);
1324 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1325 if (fd_n == fd) found = TRUE;
1328 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1334 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1338 fd = vf_index_get_data(vf, row);
1339 return vflist_row_is_selected(vf, fd);
1342 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1344 GtkTreeModel *store;
1345 GtkTreeSelection *selection;
1349 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1350 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1360 GtkTreePath *tpath = work->data;
1364 gtk_tree_model_get_iter(store, &iter, tpath);
1365 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1374 count = g_list_length(slist);
1375 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1381 GList *vflist_selection_get_list(ViewFile *vf)
1383 GtkTreeModel *store;
1384 GtkTreeSelection *selection;
1389 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1390 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1394 GtkTreePath *tpath = work->data;
1398 gtk_tree_model_get_iter(store, &iter, tpath);
1399 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1401 list = g_list_prepend(list, file_data_ref(fd));
1403 if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
1405 /* unexpanded - add whole group */
1406 GList *work2 = fd->sidecar_files;
1409 FileData *sfd = work2->data;
1410 list = g_list_prepend(list, file_data_ref(sfd));
1411 work2 = work2->next;
1417 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1420 return g_list_reverse(list);
1423 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1425 GtkTreeModel *store;
1426 GtkTreeSelection *selection;
1431 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1432 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1436 GtkTreePath *tpath = work->data;
1440 gtk_tree_model_get_iter(store, &iter, tpath);
1441 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1443 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1447 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1450 return g_list_reverse(list);
1453 void vflist_select_all(ViewFile *vf)
1455 GtkTreeSelection *selection;
1457 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1458 gtk_tree_selection_select_all(selection);
1460 VFLIST(vf)->select_fd = NULL;
1463 void vflist_select_none(ViewFile *vf)
1465 GtkTreeSelection *selection;
1467 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1468 gtk_tree_selection_unselect_all(selection);
1471 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1476 tpath = gtk_tree_model_get_path(store, iter);
1477 result = gtk_tree_path_prev(tpath);
1479 gtk_tree_model_get_iter(store, iter, tpath);
1481 gtk_tree_path_free(tpath);
1486 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1488 if (!gtk_tree_model_get_iter_first(store, iter))
1493 GtkTreeIter next = *iter;
1495 if (gtk_tree_model_iter_next(store, &next))
1504 void vflist_select_invert(ViewFile *vf)
1507 GtkTreeSelection *selection;
1508 GtkTreeModel *store;
1511 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1512 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1514 /* Backward iteration prevents scrolling to the end of the list,
1515 * it scrolls to the first selected row instead. */
1516 valid = tree_model_get_iter_last(store, &iter);
1520 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1523 gtk_tree_selection_unselect_iter(selection, &iter);
1525 gtk_tree_selection_select_iter(selection, &iter);
1527 valid = tree_model_iter_prev(store, &iter);
1531 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1535 if (vflist_find_row(vf, fd, &iter) < 0) return;
1537 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1539 if (!vflist_row_is_selected(vf, fd))
1541 GtkTreeSelection *selection;
1542 GtkTreeModel *store;
1545 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1546 gtk_tree_selection_unselect_all(selection);
1547 gtk_tree_selection_select_iter(selection, &iter);
1548 vflist_move_cursor(vf, &iter);
1550 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1551 tpath = gtk_tree_model_get_path(store, &iter);
1552 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1553 gtk_tree_path_free(tpath);
1557 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1560 FileData *fd = NULL;
1562 if (sel_fd->parent) sel_fd = sel_fd->parent;
1571 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1573 if (match >= 0) break;
1576 if (fd) vflist_select_by_fd(vf, fd);
1580 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1582 GtkTreeModel *store;
1584 GtkTreeSelection *selection;
1588 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1590 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1591 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1593 valid = gtk_tree_model_get_iter_first(store, &iter);
1597 gboolean mark_val, selected;
1598 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1600 mark_val = file_data_get_mark(fd, n);
1601 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1605 case MTS_MODE_SET: selected = mark_val;
1607 case MTS_MODE_OR: selected = mark_val | selected;
1609 case MTS_MODE_AND: selected = mark_val & selected;
1611 case MTS_MODE_MINUS: selected = !mark_val & selected;
1616 gtk_tree_selection_select_iter(selection, &iter);
1618 gtk_tree_selection_unselect_iter(selection, &iter);
1620 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1624 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1626 GtkTreeModel *store;
1627 GtkTreeSelection *selection;
1632 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1634 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1635 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1639 GtkTreePath *tpath = work->data;
1643 gtk_tree_model_get_iter(store, &iter, tpath);
1644 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1646 /* the change has a very limited range and the standard notification would trigger
1647 complete re-read of the directory - try to do only minimal update instead */
1648 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1652 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1654 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1656 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1660 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1662 vf_refresh_idle(vf);
1666 /* mark functions can have various side effects - update all columns to be sure */
1667 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1668 /* mark functions can change sidecars too */
1669 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL);
1673 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1677 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1682 *-----------------------------------------------------------------------------
1684 *-----------------------------------------------------------------------------
1687 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
1689 GtkTreeViewColumn *column;
1690 GtkCellRenderer *cell;
1693 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1694 if (!column) return;
1696 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1698 list = gtk_tree_view_column_get_cell_renderers(column);
1703 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1704 gtk_tree_view_column_set_visible(column, thumb);
1706 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1707 if (!column) return;
1708 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1710 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1711 if (!column) return;
1712 gtk_tree_view_column_set_visible(column, !multiline);
1714 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1715 if (!column) return;
1716 gtk_tree_view_column_set_visible(column, !multiline);
1718 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1721 static gboolean vflist_is_multiline(ViewFile *vf)
1723 return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
1727 static void vflist_populate_view(ViewFile *vf)
1729 GtkTreeStore *store;
1732 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1738 vflist_store_clear(vf);
1743 vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
1745 selected = vflist_selection_get_list(vf);
1747 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected);
1749 if (selected && vflist_selection_count(vf, NULL) == 0)
1751 /* all selected files disappeared */
1752 vflist_select_closest(vf, selected->data);
1755 filelist_free(selected);
1758 vf_thumb_update(vf);
1761 gboolean vflist_refresh(ViewFile *vf)
1764 gboolean ret = TRUE;
1766 old_list = vf->list;
1769 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1772 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1774 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1775 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1776 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1778 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1779 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1782 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1784 vflist_populate_view(vf);
1786 filelist_free(old_list);
1787 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1794 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1796 #define CELL_HEIGHT_OVERRIDE 512
1798 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1802 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1803 if (spec && G_IS_PARAM_SPEC_INT(spec))
1805 GParamSpecInt *spec_int;
1807 spec_int = G_PARAM_SPEC_INT(spec);
1808 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1812 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1814 static GdkColor color;
1815 static GtkWidget *done = NULL;
1821 style = gtk_widget_get_style(widget);
1822 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1823 shift_color(&color, -1, 0);
1830 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1831 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1833 ViewFile *vf = data;
1836 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1837 g_object_set(G_OBJECT(cell),
1838 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1839 "cell-background-set", set, NULL);
1842 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1844 GtkTreeViewColumn *column;
1845 GtkCellRenderer *renderer;
1847 column = gtk_tree_view_column_new();
1848 gtk_tree_view_column_set_title(column, title);
1849 gtk_tree_view_column_set_min_width(column, 4);
1853 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1854 renderer = gtk_cell_renderer_text_new();
1857 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1859 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1860 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1862 gtk_tree_view_column_set_expand(column, TRUE);
1866 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1867 renderer = gtk_cell_renderer_pixbuf_new();
1868 cell_renderer_height_override(renderer);
1869 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1870 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1873 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1874 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1875 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1877 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1880 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1882 ViewFile *vf = data;
1883 GtkTreeStore *store;
1884 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1890 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1891 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1894 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1896 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1898 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1901 /* the change has a very limited range and the standard notification would trigger
1902 complete re-read of the directory - try to do only minimal update instead */
1903 file_data_unregister_notify_func(vf_notify_cb, vf);
1904 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1905 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1907 vf_refresh_idle(vf);
1911 /* mark functions can have various side effects - update all columns to be sure */
1912 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1913 /* mark functions can change sidecars too */
1914 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL);
1916 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1918 gtk_tree_path_free(path);
1921 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1923 GtkTreeViewColumn *column;
1924 GtkCellRenderer *renderer;
1925 GtkTreeStore *store;
1928 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1930 renderer = gtk_cell_renderer_toggle_new();
1931 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1933 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1934 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1935 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1937 index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1938 gtk_tree_view_column_set_fixed_width(column, 22);
1939 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1942 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1946 *-----------------------------------------------------------------------------
1948 *-----------------------------------------------------------------------------
1951 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1953 if (!dir_fd) return FALSE;
1954 if (vf->dir_fd == dir_fd) return TRUE;
1956 file_data_unref(vf->dir_fd);
1957 vf->dir_fd = file_data_ref(dir_fd);
1959 /* force complete reload */
1960 vflist_store_clear(vf);
1962 filelist_free(vf->list);
1965 return vf_refresh(vf);
1968 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1970 ViewFile *vf = data;
1972 file_data_unregister_notify_func(vf_notify_cb, vf);
1974 vflist_select_idle_cancel(vf);
1975 vf_refresh_idle_cancel(vf);
1978 filelist_free(vf->list);
1981 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1983 GtkTreeStore *store;
1984 GtkTreeSelection *selection;
1985 GType flist_types[FILE_COLUMN_COUNT];
1989 vf->info = g_new0(ViewFileInfoList, 1);
1991 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1992 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1993 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1994 flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING;
1995 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1996 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
1997 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1998 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1999 flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN;
2000 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
2001 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
2002 flist_types[i] = G_TYPE_BOOLEAN;
2004 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
2006 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2007 g_object_unref(store);
2009 g_signal_connect(G_OBJECT(vf->listview), "row-expanded",
2010 G_CALLBACK(vflist_expand_cb), vf);
2012 g_signal_connect(G_OBJECT(vf->listview), "row-collapsed",
2013 G_CALLBACK(vflist_collapse_cb), vf);
2015 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2016 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
2017 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
2019 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2020 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2024 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2026 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2027 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
2031 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2032 g_assert(column == FILE_VIEW_COLUMN_THUMB);
2035 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE);
2036 g_assert(column == FILE_VIEW_COLUMN_FORMATTED);
2039 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2040 g_assert(column == FILE_VIEW_COLUMN_SIZE);
2043 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2044 g_assert(column == FILE_VIEW_COLUMN_DATE);
2047 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2051 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2053 if (VFLIST(vf)->thumbs_enabled == enable) return;
2055 VFLIST(vf)->thumbs_enabled = enable;
2056 if (vf->layout) vf_refresh(vf);
2059 void vflist_marks_set(ViewFile *vf, gboolean enable)
2061 GList *columns, *work;
2063 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2068 GtkTreeViewColumn *column = work->data;
2069 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2072 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2073 gtk_tree_view_column_set_visible(column, enable);
2076 g_list_free(columns);
2080 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */