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, 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);
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);
479 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
482 void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
485 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
486 VFLIST(vf)->click_fd = NULL;
492 *-----------------------------------------------------------------------------
494 *-----------------------------------------------------------------------------
497 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
502 if (!new || !new[0]) return FALSE;
504 new_path = g_build_filename(vf->dir_fd->path, new, NULL);
506 if (strchr(new, G_DIR_SEPARATOR) != NULL)
508 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
509 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
514 gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL);
515 FileData *fd = file_data_new_simple(old_path); /* get the fd from cache */
516 file_util_rename_simple(fd, new_path, vf->listview);
526 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
534 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return;
535 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
536 tpath = gtk_tree_model_get_path(store, &iter);
537 tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
538 gtk_tree_path_free(tpath);
540 popup_menu_position_clamp(menu, x, y, 0);
543 gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
548 if (event->keyval != GDK_Menu) return FALSE;
550 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
556 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
557 gtk_tree_model_get_iter(store, &iter, tpath);
558 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
559 gtk_tree_path_free(tpath);
563 VFLIST(vf)->click_fd = NULL;
566 vf->popup = vf_pop_menu(vf);
567 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
572 gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
578 GtkTreeViewColumn *column;
580 vf->clicked_mark = 0;
582 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
583 &tpath, &column, NULL, NULL))
586 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
588 if (bevent->button == MOUSE_BUTTON_LEFT &&
589 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
592 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
593 vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
595 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
597 gtk_tree_model_get_iter(store, &iter, tpath);
598 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
600 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE);
602 gtk_tree_path_free(tpath);
605 VFLIST(vf)->click_fd = fd;
607 if (bevent->button == MOUSE_BUTTON_RIGHT)
609 vf->popup = vf_pop_menu(vf);
610 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
611 bevent->button, bevent->time);
615 if (!fd) return FALSE;
617 if (bevent->button == MOUSE_BUTTON_MIDDLE)
619 if (!vflist_row_is_selected(vf, fd))
621 vflist_color_set(vf, fd, TRUE);
627 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
628 !(bevent->state & GDK_SHIFT_MASK ) &&
629 !(bevent->state & GDK_CONTROL_MASK ) &&
630 vflist_row_is_selected(vf, fd))
632 GtkTreeSelection *selection;
634 gtk_widget_grab_focus(widget);
637 /* returning FALSE and further processing of the event is needed for
638 correct operation of the expander, to show the sidecar files.
639 It however resets the selection of multiple files. With this condition
640 it should work for both cases */
641 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
642 return (gtk_tree_selection_count_selected_rows(selection) > 1);
646 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
648 if (vf->layout) layout_image_full_screen_start(vf->layout);
655 gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
662 if (bevent->button == MOUSE_BUTTON_MIDDLE)
664 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
667 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
672 if ((bevent->x != 0 || bevent->y != 0) &&
673 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
674 &tpath, NULL, NULL, NULL))
678 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
679 gtk_tree_model_get_iter(store, &iter, tpath);
680 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
681 gtk_tree_path_free(tpath);
684 if (bevent->button == MOUSE_BUTTON_MIDDLE)
686 if (fd && VFLIST(vf)->click_fd == fd)
688 GtkTreeSelection *selection;
690 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
691 if (vflist_row_is_selected(vf, fd))
693 gtk_tree_selection_unselect_iter(selection, &iter);
697 gtk_tree_selection_select_iter(selection, &iter);
703 if (fd && VFLIST(vf)->click_fd == fd &&
704 !(bevent->state & GDK_SHIFT_MASK ) &&
705 !(bevent->state & GDK_CONTROL_MASK ) &&
706 vflist_row_is_selected(vf, fd))
708 GtkTreeSelection *selection;
710 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
711 gtk_tree_selection_unselect_all(selection);
712 gtk_tree_selection_select_iter(selection, &iter);
713 vflist_move_cursor(vf, &iter);
714 // return TRUE;// FIXME - expand
720 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
722 FileData *read_ahead_fd = NULL;
728 cur_fd = layout_image_get_fd(vf->layout);
729 if (sel_fd == cur_fd) return; /* no change */
731 row = g_list_index(vf->list, sel_fd);
732 // FIXME sidecar data
734 if (sel_fd && options->image.enable_read_ahead && row >= 0)
736 if (row > g_list_index(vf->list, cur_fd) &&
737 (guint) (row + 1) < vf_count(vf, NULL))
739 read_ahead_fd = vf_index_get_data(vf, row + 1);
743 read_ahead_fd = vf_index_get_data(vf, row - 1);
747 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
750 static gboolean vflist_select_idle_cb(gpointer data)
756 VFLIST(vf)->select_idle_id = 0;
762 if (VFLIST(vf)->select_fd)
764 vflist_select_image(vf, VFLIST(vf)->select_fd);
765 VFLIST(vf)->select_fd = NULL;
768 VFLIST(vf)->select_idle_id = 0;
772 static void vflist_select_idle_cancel(ViewFile *vf)
774 if (VFLIST(vf)->select_idle_id)
776 g_source_remove(VFLIST(vf)->select_idle_id);
777 VFLIST(vf)->select_idle_id = 0;
781 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
782 gboolean path_currently_selected, gpointer data)
787 if (!path_currently_selected &&
788 gtk_tree_model_get_iter(store, &iter, tpath))
790 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
794 VFLIST(vf)->select_fd = NULL;
798 !VFLIST(vf)->select_idle_id)
800 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
806 static void vflist_expand_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
809 vflist_set_expanded(vf, iter, TRUE);
812 static void vflist_collapse_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
815 vflist_set_expanded(vf, iter, FALSE);
819 *-----------------------------------------------------------------------------
821 *-----------------------------------------------------------------------------
825 static gboolean vflist_dummy_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
826 gboolean path_currently_selected, gpointer data)
832 static gchar* vflist_get_formatted(ViewFile *vf, const gchar *name, const gchar *sidecars, const gchar *size, const gchar *time, gboolean expanded)
834 gboolean multiline = vflist_is_multiline(vf);
839 text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time);
843 text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars);
848 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded)
857 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
859 gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
860 FILE_COLUMN_NAME, &name,
861 FILE_COLUMN_SIDECARS, &sidecars,
862 FILE_COLUMN_SIZE, &size,
863 FILE_COLUMN_DATE, &time,
865 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
867 gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED, formatted,
868 FILE_COLUMN_EXPANDED, expanded,
877 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
880 gchar *sidecars = NULL;
882 const gchar *time = text_from_time(fd->date);
883 gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
884 const gchar *disabled_grouping;
886 gboolean expanded = FALSE;
888 if (fd->sidecar_files) /* expanded has no effect on files without sidecars */
890 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_EXPANDED, &expanded, -1);
893 sidecars = file_data_sc_list_to_string(fd);
895 disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
896 name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
897 size = text_from_size(fd->size);
899 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
901 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
902 FILE_COLUMN_VERSION, fd->version,
903 FILE_COLUMN_THUMB, fd->thumb_pixbuf,
904 FILE_COLUMN_FORMATTED, formatted,
905 FILE_COLUMN_SIDECARS, sidecars,
906 FILE_COLUMN_NAME, name,
907 FILE_COLUMN_SIZE, size,
908 FILE_COLUMN_DATE, time,
909 #define STORE_SET_IS_SLOW 1
910 #if STORE_SET_IS_SLOW
911 /* this is 3x faster on a directory with 20000 files */
912 FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
913 FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
914 FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
915 FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
916 FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
917 FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
918 #if FILEDATA_MARKS_SIZE != 6
919 #error this needs to be updated
922 FILE_COLUMN_COLOR, FALSE, -1);
924 #if !STORE_SET_IS_SLOW
927 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
928 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
937 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected, gboolean force)
942 gint num_ordered = 0;
943 gint num_prepended = 0;
945 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
951 FileData *fd = work->data;
952 gboolean done = FALSE;
956 FileData *old_fd = NULL;
957 gint old_version = 0;
962 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
963 FILE_COLUMN_POINTER, &old_fd,
964 FILE_COLUMN_VERSION, &old_version,
974 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
976 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
978 if (match == 0) g_warning("multiple fd for the same path");
993 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
998 here should be used gtk_tree_store_append, but this function seems to be O(n)
999 and it seems to be much faster to add new entries to the beginning and reorder later
1002 gtk_tree_store_prepend(store, &new, parent_iter);
1005 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
1006 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected, force);
1008 if (g_list_find(selected, fd))
1010 /* renamed files - the same fd appears at different position - select it again*/
1011 GtkTreeSelection *selection;
1012 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1013 gtk_tree_selection_select_iter(selection, &new);
1020 file_data_unref(old_fd);
1021 valid = gtk_tree_store_remove(store, &iter);
1025 if (fd->version != old_version || force)
1027 vflist_setup_iter(vf, store, &iter, fd);
1028 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected, force);
1031 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1042 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1043 file_data_unref(old_fd);
1045 valid = gtk_tree_store_remove(store, &iter);
1048 /* move the prepended entries to the correct position */
1052 gint num_total = num_prepended + num_ordered;
1053 gint *new_order = g_malloc(num_total * sizeof(gint));
1055 for (i = 0; i < num_total; i++)
1057 if (i < num_ordered)
1058 new_order[i] = num_prepended + i;
1060 new_order[i] = num_total - 1 - i;
1062 gtk_tree_store_reorder(store, parent_iter, new_order);
1068 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
1071 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
1073 GtkTreeStore *store;
1076 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1077 if (!vf->list) return;
1083 FileData *fd = work->data;
1084 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1089 vf->sort_method = type;
1090 vf->sort_ascend = ascend;
1092 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1094 new_order = g_malloc(i * sizeof(gint));
1100 FileData *fd = work->data;
1101 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1106 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1107 gtk_tree_store_reorder(store, NULL, new_order);
1110 g_hash_table_destroy(fd_idx_hash);
1114 *-----------------------------------------------------------------------------
1116 *-----------------------------------------------------------------------------
1120 void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1125 FileData *fd = work->data;
1128 if (fd->thumb_pixbuf) (*done)++;
1130 if (fd->sidecar_files)
1132 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1138 void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
1140 GtkTreeStore *store;
1143 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1145 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1146 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1149 FileData *vflist_thumb_next_fd(ViewFile *vf)
1152 FileData *fd = NULL;
1154 /* first check the visible files */
1156 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1158 GtkTreeModel *store;
1160 gboolean valid = TRUE;
1162 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1163 gtk_tree_model_get_iter(store, &iter, tpath);
1164 gtk_tree_path_free(tpath);
1166 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1170 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1172 if (!nfd->thumb_pixbuf) fd = nfd;
1174 valid = gtk_tree_model_iter_next(store, &iter);
1178 /* then find first undone */
1182 GList *work = vf->list;
1185 FileData *fd_p = work->data;
1186 if (!fd_p->thumb_pixbuf)
1190 GList *work2 = fd_p->sidecar_files;
1192 while (work2 && !fd)
1195 if (!fd_p->thumb_pixbuf) fd = fd_p;
1196 work2 = work2->next;
1207 void vflist_thumb_reset_all(ViewFile *vf)
1209 GList *work = vf->list;
1212 FileData *fd = work->data;
1213 if (fd->thumb_pixbuf)
1215 g_object_unref(fd->thumb_pixbuf);
1216 fd->thumb_pixbuf = NULL;
1223 *-----------------------------------------------------------------------------
1225 *-----------------------------------------------------------------------------
1228 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1230 return g_list_nth_data(vf->list, row);
1233 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1236 GList *work, *work2;
1241 FileData *list_fd = work->data;
1242 if (list_fd == fd) return p;
1244 work2 = list_fd->sidecar_files;
1247 /* FIXME: return the same index also for sidecars
1248 it is sufficient for next/prev navigation but it should be rewritten
1249 without using indexes at all
1251 FileData *sidecar_fd = work2->data;
1252 if (sidecar_fd == fd) return p;
1253 work2 = work2->next;
1263 guint vflist_count(ViewFile *vf, gint64 *bytes)
1273 FileData *fd = work->data;
1281 return g_list_length(vf->list);
1284 GList *vflist_get_list(ViewFile *vf)
1292 FileData *fd = work->data;
1295 list = g_list_prepend(list, file_data_ref(fd));
1298 return g_list_reverse(list);
1302 *-----------------------------------------------------------------------------
1304 *-----------------------------------------------------------------------------
1307 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1309 GtkTreeModel *store;
1310 GtkTreeSelection *selection;
1313 gboolean found = FALSE;
1315 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1316 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1318 while (!found && work)
1320 GtkTreePath *tpath = work->data;
1324 gtk_tree_model_get_iter(store, &iter, tpath);
1325 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1326 if (fd_n == fd) found = TRUE;
1329 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1335 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1339 fd = vf_index_get_data(vf, row);
1340 return vflist_row_is_selected(vf, fd);
1343 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1345 GtkTreeModel *store;
1346 GtkTreeSelection *selection;
1350 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1351 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1361 GtkTreePath *tpath = work->data;
1365 gtk_tree_model_get_iter(store, &iter, tpath);
1366 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1375 count = g_list_length(slist);
1376 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1382 GList *vflist_selection_get_list(ViewFile *vf)
1384 GtkTreeModel *store;
1385 GtkTreeSelection *selection;
1390 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1391 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1395 GtkTreePath *tpath = work->data;
1399 gtk_tree_model_get_iter(store, &iter, tpath);
1400 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1402 list = g_list_prepend(list, file_data_ref(fd));
1404 if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
1406 /* unexpanded - add whole group */
1407 GList *work2 = fd->sidecar_files;
1410 FileData *sfd = work2->data;
1411 list = g_list_prepend(list, file_data_ref(sfd));
1412 work2 = work2->next;
1418 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1421 return g_list_reverse(list);
1424 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1426 GtkTreeModel *store;
1427 GtkTreeSelection *selection;
1432 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1433 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1437 GtkTreePath *tpath = work->data;
1441 gtk_tree_model_get_iter(store, &iter, tpath);
1442 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1444 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1448 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1451 return g_list_reverse(list);
1454 void vflist_select_all(ViewFile *vf)
1456 GtkTreeSelection *selection;
1458 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1459 gtk_tree_selection_select_all(selection);
1461 VFLIST(vf)->select_fd = NULL;
1464 void vflist_select_none(ViewFile *vf)
1466 GtkTreeSelection *selection;
1468 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1469 gtk_tree_selection_unselect_all(selection);
1472 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1477 tpath = gtk_tree_model_get_path(store, iter);
1478 result = gtk_tree_path_prev(tpath);
1480 gtk_tree_model_get_iter(store, iter, tpath);
1482 gtk_tree_path_free(tpath);
1487 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1489 if (!gtk_tree_model_get_iter_first(store, iter))
1494 GtkTreeIter next = *iter;
1496 if (gtk_tree_model_iter_next(store, &next))
1505 void vflist_select_invert(ViewFile *vf)
1508 GtkTreeSelection *selection;
1509 GtkTreeModel *store;
1512 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1513 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1515 /* Backward iteration prevents scrolling to the end of the list,
1516 * it scrolls to the first selected row instead. */
1517 valid = tree_model_get_iter_last(store, &iter);
1521 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1524 gtk_tree_selection_unselect_iter(selection, &iter);
1526 gtk_tree_selection_select_iter(selection, &iter);
1528 valid = tree_model_iter_prev(store, &iter);
1532 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1536 if (vflist_find_row(vf, fd, &iter) < 0) return;
1538 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1540 if (!vflist_row_is_selected(vf, fd))
1542 GtkTreeSelection *selection;
1543 GtkTreeModel *store;
1546 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1547 gtk_tree_selection_unselect_all(selection);
1548 gtk_tree_selection_select_iter(selection, &iter);
1549 vflist_move_cursor(vf, &iter);
1551 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1552 tpath = gtk_tree_model_get_path(store, &iter);
1553 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1554 gtk_tree_path_free(tpath);
1558 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1561 FileData *fd = NULL;
1563 if (sel_fd->parent) sel_fd = sel_fd->parent;
1572 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1574 if (match >= 0) break;
1577 if (fd) vflist_select_by_fd(vf, fd);
1581 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1583 GtkTreeModel *store;
1585 GtkTreeSelection *selection;
1589 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1591 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1592 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1594 valid = gtk_tree_model_get_iter_first(store, &iter);
1598 gboolean mark_val, selected;
1599 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1601 mark_val = file_data_get_mark(fd, n);
1602 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1606 case MTS_MODE_SET: selected = mark_val;
1608 case MTS_MODE_OR: selected = mark_val | selected;
1610 case MTS_MODE_AND: selected = mark_val & selected;
1612 case MTS_MODE_MINUS: selected = !mark_val & selected;
1617 gtk_tree_selection_select_iter(selection, &iter);
1619 gtk_tree_selection_unselect_iter(selection, &iter);
1621 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1625 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1627 GtkTreeModel *store;
1628 GtkTreeSelection *selection;
1633 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1635 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1636 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1640 GtkTreePath *tpath = work->data;
1644 gtk_tree_model_get_iter(store, &iter, tpath);
1645 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1647 /* the change has a very limited range and the standard notification would trigger
1648 complete re-read of the directory - try to do only minimal update instead */
1649 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1653 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1655 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1657 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1661 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1663 vf_refresh_idle(vf);
1667 /* mark functions can have various side effects - update all columns to be sure */
1668 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1669 /* mark functions can change sidecars too */
1670 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1674 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1678 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1683 *-----------------------------------------------------------------------------
1685 *-----------------------------------------------------------------------------
1688 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
1690 GtkTreeViewColumn *column;
1691 GtkCellRenderer *cell;
1694 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1695 if (!column) return;
1697 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1699 list = gtk_tree_view_column_get_cell_renderers(column);
1704 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1705 gtk_tree_view_column_set_visible(column, thumb);
1707 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1708 if (!column) return;
1709 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1711 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1712 if (!column) return;
1713 gtk_tree_view_column_set_visible(column, !multiline);
1715 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1716 if (!column) return;
1717 gtk_tree_view_column_set_visible(column, !multiline);
1720 static gboolean vflist_is_multiline(ViewFile *vf)
1722 return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
1726 static void vflist_populate_view(ViewFile *vf, gboolean force)
1728 GtkTreeStore *store;
1731 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1737 vflist_store_clear(vf);
1742 vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
1744 selected = vflist_selection_get_list(vf);
1746 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force);
1748 if (selected && vflist_selection_count(vf, NULL) == 0)
1750 /* all selected files disappeared */
1751 vflist_select_closest(vf, selected->data);
1754 filelist_free(selected);
1757 vf_thumb_update(vf);
1760 gboolean vflist_refresh(ViewFile *vf)
1763 gboolean ret = TRUE;
1765 old_list = vf->list;
1768 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1771 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1773 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1774 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1775 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1777 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1778 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1781 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1783 vflist_populate_view(vf, FALSE);
1785 filelist_free(old_list);
1786 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1793 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1795 #define CELL_HEIGHT_OVERRIDE 512
1797 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1801 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1802 if (spec && G_IS_PARAM_SPEC_INT(spec))
1804 GParamSpecInt *spec_int;
1806 spec_int = G_PARAM_SPEC_INT(spec);
1807 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1811 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1813 static GdkColor color;
1814 static GtkWidget *done = NULL;
1820 style = gtk_widget_get_style(widget);
1821 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1822 shift_color(&color, -1, 0);
1829 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1830 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1832 ViewFile *vf = data;
1835 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1836 g_object_set(G_OBJECT(cell),
1837 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1838 "cell-background-set", set, NULL);
1841 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1843 GtkTreeViewColumn *column;
1844 GtkCellRenderer *renderer;
1846 column = gtk_tree_view_column_new();
1847 gtk_tree_view_column_set_title(column, title);
1848 gtk_tree_view_column_set_min_width(column, 4);
1852 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1853 renderer = gtk_cell_renderer_text_new();
1856 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1858 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1859 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1861 gtk_tree_view_column_set_expand(column, TRUE);
1865 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1866 renderer = gtk_cell_renderer_pixbuf_new();
1867 cell_renderer_height_override(renderer);
1868 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1869 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1872 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1873 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1874 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1876 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1879 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1881 ViewFile *vf = data;
1882 GtkTreeStore *store;
1883 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1889 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1890 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1893 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1895 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1897 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1900 /* the change has a very limited range and the standard notification would trigger
1901 complete re-read of the directory - try to do only minimal update instead */
1902 file_data_unregister_notify_func(vf_notify_cb, vf);
1903 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1904 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1906 vf_refresh_idle(vf);
1910 /* mark functions can have various side effects - update all columns to be sure */
1911 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1912 /* mark functions can change sidecars too */
1913 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1915 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1917 gtk_tree_path_free(path);
1920 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1922 GtkTreeViewColumn *column;
1923 GtkCellRenderer *renderer;
1924 GtkTreeStore *store;
1927 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1929 renderer = gtk_cell_renderer_toggle_new();
1930 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1932 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1933 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1934 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1936 index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1937 gtk_tree_view_column_set_fixed_width(column, 22);
1938 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1941 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1945 *-----------------------------------------------------------------------------
1947 *-----------------------------------------------------------------------------
1950 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 ret = vf_refresh(vf);
1966 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
1970 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1972 ViewFile *vf = data;
1974 file_data_unregister_notify_func(vf_notify_cb, vf);
1976 vflist_select_idle_cancel(vf);
1977 vf_refresh_idle_cancel(vf);
1980 filelist_free(vf->list);
1983 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1985 GtkTreeStore *store;
1986 GtkTreeSelection *selection;
1987 GType flist_types[FILE_COLUMN_COUNT];
1991 vf->info = g_new0(ViewFileInfoList, 1);
1993 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1994 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1995 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1996 flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING;
1997 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1998 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
1999 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
2000 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
2001 flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN;
2002 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
2003 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
2004 flist_types[i] = G_TYPE_BOOLEAN;
2006 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
2008 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2009 g_object_unref(store);
2011 g_signal_connect(G_OBJECT(vf->listview), "row-expanded",
2012 G_CALLBACK(vflist_expand_cb), vf);
2014 g_signal_connect(G_OBJECT(vf->listview), "row-collapsed",
2015 G_CALLBACK(vflist_collapse_cb), vf);
2017 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2018 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
2019 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
2021 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2022 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2026 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2028 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2029 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
2033 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2034 g_assert(column == FILE_VIEW_COLUMN_THUMB);
2037 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE);
2038 g_assert(column == FILE_VIEW_COLUMN_FORMATTED);
2041 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2042 g_assert(column == FILE_VIEW_COLUMN_SIZE);
2045 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2046 g_assert(column == FILE_VIEW_COLUMN_DATE);
2049 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2053 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2055 if (VFLIST(vf)->thumbs_enabled == enable) return;
2057 VFLIST(vf)->thumbs_enabled = enable;
2059 /* vflist_populate_view is better than vf_refresh:
2060 - no need to re-read the directory
2061 - force update because the formatted string has changed
2063 if (vf->layout) vflist_populate_view(vf, TRUE);
2066 void vflist_marks_set(ViewFile *vf, gboolean enable)
2068 GList *columns, *work;
2070 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2075 GtkTreeViewColumn *column = work->data;
2076 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2079 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2080 gtk_tree_view_column_set_visible(column, enable);
2083 g_list_free(columns);
2087 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */