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_selection_get_one(ViewFile *vf, FileData *fd)
368 GList *list = g_list_append(NULL, file_data_ref(fd));
370 if (fd->sidecar_files)
372 /* check if the row is expanded */
376 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
377 if (vflist_find_row(vf, fd, &iter) >= 0)
381 tpath = gtk_tree_model_get_path(store, &iter);
382 if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
384 /* unexpanded - add whole group */
385 GList *work = fd->sidecar_files;
388 FileData *sfd = work->data;
389 list = g_list_prepend(list, file_data_ref(sfd));
393 gtk_tree_path_free(tpath);
395 list = g_list_reverse(list);
401 GList *vflist_pop_menu_file_list(ViewFile *vf)
403 if (!VFLIST(vf)->click_fd) return NULL;
405 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
407 return vf_selection_get_list(vf);
409 return vflist_selection_get_one(vf, VFLIST(vf)->click_fd);
413 void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
417 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
421 list = vf_selection_get_list(vf);
422 view_window_new_from_list(list);
427 view_window_new(VFLIST(vf)->click_fd);
431 void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
436 list = vf_pop_menu_file_list(vf);
437 if (options->file_ops.enable_in_place_rename &&
438 list && !list->next && VFLIST(vf)->click_fd)
445 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
446 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
450 tpath = gtk_tree_model_get_path(store, &iter);
451 tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
452 FILE_VIEW_COLUMN_FORMATTED, VFLIST(vf)->click_fd->name,
453 vflist_row_rename_cb, vf);
454 gtk_tree_path_free(tpath);
459 file_util_rename(NULL, list, vf->listview);
462 void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
466 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
469 layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
473 vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled);
477 void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
481 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
483 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
486 void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
489 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
490 VFLIST(vf)->click_fd = NULL;
496 *-----------------------------------------------------------------------------
498 *-----------------------------------------------------------------------------
501 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
506 if (!new || !new[0]) return FALSE;
508 new_path = g_build_filename(vf->dir_fd->path, new, NULL);
510 if (strchr(new, G_DIR_SEPARATOR) != NULL)
512 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
513 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
518 gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL);
519 FileData *fd = file_data_new_simple(old_path); /* get the fd from cache */
520 file_util_rename_simple(fd, new_path, vf->listview);
530 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
538 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return;
539 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
540 tpath = gtk_tree_model_get_path(store, &iter);
541 tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
542 gtk_tree_path_free(tpath);
544 popup_menu_position_clamp(menu, x, y, 0);
547 gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
552 if (event->keyval != GDK_Menu) return FALSE;
554 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
560 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
561 gtk_tree_model_get_iter(store, &iter, tpath);
562 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
563 gtk_tree_path_free(tpath);
567 VFLIST(vf)->click_fd = NULL;
570 vf->popup = vf_pop_menu(vf);
571 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
576 gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
582 GtkTreeViewColumn *column;
584 vf->clicked_mark = 0;
586 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
587 &tpath, &column, NULL, NULL))
590 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
592 if (bevent->button == MOUSE_BUTTON_LEFT &&
593 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
596 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
597 vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
599 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
601 gtk_tree_model_get_iter(store, &iter, tpath);
602 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
604 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE);
606 gtk_tree_path_free(tpath);
609 VFLIST(vf)->click_fd = fd;
611 if (bevent->button == MOUSE_BUTTON_RIGHT)
613 vf->popup = vf_pop_menu(vf);
614 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
615 bevent->button, bevent->time);
619 if (!fd) return FALSE;
621 if (bevent->button == MOUSE_BUTTON_MIDDLE)
623 if (!vflist_row_is_selected(vf, fd))
625 vflist_color_set(vf, fd, TRUE);
631 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
632 !(bevent->state & GDK_SHIFT_MASK ) &&
633 !(bevent->state & GDK_CONTROL_MASK ) &&
634 vflist_row_is_selected(vf, fd))
636 GtkTreeSelection *selection;
638 gtk_widget_grab_focus(widget);
641 /* returning FALSE and further processing of the event is needed for
642 correct operation of the expander, to show the sidecar files.
643 It however resets the selection of multiple files. With this condition
644 it should work for both cases */
645 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
646 return (gtk_tree_selection_count_selected_rows(selection) > 1);
650 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
652 if (vf->layout) layout_image_full_screen_start(vf->layout);
659 gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
666 if (bevent->button == MOUSE_BUTTON_MIDDLE)
668 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
671 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
676 if ((bevent->x != 0 || bevent->y != 0) &&
677 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
678 &tpath, NULL, NULL, NULL))
682 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
683 gtk_tree_model_get_iter(store, &iter, tpath);
684 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
685 gtk_tree_path_free(tpath);
688 if (bevent->button == MOUSE_BUTTON_MIDDLE)
690 if (fd && VFLIST(vf)->click_fd == fd)
692 GtkTreeSelection *selection;
694 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
695 if (vflist_row_is_selected(vf, fd))
697 gtk_tree_selection_unselect_iter(selection, &iter);
701 gtk_tree_selection_select_iter(selection, &iter);
707 if (fd && VFLIST(vf)->click_fd == fd &&
708 !(bevent->state & GDK_SHIFT_MASK ) &&
709 !(bevent->state & GDK_CONTROL_MASK ) &&
710 vflist_row_is_selected(vf, fd))
712 GtkTreeSelection *selection;
714 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
715 gtk_tree_selection_unselect_all(selection);
716 gtk_tree_selection_select_iter(selection, &iter);
717 vflist_move_cursor(vf, &iter);
718 // return TRUE;// FIXME - expand
724 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
726 FileData *read_ahead_fd = NULL;
732 cur_fd = layout_image_get_fd(vf->layout);
733 if (sel_fd == cur_fd) return; /* no change */
735 row = g_list_index(vf->list, sel_fd);
736 // FIXME sidecar data
738 if (sel_fd && options->image.enable_read_ahead && row >= 0)
740 if (row > g_list_index(vf->list, cur_fd) &&
741 (guint) (row + 1) < vf_count(vf, NULL))
743 read_ahead_fd = vf_index_get_data(vf, row + 1);
747 read_ahead_fd = vf_index_get_data(vf, row - 1);
751 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
754 static gboolean vflist_select_idle_cb(gpointer data)
760 VFLIST(vf)->select_idle_id = 0;
766 if (VFLIST(vf)->select_fd)
768 vflist_select_image(vf, VFLIST(vf)->select_fd);
769 VFLIST(vf)->select_fd = NULL;
772 VFLIST(vf)->select_idle_id = 0;
776 static void vflist_select_idle_cancel(ViewFile *vf)
778 if (VFLIST(vf)->select_idle_id)
780 g_source_remove(VFLIST(vf)->select_idle_id);
781 VFLIST(vf)->select_idle_id = 0;
785 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
786 gboolean path_currently_selected, gpointer data)
791 if (!path_currently_selected &&
792 gtk_tree_model_get_iter(store, &iter, tpath))
794 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
798 VFLIST(vf)->select_fd = NULL;
802 !VFLIST(vf)->select_idle_id)
804 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
810 static void vflist_expand_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
813 vflist_set_expanded(vf, iter, TRUE);
816 static void vflist_collapse_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
819 vflist_set_expanded(vf, iter, FALSE);
823 *-----------------------------------------------------------------------------
825 *-----------------------------------------------------------------------------
829 static gboolean vflist_dummy_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
830 gboolean path_currently_selected, gpointer data)
836 static gchar* vflist_get_formatted(ViewFile *vf, const gchar *name, const gchar *sidecars, const gchar *size, const gchar *time, gboolean expanded)
838 gboolean multiline = vflist_is_multiline(vf);
843 text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time);
847 text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars);
852 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded)
861 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
863 gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
864 FILE_COLUMN_NAME, &name,
865 FILE_COLUMN_SIDECARS, &sidecars,
866 FILE_COLUMN_SIZE, &size,
867 FILE_COLUMN_DATE, &time,
869 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
871 gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED, formatted,
872 FILE_COLUMN_EXPANDED, expanded,
881 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
884 gchar *sidecars = NULL;
886 const gchar *time = text_from_time(fd->date);
887 gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
888 const gchar *disabled_grouping;
890 gboolean expanded = FALSE;
892 if (fd->sidecar_files) /* expanded has no effect on files without sidecars */
894 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_EXPANDED, &expanded, -1);
897 sidecars = file_data_sc_list_to_string(fd);
899 disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
900 name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
901 size = text_from_size(fd->size);
903 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
905 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
906 FILE_COLUMN_VERSION, fd->version,
907 FILE_COLUMN_THUMB, fd->thumb_pixbuf,
908 FILE_COLUMN_FORMATTED, formatted,
909 FILE_COLUMN_SIDECARS, sidecars,
910 FILE_COLUMN_NAME, name,
911 FILE_COLUMN_SIZE, size,
912 FILE_COLUMN_DATE, time,
913 #define STORE_SET_IS_SLOW 1
914 #if STORE_SET_IS_SLOW
915 /* this is 3x faster on a directory with 20000 files */
916 FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
917 FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
918 FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
919 FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
920 FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
921 FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
922 #if FILEDATA_MARKS_SIZE != 6
923 #error this needs to be updated
926 FILE_COLUMN_COLOR, FALSE, -1);
928 #if !STORE_SET_IS_SLOW
931 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
932 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
941 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected, gboolean force)
946 gint num_ordered = 0;
947 gint num_prepended = 0;
949 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
955 FileData *fd = work->data;
956 gboolean done = FALSE;
960 FileData *old_fd = NULL;
961 gint old_version = 0;
966 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
967 FILE_COLUMN_POINTER, &old_fd,
968 FILE_COLUMN_VERSION, &old_version,
978 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
980 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
982 if (match == 0) g_warning("multiple fd for the same path");
997 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
1002 here should be used gtk_tree_store_append, but this function seems to be O(n)
1003 and it seems to be much faster to add new entries to the beginning and reorder later
1006 gtk_tree_store_prepend(store, &new, parent_iter);
1009 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
1010 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected, force);
1012 if (g_list_find(selected, fd))
1014 /* renamed files - the same fd appears at different position - select it again*/
1015 GtkTreeSelection *selection;
1016 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1017 gtk_tree_selection_select_iter(selection, &new);
1024 file_data_unref(old_fd);
1025 valid = gtk_tree_store_remove(store, &iter);
1029 if (fd->version != old_version || force)
1031 vflist_setup_iter(vf, store, &iter, fd);
1032 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected, force);
1035 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1046 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1047 file_data_unref(old_fd);
1049 valid = gtk_tree_store_remove(store, &iter);
1052 /* move the prepended entries to the correct position */
1056 gint num_total = num_prepended + num_ordered;
1057 gint *new_order = g_malloc(num_total * sizeof(gint));
1059 for (i = 0; i < num_total; i++)
1061 if (i < num_ordered)
1062 new_order[i] = num_prepended + i;
1064 new_order[i] = num_total - 1 - i;
1066 gtk_tree_store_reorder(store, parent_iter, new_order);
1072 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
1075 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
1077 GtkTreeStore *store;
1080 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1081 if (!vf->list) return;
1087 FileData *fd = work->data;
1088 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1093 vf->sort_method = type;
1094 vf->sort_ascend = ascend;
1096 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1098 new_order = g_malloc(i * sizeof(gint));
1104 FileData *fd = work->data;
1105 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1110 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1111 gtk_tree_store_reorder(store, NULL, new_order);
1114 g_hash_table_destroy(fd_idx_hash);
1118 *-----------------------------------------------------------------------------
1120 *-----------------------------------------------------------------------------
1124 void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1129 FileData *fd = work->data;
1132 if (fd->thumb_pixbuf) (*done)++;
1134 if (fd->sidecar_files)
1136 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1142 void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
1144 GtkTreeStore *store;
1147 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1149 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1150 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1153 FileData *vflist_thumb_next_fd(ViewFile *vf)
1156 FileData *fd = NULL;
1158 /* first check the visible files */
1160 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1162 GtkTreeModel *store;
1164 gboolean valid = TRUE;
1166 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1167 gtk_tree_model_get_iter(store, &iter, tpath);
1168 gtk_tree_path_free(tpath);
1170 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1174 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1176 if (!nfd->thumb_pixbuf) fd = nfd;
1178 valid = gtk_tree_model_iter_next(store, &iter);
1182 /* then find first undone */
1186 GList *work = vf->list;
1189 FileData *fd_p = work->data;
1190 if (!fd_p->thumb_pixbuf)
1194 GList *work2 = fd_p->sidecar_files;
1196 while (work2 && !fd)
1199 if (!fd_p->thumb_pixbuf) fd = fd_p;
1200 work2 = work2->next;
1211 void vflist_thumb_reset_all(ViewFile *vf)
1213 GList *work = vf->list;
1216 FileData *fd = work->data;
1217 if (fd->thumb_pixbuf)
1219 g_object_unref(fd->thumb_pixbuf);
1220 fd->thumb_pixbuf = NULL;
1227 *-----------------------------------------------------------------------------
1229 *-----------------------------------------------------------------------------
1232 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1234 return g_list_nth_data(vf->list, row);
1237 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1240 GList *work, *work2;
1245 FileData *list_fd = work->data;
1246 if (list_fd == fd) return p;
1248 work2 = list_fd->sidecar_files;
1251 /* FIXME: return the same index also for sidecars
1252 it is sufficient for next/prev navigation but it should be rewritten
1253 without using indexes at all
1255 FileData *sidecar_fd = work2->data;
1256 if (sidecar_fd == fd) return p;
1257 work2 = work2->next;
1267 guint vflist_count(ViewFile *vf, gint64 *bytes)
1277 FileData *fd = work->data;
1285 return g_list_length(vf->list);
1288 GList *vflist_get_list(ViewFile *vf)
1296 FileData *fd = work->data;
1299 list = g_list_prepend(list, file_data_ref(fd));
1302 return g_list_reverse(list);
1306 *-----------------------------------------------------------------------------
1308 *-----------------------------------------------------------------------------
1311 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1313 GtkTreeModel *store;
1314 GtkTreeSelection *selection;
1317 gboolean found = FALSE;
1319 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1320 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1322 while (!found && work)
1324 GtkTreePath *tpath = work->data;
1328 gtk_tree_model_get_iter(store, &iter, tpath);
1329 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1330 if (fd_n == fd) found = TRUE;
1333 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1339 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1343 fd = vf_index_get_data(vf, row);
1344 return vflist_row_is_selected(vf, fd);
1347 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1349 GtkTreeModel *store;
1350 GtkTreeSelection *selection;
1354 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1355 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1365 GtkTreePath *tpath = work->data;
1369 gtk_tree_model_get_iter(store, &iter, tpath);
1370 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1379 count = g_list_length(slist);
1380 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1386 GList *vflist_selection_get_list(ViewFile *vf)
1388 GtkTreeModel *store;
1389 GtkTreeSelection *selection;
1394 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1395 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1399 GtkTreePath *tpath = work->data;
1403 gtk_tree_model_get_iter(store, &iter, tpath);
1404 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1406 list = g_list_prepend(list, file_data_ref(fd));
1408 if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
1410 /* unexpanded - add whole group */
1411 GList *work2 = fd->sidecar_files;
1414 FileData *sfd = work2->data;
1415 list = g_list_prepend(list, file_data_ref(sfd));
1416 work2 = work2->next;
1422 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1425 return g_list_reverse(list);
1428 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1430 GtkTreeModel *store;
1431 GtkTreeSelection *selection;
1436 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1437 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1441 GtkTreePath *tpath = work->data;
1445 gtk_tree_model_get_iter(store, &iter, tpath);
1446 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1448 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1452 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1455 return g_list_reverse(list);
1458 void vflist_select_all(ViewFile *vf)
1460 GtkTreeSelection *selection;
1462 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1463 gtk_tree_selection_select_all(selection);
1465 VFLIST(vf)->select_fd = NULL;
1468 void vflist_select_none(ViewFile *vf)
1470 GtkTreeSelection *selection;
1472 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1473 gtk_tree_selection_unselect_all(selection);
1476 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1481 tpath = gtk_tree_model_get_path(store, iter);
1482 result = gtk_tree_path_prev(tpath);
1484 gtk_tree_model_get_iter(store, iter, tpath);
1486 gtk_tree_path_free(tpath);
1491 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1493 if (!gtk_tree_model_get_iter_first(store, iter))
1498 GtkTreeIter next = *iter;
1500 if (gtk_tree_model_iter_next(store, &next))
1509 void vflist_select_invert(ViewFile *vf)
1512 GtkTreeSelection *selection;
1513 GtkTreeModel *store;
1516 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1517 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1519 /* Backward iteration prevents scrolling to the end of the list,
1520 * it scrolls to the first selected row instead. */
1521 valid = tree_model_get_iter_last(store, &iter);
1525 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1528 gtk_tree_selection_unselect_iter(selection, &iter);
1530 gtk_tree_selection_select_iter(selection, &iter);
1532 valid = tree_model_iter_prev(store, &iter);
1536 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1540 if (vflist_find_row(vf, fd, &iter) < 0) return;
1542 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1544 if (!vflist_row_is_selected(vf, fd))
1546 GtkTreeSelection *selection;
1547 GtkTreeModel *store;
1550 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1551 gtk_tree_selection_unselect_all(selection);
1552 gtk_tree_selection_select_iter(selection, &iter);
1553 vflist_move_cursor(vf, &iter);
1555 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1556 tpath = gtk_tree_model_get_path(store, &iter);
1557 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1558 gtk_tree_path_free(tpath);
1562 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1565 FileData *fd = NULL;
1567 if (sel_fd->parent) sel_fd = sel_fd->parent;
1576 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1578 if (match >= 0) break;
1581 if (fd) vflist_select_by_fd(vf, fd);
1585 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1587 GtkTreeModel *store;
1589 GtkTreeSelection *selection;
1593 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1595 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1596 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1598 valid = gtk_tree_model_get_iter_first(store, &iter);
1602 gboolean mark_val, selected;
1603 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1605 mark_val = file_data_get_mark(fd, n);
1606 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1610 case MTS_MODE_SET: selected = mark_val;
1612 case MTS_MODE_OR: selected = mark_val | selected;
1614 case MTS_MODE_AND: selected = mark_val & selected;
1616 case MTS_MODE_MINUS: selected = !mark_val & selected;
1621 gtk_tree_selection_select_iter(selection, &iter);
1623 gtk_tree_selection_unselect_iter(selection, &iter);
1625 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1629 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1631 GtkTreeModel *store;
1632 GtkTreeSelection *selection;
1637 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1639 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1640 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1644 GtkTreePath *tpath = work->data;
1648 gtk_tree_model_get_iter(store, &iter, tpath);
1649 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1651 /* the change has a very limited range and the standard notification would trigger
1652 complete re-read of the directory - try to do only minimal update instead */
1653 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1657 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1659 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1661 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1665 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1667 vf_refresh_idle(vf);
1671 /* mark functions can have various side effects - update all columns to be sure */
1672 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1673 /* mark functions can change sidecars too */
1674 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1678 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1682 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1687 *-----------------------------------------------------------------------------
1689 *-----------------------------------------------------------------------------
1692 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
1694 GtkTreeViewColumn *column;
1695 GtkCellRenderer *cell;
1698 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1699 if (!column) return;
1701 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1703 list = gtk_tree_view_column_get_cell_renderers(column);
1708 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1709 gtk_tree_view_column_set_visible(column, thumb);
1711 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1712 if (!column) return;
1713 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1715 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1716 if (!column) return;
1717 gtk_tree_view_column_set_visible(column, !multiline);
1719 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1720 if (!column) return;
1721 gtk_tree_view_column_set_visible(column, !multiline);
1724 static gboolean vflist_is_multiline(ViewFile *vf)
1726 return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
1730 static void vflist_populate_view(ViewFile *vf, gboolean force)
1732 GtkTreeStore *store;
1735 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1741 vflist_store_clear(vf);
1746 vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
1748 selected = vflist_selection_get_list(vf);
1750 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force);
1752 if (selected && vflist_selection_count(vf, NULL) == 0)
1754 /* all selected files disappeared */
1755 vflist_select_closest(vf, selected->data);
1758 filelist_free(selected);
1761 vf_thumb_update(vf);
1764 gboolean vflist_refresh(ViewFile *vf)
1767 gboolean ret = TRUE;
1769 old_list = vf->list;
1772 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1775 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1777 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1778 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1779 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1781 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1782 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1785 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1787 vflist_populate_view(vf, FALSE);
1789 filelist_free(old_list);
1790 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1797 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1799 #define CELL_HEIGHT_OVERRIDE 512
1801 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1805 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1806 if (spec && G_IS_PARAM_SPEC_INT(spec))
1808 GParamSpecInt *spec_int;
1810 spec_int = G_PARAM_SPEC_INT(spec);
1811 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1815 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1817 static GdkColor color;
1818 static GtkWidget *done = NULL;
1824 style = gtk_widget_get_style(widget);
1825 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1826 shift_color(&color, -1, 0);
1833 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1834 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1836 ViewFile *vf = data;
1839 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1840 g_object_set(G_OBJECT(cell),
1841 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1842 "cell-background-set", set, NULL);
1845 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1847 GtkTreeViewColumn *column;
1848 GtkCellRenderer *renderer;
1850 column = gtk_tree_view_column_new();
1851 gtk_tree_view_column_set_title(column, title);
1852 gtk_tree_view_column_set_min_width(column, 4);
1856 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1857 renderer = gtk_cell_renderer_text_new();
1860 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1862 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1863 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1865 gtk_tree_view_column_set_expand(column, TRUE);
1869 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1870 renderer = gtk_cell_renderer_pixbuf_new();
1871 cell_renderer_height_override(renderer);
1872 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1873 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1876 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1877 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1878 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1880 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1883 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1885 ViewFile *vf = data;
1886 GtkTreeStore *store;
1887 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1893 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1894 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1897 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1899 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1901 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1904 /* the change has a very limited range and the standard notification would trigger
1905 complete re-read of the directory - try to do only minimal update instead */
1906 file_data_unregister_notify_func(vf_notify_cb, vf);
1907 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1908 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1910 vf_refresh_idle(vf);
1914 /* mark functions can have various side effects - update all columns to be sure */
1915 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1916 /* mark functions can change sidecars too */
1917 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1919 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1921 gtk_tree_path_free(path);
1924 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1926 GtkTreeViewColumn *column;
1927 GtkCellRenderer *renderer;
1928 GtkTreeStore *store;
1931 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1933 renderer = gtk_cell_renderer_toggle_new();
1934 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1936 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1937 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1938 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1940 index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1941 gtk_tree_view_column_set_fixed_width(column, 22);
1942 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1945 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1949 *-----------------------------------------------------------------------------
1951 *-----------------------------------------------------------------------------
1954 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1957 if (!dir_fd) return FALSE;
1958 if (vf->dir_fd == dir_fd) return TRUE;
1960 file_data_unref(vf->dir_fd);
1961 vf->dir_fd = file_data_ref(dir_fd);
1963 /* force complete reload */
1964 vflist_store_clear(vf);
1966 filelist_free(vf->list);
1969 ret = vf_refresh(vf);
1970 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
1974 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1976 ViewFile *vf = data;
1978 file_data_unregister_notify_func(vf_notify_cb, vf);
1980 vflist_select_idle_cancel(vf);
1981 vf_refresh_idle_cancel(vf);
1984 filelist_free(vf->list);
1987 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1989 GtkTreeStore *store;
1990 GtkTreeSelection *selection;
1991 GType flist_types[FILE_COLUMN_COUNT];
1995 vf->info = g_new0(ViewFileInfoList, 1);
1997 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1998 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1999 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
2000 flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING;
2001 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
2002 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
2003 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
2004 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
2005 flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN;
2006 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
2007 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
2008 flist_types[i] = G_TYPE_BOOLEAN;
2010 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
2012 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2013 g_object_unref(store);
2015 g_signal_connect(G_OBJECT(vf->listview), "row-expanded",
2016 G_CALLBACK(vflist_expand_cb), vf);
2018 g_signal_connect(G_OBJECT(vf->listview), "row-collapsed",
2019 G_CALLBACK(vflist_collapse_cb), vf);
2021 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2022 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
2023 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
2025 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2026 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2030 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2032 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2033 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
2037 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2038 g_assert(column == FILE_VIEW_COLUMN_THUMB);
2041 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE);
2042 g_assert(column == FILE_VIEW_COLUMN_FORMATTED);
2045 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2046 g_assert(column == FILE_VIEW_COLUMN_SIZE);
2049 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2050 g_assert(column == FILE_VIEW_COLUMN_DATE);
2053 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2057 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2059 if (VFLIST(vf)->thumbs_enabled == enable) return;
2061 VFLIST(vf)->thumbs_enabled = enable;
2063 /* vflist_populate_view is better than vf_refresh:
2064 - no need to re-read the directory
2065 - force update because the formatted string has changed
2069 vflist_populate_view(vf, TRUE);
2070 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
2074 void vflist_marks_set(ViewFile *vf, gboolean enable)
2076 GList *columns, *work;
2078 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2083 GtkTreeViewColumn *column = work->data;
2084 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2087 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2088 gtk_tree_view_column_set_visible(column, enable);
2091 g_list_free(columns);
2095 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */