4 * Copyright (C) 2008 - 2012 The Geeqie Team
8 * This software is released under the GNU General Public License (GNU GPL).
9 * Please read the included file COPYING for more information.
10 * This software comes with no warranty of any kind, use at your own risk!
14 #include "view_file_list.h"
17 #include "cache_maint.h"
22 #include "layout_image.h"
27 #include "ui_fileops.h"
29 #include "ui_tree_edit.h"
30 #include "uri_utils.h"
31 #include "view_file.h"
33 #include <gdk/gdkkeysyms.h> /* for keyboard values */
35 /* Index to tree store */
37 FILE_COLUMN_POINTER = 0,
40 FILE_COLUMN_FORMATTED,
48 FILE_COLUMN_MARKS_LAST = FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
53 /* Index to tree view */
55 FILE_VIEW_COLUMN_MARKS = 0,
56 FILE_VIEW_COLUMN_MARKS_LAST = FILE_VIEW_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
57 FILE_VIEW_COLUMN_THUMB,
58 FILE_VIEW_COLUMN_FORMATTED,
59 FILE_VIEW_COLUMN_SIZE,
60 FILE_VIEW_COLUMN_DATE,
61 FILE_VIEW_COLUMN_COUNT
66 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd);
67 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data);
68 static void vflist_populate_view(ViewFile *vf, gboolean force);
69 static gboolean vflist_is_multiline(ViewFile *vf);
70 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded);
74 *-----------------------------------------------------------------------------
76 *-----------------------------------------------------------------------------
83 } ViewFileFindRowData;
85 static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
87 ViewFileFindRowData *find = data;
89 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
100 static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter)
103 ViewFileFindRowData data = {fd, iter, FALSE, 0};
105 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
106 gtk_tree_model_foreach(store, vflist_find_row_cb, &data);
116 static FileData *vflist_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter)
119 GtkTreeViewColumn *column;
121 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), x, y,
122 &tpath, &column, NULL, NULL))
128 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
129 gtk_tree_model_get_iter(store, &row, tpath);
130 gtk_tree_path_free(tpath);
131 gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &fd, -1);
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);
189 /* it seems that gtk_tree_store_clear may call some callbacks
190 that use the column. Set the pointer to NULL to be safe. */
191 gtk_tree_store_set(GTK_TREE_STORE(model), iter, FILE_COLUMN_POINTER, NULL, -1);
196 static void vflist_store_clear(ViewFile *vf)
199 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
200 gtk_tree_model_foreach(store, vflist_store_clear_cb, NULL);
201 gtk_tree_store_clear(GTK_TREE_STORE(store));
204 void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set)
209 if (vflist_find_row(vf, fd, &iter) < 0) return;
210 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
211 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
214 static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
219 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
221 tpath = gtk_tree_model_get_path(store, iter);
222 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
223 gtk_tree_path_free(tpath);
227 static gint vflist_column_idx(ViewFile *vf, gint store_idx)
229 GList *columns, *work;
232 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
236 GtkTreeViewColumn *column = work->data;
237 if (store_idx == GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx")))
243 g_list_free(columns);
249 *-----------------------------------------------------------------------------
251 *-----------------------------------------------------------------------------
254 static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
255 GtkSelectionData *selection_data, guint info,
256 guint time, gpointer data)
260 gchar *uri_text = NULL;
263 if (!VFLIST(vf)->click_fd) return;
265 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
267 list = vf_selection_get_list(vf);
271 list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
275 uri_selection_data_set_uris_from_filelist(selection_data, list);
279 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
283 vflist_color_set(vf, VFLIST(vf)->click_fd, TRUE);
285 if (VFLIST(vf)->thumbs_enabled &&
286 VFLIST(vf)->click_fd && VFLIST(vf)->click_fd->thumb_pixbuf)
290 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
291 items = vf_selection_count(vf, NULL);
295 dnd_set_drag_icon(widget, context, VFLIST(vf)->click_fd->thumb_pixbuf, items);
299 static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
303 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
305 if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE)
311 static void vflist_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context,
312 int x, int y, GtkSelectionData *selection,
313 guint info, guint time, gpointer data)
317 if (info == TARGET_TEXT_PLAIN) {
318 FileData *fd = vflist_find_data_by_coord(vf, x, y, NULL);
321 /* Add keywords to file */
322 gchar *str = gtk_selection_data_get_text(selection);
323 GList *kw_list = string_to_keywords_list(str);
325 metadata_append_list(fd, KEYWORD_KEY, kw_list);
326 string_list_free(kw_list);
329 file notification should handle this automatically
330 if (vf->layout && vf->layout->bar_info) {
331 bar_set_fd(vf->layout->bar_info, fd);
338 void vflist_dnd_init(ViewFile *vf)
340 gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
341 dnd_file_drag_types, dnd_file_drag_types_count,
342 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
343 gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL,
344 dnd_file_drag_types, dnd_file_drag_types_count,
345 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
347 g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
348 G_CALLBACK(vflist_dnd_get), vf);
349 g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
350 G_CALLBACK(vflist_dnd_begin), vf);
351 g_signal_connect(G_OBJECT(vf->listview), "drag_end",
352 G_CALLBACK(vflist_dnd_end), vf);
353 g_signal_connect(G_OBJECT(vf->listview), "drag_data_received",
354 G_CALLBACK(vflist_drag_data_received), vf);
358 *-----------------------------------------------------------------------------
360 *-----------------------------------------------------------------------------
363 GList *vflist_selection_get_one(ViewFile *vf, FileData *fd)
365 GList *list = g_list_append(NULL, file_data_ref(fd));
367 if (fd->sidecar_files)
369 /* check if the row is expanded */
373 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
374 if (vflist_find_row(vf, fd, &iter) >= 0)
378 tpath = gtk_tree_model_get_path(store, &iter);
379 if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
381 /* unexpanded - add whole group */
382 GList *work = fd->sidecar_files;
385 FileData *sfd = work->data;
386 list = g_list_prepend(list, file_data_ref(sfd));
390 gtk_tree_path_free(tpath);
392 list = g_list_reverse(list);
398 GList *vflist_pop_menu_file_list(ViewFile *vf)
400 if (!VFLIST(vf)->click_fd) return NULL;
402 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
404 return vf_selection_get_list(vf);
406 return vflist_selection_get_one(vf, VFLIST(vf)->click_fd);
410 void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
414 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
418 list = vf_selection_get_list(vf);
419 view_window_new_from_list(list);
424 view_window_new(VFLIST(vf)->click_fd);
428 void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
433 list = vf_pop_menu_file_list(vf);
434 if (options->file_ops.enable_in_place_rename &&
435 list && !list->next && VFLIST(vf)->click_fd)
442 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
443 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
447 tpath = gtk_tree_model_get_path(store, &iter);
448 tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
449 FILE_VIEW_COLUMN_FORMATTED, VFLIST(vf)->click_fd->name,
450 vflist_row_rename_cb, vf);
451 gtk_tree_path_free(tpath);
456 file_util_rename(NULL, list, vf->listview);
459 void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
463 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
466 layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
470 vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled);
474 void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
478 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
480 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
483 void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
486 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
487 VFLIST(vf)->click_fd = NULL;
493 *-----------------------------------------------------------------------------
495 *-----------------------------------------------------------------------------
498 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
503 if (!new || !new[0]) return FALSE;
505 new_path = g_build_filename(vf->dir_fd->path, new, NULL);
507 if (strchr(new, G_DIR_SEPARATOR) != NULL)
509 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
510 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
515 gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL);
516 FileData *fd = file_data_new_group(old_path); /* get the fd from cache */
517 file_util_rename_simple(fd, new_path, vf->listview);
527 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
535 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return;
536 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
537 tpath = gtk_tree_model_get_path(store, &iter);
538 tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
539 gtk_tree_path_free(tpath);
541 popup_menu_position_clamp(menu, x, y, 0);
544 gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
549 if (event->keyval != GDK_KEY_Menu) return FALSE;
551 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
557 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
558 gtk_tree_model_get_iter(store, &iter, tpath);
559 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
560 gtk_tree_path_free(tpath);
564 VFLIST(vf)->click_fd = NULL;
567 vf->popup = vf_pop_menu(vf);
568 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
573 gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
579 GtkTreeViewColumn *column;
581 vf->clicked_mark = 0;
583 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
584 &tpath, &column, NULL, NULL))
587 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
589 if (bevent->button == MOUSE_BUTTON_LEFT &&
590 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
593 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
594 vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
596 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
598 gtk_tree_model_get_iter(store, &iter, tpath);
599 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
601 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE);
603 gtk_tree_path_free(tpath);
606 VFLIST(vf)->click_fd = fd;
608 if (bevent->button == MOUSE_BUTTON_RIGHT)
610 vf->popup = vf_pop_menu(vf);
611 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
612 bevent->button, bevent->time);
616 if (!fd) return FALSE;
618 if (bevent->button == MOUSE_BUTTON_MIDDLE)
620 if (!vflist_row_is_selected(vf, fd))
622 vflist_color_set(vf, fd, TRUE);
628 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
629 !(bevent->state & GDK_SHIFT_MASK ) &&
630 !(bevent->state & GDK_CONTROL_MASK ) &&
631 vflist_row_is_selected(vf, fd))
633 GtkTreeSelection *selection;
635 gtk_widget_grab_focus(widget);
638 /* returning FALSE and further processing of the event is needed for
639 correct operation of the expander, to show the sidecar files.
640 It however resets the selection of multiple files. With this condition
641 it should work for both cases */
642 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
643 return (gtk_tree_selection_count_selected_rows(selection) > 1);
647 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
649 if (vf->layout) layout_image_full_screen_start(vf->layout);
656 gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
663 if (bevent->button == MOUSE_BUTTON_MIDDLE)
665 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
668 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
673 if ((bevent->x != 0 || bevent->y != 0) &&
674 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
675 &tpath, NULL, NULL, NULL))
679 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
680 gtk_tree_model_get_iter(store, &iter, tpath);
681 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
682 gtk_tree_path_free(tpath);
685 if (bevent->button == MOUSE_BUTTON_MIDDLE)
687 if (fd && VFLIST(vf)->click_fd == fd)
689 GtkTreeSelection *selection;
691 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
692 if (vflist_row_is_selected(vf, fd))
694 gtk_tree_selection_unselect_iter(selection, &iter);
698 gtk_tree_selection_select_iter(selection, &iter);
704 if (fd && VFLIST(vf)->click_fd == fd &&
705 !(bevent->state & GDK_SHIFT_MASK ) &&
706 !(bevent->state & GDK_CONTROL_MASK ) &&
707 vflist_row_is_selected(vf, fd))
709 GtkTreeSelection *selection;
711 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
712 gtk_tree_selection_unselect_all(selection);
713 gtk_tree_selection_select_iter(selection, &iter);
714 vflist_move_cursor(vf, &iter);
715 // return TRUE;// FIXME - expand
721 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
723 FileData *read_ahead_fd = NULL;
729 cur_fd = layout_image_get_fd(vf->layout);
730 if (sel_fd == cur_fd) return; /* no change */
732 row = g_list_index(vf->list, sel_fd);
733 // FIXME sidecar data
735 if (sel_fd && options->image.enable_read_ahead && row >= 0)
737 if (row > g_list_index(vf->list, cur_fd) &&
738 (guint) (row + 1) < vf_count(vf, NULL))
740 read_ahead_fd = vf_index_get_data(vf, row + 1);
744 read_ahead_fd = vf_index_get_data(vf, row - 1);
748 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
751 static gboolean vflist_select_idle_cb(gpointer data)
757 VFLIST(vf)->select_idle_id = 0;
763 if (VFLIST(vf)->select_fd)
765 vflist_select_image(vf, VFLIST(vf)->select_fd);
766 VFLIST(vf)->select_fd = NULL;
769 VFLIST(vf)->select_idle_id = 0;
773 static void vflist_select_idle_cancel(ViewFile *vf)
775 if (VFLIST(vf)->select_idle_id)
777 g_source_remove(VFLIST(vf)->select_idle_id);
778 VFLIST(vf)->select_idle_id = 0;
782 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
783 gboolean path_currently_selected, gpointer data)
788 if (!path_currently_selected &&
789 gtk_tree_model_get_iter(store, &iter, tpath))
791 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
795 VFLIST(vf)->select_fd = NULL;
799 !VFLIST(vf)->select_idle_id)
801 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
807 static void vflist_expand_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
810 vflist_set_expanded(vf, iter, TRUE);
813 static void vflist_collapse_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
816 vflist_set_expanded(vf, iter, FALSE);
820 *-----------------------------------------------------------------------------
822 *-----------------------------------------------------------------------------
826 static gboolean vflist_dummy_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
827 gboolean path_currently_selected, gpointer data)
833 static gchar* vflist_get_formatted(ViewFile *vf, const gchar *name, const gchar *sidecars, const gchar *size, const gchar *time, gboolean expanded)
835 gboolean multiline = vflist_is_multiline(vf);
840 text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time);
844 text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars);
849 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded)
858 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
860 gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
861 FILE_COLUMN_NAME, &name,
862 FILE_COLUMN_SIDECARS, &sidecars,
863 FILE_COLUMN_SIZE, &size,
864 FILE_COLUMN_DATE, &time,
866 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
868 gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED, formatted,
869 FILE_COLUMN_EXPANDED, expanded,
878 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
881 gchar *sidecars = NULL;
883 const gchar *time = text_from_time(fd->date);
884 gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
885 const gchar *disabled_grouping;
887 gboolean expanded = FALSE;
889 if (fd->sidecar_files) /* expanded has no effect on files without sidecars */
891 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_EXPANDED, &expanded, -1);
894 sidecars = file_data_sc_list_to_string(fd);
896 disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
897 name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
898 size = text_from_size(fd->size);
900 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
902 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
903 FILE_COLUMN_VERSION, fd->version,
904 FILE_COLUMN_THUMB, fd->thumb_pixbuf,
905 FILE_COLUMN_FORMATTED, formatted,
906 FILE_COLUMN_SIDECARS, sidecars,
907 FILE_COLUMN_NAME, name,
908 FILE_COLUMN_SIZE, size,
909 FILE_COLUMN_DATE, time,
910 #define STORE_SET_IS_SLOW 1
911 #if STORE_SET_IS_SLOW
912 /* this is 3x faster on a directory with 20000 files */
913 FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
914 FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
915 FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
916 FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
917 FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
918 FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
919 #if FILEDATA_MARKS_SIZE != 6
920 #error this needs to be updated
923 FILE_COLUMN_COLOR, FALSE, -1);
925 #if !STORE_SET_IS_SLOW
928 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
929 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
938 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected, gboolean force)
943 gint num_ordered = 0;
944 gint num_prepended = 0;
946 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
952 FileData *fd = work->data;
953 gboolean done = FALSE;
957 FileData *old_fd = NULL;
958 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");
994 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
999 here should be used gtk_tree_store_append, but this function seems to be O(n)
1000 and it seems to be much faster to add new entries to the beginning and reorder later
1003 gtk_tree_store_prepend(store, &new, parent_iter);
1006 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
1007 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected, force);
1009 if (g_list_find(selected, fd))
1011 /* renamed files - the same fd appears at different position - select it again*/
1012 GtkTreeSelection *selection;
1013 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1014 gtk_tree_selection_select_iter(selection, &new);
1021 file_data_unref(old_fd);
1022 valid = gtk_tree_store_remove(store, &iter);
1027 if (fd->version != old_version || force)
1029 vflist_setup_iter(vf, store, &iter, fd);
1030 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected, force);
1033 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1044 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1045 file_data_unref(old_fd);
1047 valid = gtk_tree_store_remove(store, &iter);
1050 /* move the prepended entries to the correct position */
1054 gint num_total = num_prepended + num_ordered;
1055 gint *new_order = g_malloc(num_total * sizeof(gint));
1057 for (i = 0; i < num_total; i++)
1059 if (i < num_ordered)
1060 new_order[i] = num_prepended + i;
1062 new_order[i] = num_total - 1 - i;
1064 gtk_tree_store_reorder(store, parent_iter, new_order);
1070 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
1073 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
1075 GtkTreeStore *store;
1078 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1079 if (!vf->list) return;
1085 FileData *fd = work->data;
1086 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1091 vf->sort_method = type;
1092 vf->sort_ascend = ascend;
1094 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1096 new_order = g_malloc(i * sizeof(gint));
1102 FileData *fd = work->data;
1103 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1108 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1109 gtk_tree_store_reorder(store, NULL, new_order);
1112 g_hash_table_destroy(fd_idx_hash);
1116 *-----------------------------------------------------------------------------
1118 *-----------------------------------------------------------------------------
1122 void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1127 FileData *fd = work->data;
1130 if (fd->thumb_pixbuf) (*done)++;
1132 if (fd->sidecar_files)
1134 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1140 void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
1142 GtkTreeStore *store;
1145 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1147 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1148 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1151 FileData *vflist_thumb_next_fd(ViewFile *vf)
1154 FileData *fd = NULL;
1156 /* first check the visible files */
1158 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1160 GtkTreeModel *store;
1162 gboolean valid = TRUE;
1164 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1165 gtk_tree_model_get_iter(store, &iter, tpath);
1166 gtk_tree_path_free(tpath);
1168 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1172 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1174 if (!nfd->thumb_pixbuf) fd = nfd;
1176 valid = gtk_tree_model_iter_next(store, &iter);
1180 /* then find first undone */
1184 GList *work = vf->list;
1187 FileData *fd_p = work->data;
1188 if (!fd_p->thumb_pixbuf)
1192 GList *work2 = fd_p->sidecar_files;
1194 while (work2 && !fd)
1197 if (!fd_p->thumb_pixbuf) fd = fd_p;
1198 work2 = work2->next;
1209 void vflist_thumb_reset_all(ViewFile *vf)
1211 GList *work = vf->list;
1214 FileData *fd = work->data;
1215 if (fd->thumb_pixbuf)
1217 g_object_unref(fd->thumb_pixbuf);
1218 fd->thumb_pixbuf = NULL;
1225 *-----------------------------------------------------------------------------
1227 *-----------------------------------------------------------------------------
1230 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1232 return g_list_nth_data(vf->list, row);
1235 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1238 GList *work, *work2;
1243 FileData *list_fd = work->data;
1244 if (list_fd == fd) return p;
1246 work2 = list_fd->sidecar_files;
1249 /* FIXME: return the same index also for sidecars
1250 it is sufficient for next/prev navigation but it should be rewritten
1251 without using indexes at all
1253 FileData *sidecar_fd = work2->data;
1254 if (sidecar_fd == fd) return p;
1255 work2 = work2->next;
1265 guint vflist_count(ViewFile *vf, gint64 *bytes)
1275 FileData *fd = work->data;
1283 return g_list_length(vf->list);
1286 GList *vflist_get_list(ViewFile *vf)
1294 FileData *fd = work->data;
1297 list = g_list_prepend(list, file_data_ref(fd));
1300 return g_list_reverse(list);
1304 *-----------------------------------------------------------------------------
1306 *-----------------------------------------------------------------------------
1309 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1311 GtkTreeModel *store;
1312 GtkTreeSelection *selection;
1315 gboolean found = FALSE;
1317 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1318 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1320 while (!found && work)
1322 GtkTreePath *tpath = work->data;
1326 gtk_tree_model_get_iter(store, &iter, tpath);
1327 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1328 if (fd_n == fd) found = TRUE;
1331 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1337 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1341 fd = vf_index_get_data(vf, row);
1342 return vflist_row_is_selected(vf, fd);
1345 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1347 GtkTreeModel *store;
1348 GtkTreeSelection *selection;
1352 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1353 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1363 GtkTreePath *tpath = work->data;
1367 gtk_tree_model_get_iter(store, &iter, tpath);
1368 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1377 count = g_list_length(slist);
1378 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1384 GList *vflist_selection_get_list(ViewFile *vf)
1386 GtkTreeModel *store;
1387 GtkTreeSelection *selection;
1392 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1393 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1397 GtkTreePath *tpath = work->data;
1401 gtk_tree_model_get_iter(store, &iter, tpath);
1402 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1404 list = g_list_prepend(list, file_data_ref(fd));
1406 if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
1408 /* unexpanded - add whole group */
1409 GList *work2 = fd->sidecar_files;
1412 FileData *sfd = work2->data;
1413 list = g_list_prepend(list, file_data_ref(sfd));
1414 work2 = work2->next;
1420 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1423 return g_list_reverse(list);
1426 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1428 GtkTreeModel *store;
1429 GtkTreeSelection *selection;
1434 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1435 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1439 GtkTreePath *tpath = work->data;
1443 gtk_tree_model_get_iter(store, &iter, tpath);
1444 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1446 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1450 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1453 return g_list_reverse(list);
1456 void vflist_select_all(ViewFile *vf)
1458 GtkTreeSelection *selection;
1460 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1461 gtk_tree_selection_select_all(selection);
1463 VFLIST(vf)->select_fd = NULL;
1466 void vflist_select_none(ViewFile *vf)
1468 GtkTreeSelection *selection;
1470 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1471 gtk_tree_selection_unselect_all(selection);
1474 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1479 tpath = gtk_tree_model_get_path(store, iter);
1480 result = gtk_tree_path_prev(tpath);
1482 gtk_tree_model_get_iter(store, iter, tpath);
1484 gtk_tree_path_free(tpath);
1489 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1491 if (!gtk_tree_model_get_iter_first(store, iter))
1496 GtkTreeIter next = *iter;
1498 if (gtk_tree_model_iter_next(store, &next))
1507 void vflist_select_invert(ViewFile *vf)
1510 GtkTreeSelection *selection;
1511 GtkTreeModel *store;
1514 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1515 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1517 /* Backward iteration prevents scrolling to the end of the list,
1518 * it scrolls to the first selected row instead. */
1519 valid = tree_model_get_iter_last(store, &iter);
1523 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1526 gtk_tree_selection_unselect_iter(selection, &iter);
1528 gtk_tree_selection_select_iter(selection, &iter);
1530 valid = tree_model_iter_prev(store, &iter);
1534 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1538 if (vflist_find_row(vf, fd, &iter) < 0) return;
1540 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1542 if (!vflist_row_is_selected(vf, fd))
1544 GtkTreeSelection *selection;
1545 GtkTreeModel *store;
1548 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1549 gtk_tree_selection_unselect_all(selection);
1550 gtk_tree_selection_select_iter(selection, &iter);
1551 vflist_move_cursor(vf, &iter);
1553 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1554 tpath = gtk_tree_model_get_path(store, &iter);
1555 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1556 gtk_tree_path_free(tpath);
1560 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1563 FileData *fd = NULL;
1565 if (sel_fd->parent) sel_fd = sel_fd->parent;
1574 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1576 if (match >= 0) break;
1579 if (fd) vflist_select_by_fd(vf, fd);
1583 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1585 GtkTreeModel *store;
1587 GtkTreeSelection *selection;
1591 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1593 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1594 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1596 valid = gtk_tree_model_get_iter_first(store, &iter);
1600 gboolean mark_val, selected;
1601 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1603 mark_val = file_data_get_mark(fd, n);
1604 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1608 case MTS_MODE_SET: selected = mark_val;
1610 case MTS_MODE_OR: selected = mark_val || selected;
1612 case MTS_MODE_AND: selected = mark_val && selected;
1614 case MTS_MODE_MINUS: selected = !mark_val && selected;
1619 gtk_tree_selection_select_iter(selection, &iter);
1621 gtk_tree_selection_unselect_iter(selection, &iter);
1623 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1627 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1629 GtkTreeModel *store;
1630 GtkTreeSelection *selection;
1635 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1637 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1638 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1642 GtkTreePath *tpath = work->data;
1646 gtk_tree_model_get_iter(store, &iter, tpath);
1647 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1649 /* the change has a very limited range and the standard notification would trigger
1650 complete re-read of the directory - try to do only minimal update instead */
1651 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1655 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1657 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1659 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1663 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1665 vf_refresh_idle(vf);
1669 /* mark functions can have various side effects - update all columns to be sure */
1670 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1671 /* mark functions can change sidecars too */
1672 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1676 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1680 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1685 *-----------------------------------------------------------------------------
1687 *-----------------------------------------------------------------------------
1690 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
1692 GtkTreeViewColumn *column;
1693 GtkCellRenderer *cell;
1696 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1697 if (!column) return;
1699 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1701 list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1706 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1707 gtk_tree_view_column_set_visible(column, thumb);
1709 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1710 if (!column) return;
1711 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1713 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1714 if (!column) return;
1715 gtk_tree_view_column_set_visible(column, !multiline);
1717 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1718 if (!column) return;
1719 gtk_tree_view_column_set_visible(column, !multiline);
1722 static gboolean vflist_is_multiline(ViewFile *vf)
1724 return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
1728 static void vflist_populate_view(ViewFile *vf, gboolean force)
1730 GtkTreeStore *store;
1733 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1739 vflist_store_clear(vf);
1744 vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
1746 selected = vflist_selection_get_list(vf);
1748 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force);
1750 if (selected && vflist_selection_count(vf, NULL) == 0)
1752 /* all selected files disappeared */
1753 vflist_select_closest(vf, selected->data);
1756 filelist_free(selected);
1759 vf_thumb_update(vf);
1762 gboolean vflist_refresh(ViewFile *vf)
1765 gboolean ret = TRUE;
1767 old_list = vf->list;
1770 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1773 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1775 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1776 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1777 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1779 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1780 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1783 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1785 vflist_populate_view(vf, FALSE);
1787 filelist_free(old_list);
1788 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1795 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1797 #define CELL_HEIGHT_OVERRIDE 512
1799 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1803 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1804 if (spec && G_IS_PARAM_SPEC_INT(spec))
1806 GParamSpecInt *spec_int;
1808 spec_int = G_PARAM_SPEC_INT(spec);
1809 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1813 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1815 static GdkColor color;
1816 static GtkWidget *done = NULL;
1822 style = gtk_widget_get_style(widget);
1823 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1824 shift_color(&color, -1, 0);
1831 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1832 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1834 ViewFile *vf = data;
1837 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1838 g_object_set(G_OBJECT(cell),
1839 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1840 "cell-background-set", set, NULL);
1843 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1845 GtkTreeViewColumn *column;
1846 GtkCellRenderer *renderer;
1848 column = gtk_tree_view_column_new();
1849 gtk_tree_view_column_set_title(column, title);
1850 gtk_tree_view_column_set_min_width(column, 4);
1854 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1855 renderer = gtk_cell_renderer_text_new();
1858 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1860 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1861 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1863 gtk_tree_view_column_set_expand(column, TRUE);
1867 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1868 renderer = gtk_cell_renderer_pixbuf_new();
1869 cell_renderer_height_override(renderer);
1870 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1871 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1874 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1875 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1876 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1878 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1881 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1883 ViewFile *vf = data;
1884 GtkTreeStore *store;
1885 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1891 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1892 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1895 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1897 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1899 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1902 /* the change has a very limited range and the standard notification would trigger
1903 complete re-read of the directory - try to do only minimal update instead */
1904 file_data_unregister_notify_func(vf_notify_cb, vf);
1905 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1906 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1908 vf_refresh_idle(vf);
1912 /* mark functions can have various side effects - update all columns to be sure */
1913 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1914 /* mark functions can change sidecars too */
1915 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1917 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1919 gtk_tree_path_free(path);
1922 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1924 GtkTreeViewColumn *column;
1925 GtkCellRenderer *renderer;
1926 GtkTreeStore *store;
1929 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1931 renderer = gtk_cell_renderer_toggle_new();
1932 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1934 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1935 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1936 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1938 index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1939 gtk_tree_view_column_set_fixed_width(column, 22);
1940 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1943 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1947 *-----------------------------------------------------------------------------
1949 *-----------------------------------------------------------------------------
1952 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1955 if (!dir_fd) return FALSE;
1956 if (vf->dir_fd == dir_fd) return TRUE;
1958 file_data_unref(vf->dir_fd);
1959 vf->dir_fd = file_data_ref(dir_fd);
1961 /* force complete reload */
1962 vflist_store_clear(vf);
1964 filelist_free(vf->list);
1967 ret = vf_refresh(vf);
1968 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
1972 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1974 ViewFile *vf = data;
1976 file_data_unregister_notify_func(vf_notify_cb, vf);
1978 vflist_select_idle_cancel(vf);
1979 vf_refresh_idle_cancel(vf);
1982 filelist_free(vf->list);
1985 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1987 GtkTreeStore *store;
1988 GtkTreeSelection *selection;
1989 GType flist_types[FILE_COLUMN_COUNT];
1993 vf->info = g_new0(ViewFileInfoList, 1);
1995 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1996 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1997 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1998 flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING;
1999 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
2000 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
2001 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
2002 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
2003 flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN;
2004 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
2005 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
2006 flist_types[i] = G_TYPE_BOOLEAN;
2008 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
2010 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2011 g_object_unref(store);
2013 g_signal_connect(G_OBJECT(vf->listview), "row-expanded",
2014 G_CALLBACK(vflist_expand_cb), vf);
2016 g_signal_connect(G_OBJECT(vf->listview), "row-collapsed",
2017 G_CALLBACK(vflist_collapse_cb), vf);
2019 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2020 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
2021 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
2023 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2024 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2028 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2030 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2031 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
2035 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2036 g_assert(column == FILE_VIEW_COLUMN_THUMB);
2039 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE);
2040 g_assert(column == FILE_VIEW_COLUMN_FORMATTED);
2043 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2044 g_assert(column == FILE_VIEW_COLUMN_SIZE);
2047 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2048 g_assert(column == FILE_VIEW_COLUMN_DATE);
2051 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2055 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2057 if (VFLIST(vf)->thumbs_enabled == enable) return;
2059 VFLIST(vf)->thumbs_enabled = enable;
2061 /* vflist_populate_view is better than vf_refresh:
2062 - no need to re-read the directory
2063 - force update because the formatted string has changed
2067 vflist_populate_view(vf, TRUE);
2068 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
2072 void vflist_marks_set(ViewFile *vf, gboolean enable)
2074 GList *columns, *work;
2076 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2081 GtkTreeViewColumn *column = work->data;
2082 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2085 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2086 gtk_tree_view_column_set_visible(column, enable);
2089 g_list_free(columns);
2093 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */