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;
965 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
966 FILE_COLUMN_POINTER, &old_fd,
967 FILE_COLUMN_VERSION, &old_version,
977 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
979 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
981 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);
1030 if (fd->version != old_version || force)
1032 vflist_setup_iter(vf, store, &iter, fd);
1033 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected, force);
1036 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1047 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1048 file_data_unref(old_fd);
1050 valid = gtk_tree_store_remove(store, &iter);
1053 /* move the prepended entries to the correct position */
1057 gint num_total = num_prepended + num_ordered;
1058 gint *new_order = g_malloc(num_total * sizeof(gint));
1060 for (i = 0; i < num_total; i++)
1062 if (i < num_ordered)
1063 new_order[i] = num_prepended + i;
1065 new_order[i] = num_total - 1 - i;
1067 gtk_tree_store_reorder(store, parent_iter, new_order);
1073 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
1076 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
1078 GtkTreeStore *store;
1081 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1082 if (!vf->list) return;
1088 FileData *fd = work->data;
1089 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1094 vf->sort_method = type;
1095 vf->sort_ascend = ascend;
1097 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1099 new_order = g_malloc(i * sizeof(gint));
1105 FileData *fd = work->data;
1106 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1111 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1112 gtk_tree_store_reorder(store, NULL, new_order);
1115 g_hash_table_destroy(fd_idx_hash);
1119 *-----------------------------------------------------------------------------
1121 *-----------------------------------------------------------------------------
1125 void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1130 FileData *fd = work->data;
1133 if (fd->thumb_pixbuf) (*done)++;
1135 if (fd->sidecar_files)
1137 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1143 void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
1145 GtkTreeStore *store;
1148 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1150 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1151 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1154 FileData *vflist_thumb_next_fd(ViewFile *vf)
1157 FileData *fd = NULL;
1159 /* first check the visible files */
1161 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1163 GtkTreeModel *store;
1165 gboolean valid = TRUE;
1167 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1168 gtk_tree_model_get_iter(store, &iter, tpath);
1169 gtk_tree_path_free(tpath);
1171 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1175 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1177 if (!nfd->thumb_pixbuf) fd = nfd;
1179 valid = gtk_tree_model_iter_next(store, &iter);
1183 /* then find first undone */
1187 GList *work = vf->list;
1190 FileData *fd_p = work->data;
1191 if (!fd_p->thumb_pixbuf)
1195 GList *work2 = fd_p->sidecar_files;
1197 while (work2 && !fd)
1200 if (!fd_p->thumb_pixbuf) fd = fd_p;
1201 work2 = work2->next;
1212 void vflist_thumb_reset_all(ViewFile *vf)
1214 GList *work = vf->list;
1217 FileData *fd = work->data;
1218 if (fd->thumb_pixbuf)
1220 g_object_unref(fd->thumb_pixbuf);
1221 fd->thumb_pixbuf = NULL;
1228 *-----------------------------------------------------------------------------
1230 *-----------------------------------------------------------------------------
1233 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1235 return g_list_nth_data(vf->list, row);
1238 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1241 GList *work, *work2;
1246 FileData *list_fd = work->data;
1247 if (list_fd == fd) return p;
1249 work2 = list_fd->sidecar_files;
1252 /* FIXME: return the same index also for sidecars
1253 it is sufficient for next/prev navigation but it should be rewritten
1254 without using indexes at all
1256 FileData *sidecar_fd = work2->data;
1257 if (sidecar_fd == fd) return p;
1258 work2 = work2->next;
1268 guint vflist_count(ViewFile *vf, gint64 *bytes)
1278 FileData *fd = work->data;
1286 return g_list_length(vf->list);
1289 GList *vflist_get_list(ViewFile *vf)
1297 FileData *fd = work->data;
1300 list = g_list_prepend(list, file_data_ref(fd));
1303 return g_list_reverse(list);
1307 *-----------------------------------------------------------------------------
1309 *-----------------------------------------------------------------------------
1312 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1314 GtkTreeModel *store;
1315 GtkTreeSelection *selection;
1318 gboolean found = FALSE;
1320 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1321 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1323 while (!found && work)
1325 GtkTreePath *tpath = work->data;
1329 gtk_tree_model_get_iter(store, &iter, tpath);
1330 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1331 if (fd_n == fd) found = TRUE;
1334 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1340 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1344 fd = vf_index_get_data(vf, row);
1345 return vflist_row_is_selected(vf, fd);
1348 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1350 GtkTreeModel *store;
1351 GtkTreeSelection *selection;
1355 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1356 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1366 GtkTreePath *tpath = work->data;
1370 gtk_tree_model_get_iter(store, &iter, tpath);
1371 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1380 count = g_list_length(slist);
1381 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1387 GList *vflist_selection_get_list(ViewFile *vf)
1389 GtkTreeModel *store;
1390 GtkTreeSelection *selection;
1395 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1396 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1400 GtkTreePath *tpath = work->data;
1404 gtk_tree_model_get_iter(store, &iter, tpath);
1405 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1407 list = g_list_prepend(list, file_data_ref(fd));
1409 if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
1411 /* unexpanded - add whole group */
1412 GList *work2 = fd->sidecar_files;
1415 FileData *sfd = work2->data;
1416 list = g_list_prepend(list, file_data_ref(sfd));
1417 work2 = work2->next;
1423 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1426 return g_list_reverse(list);
1429 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1431 GtkTreeModel *store;
1432 GtkTreeSelection *selection;
1437 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1438 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1442 GtkTreePath *tpath = work->data;
1446 gtk_tree_model_get_iter(store, &iter, tpath);
1447 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1449 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1453 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1456 return g_list_reverse(list);
1459 void vflist_select_all(ViewFile *vf)
1461 GtkTreeSelection *selection;
1463 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1464 gtk_tree_selection_select_all(selection);
1466 VFLIST(vf)->select_fd = NULL;
1469 void vflist_select_none(ViewFile *vf)
1471 GtkTreeSelection *selection;
1473 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1474 gtk_tree_selection_unselect_all(selection);
1477 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1482 tpath = gtk_tree_model_get_path(store, iter);
1483 result = gtk_tree_path_prev(tpath);
1485 gtk_tree_model_get_iter(store, iter, tpath);
1487 gtk_tree_path_free(tpath);
1492 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1494 if (!gtk_tree_model_get_iter_first(store, iter))
1499 GtkTreeIter next = *iter;
1501 if (gtk_tree_model_iter_next(store, &next))
1510 void vflist_select_invert(ViewFile *vf)
1513 GtkTreeSelection *selection;
1514 GtkTreeModel *store;
1517 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1518 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1520 /* Backward iteration prevents scrolling to the end of the list,
1521 * it scrolls to the first selected row instead. */
1522 valid = tree_model_get_iter_last(store, &iter);
1526 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1529 gtk_tree_selection_unselect_iter(selection, &iter);
1531 gtk_tree_selection_select_iter(selection, &iter);
1533 valid = tree_model_iter_prev(store, &iter);
1537 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1541 if (vflist_find_row(vf, fd, &iter) < 0) return;
1543 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1545 if (!vflist_row_is_selected(vf, fd))
1547 GtkTreeSelection *selection;
1548 GtkTreeModel *store;
1551 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1552 gtk_tree_selection_unselect_all(selection);
1553 gtk_tree_selection_select_iter(selection, &iter);
1554 vflist_move_cursor(vf, &iter);
1556 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1557 tpath = gtk_tree_model_get_path(store, &iter);
1558 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1559 gtk_tree_path_free(tpath);
1563 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1566 FileData *fd = NULL;
1568 if (sel_fd->parent) sel_fd = sel_fd->parent;
1577 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1579 if (match >= 0) break;
1582 if (fd) vflist_select_by_fd(vf, fd);
1586 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1588 GtkTreeModel *store;
1590 GtkTreeSelection *selection;
1594 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1596 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1597 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1599 valid = gtk_tree_model_get_iter_first(store, &iter);
1603 gboolean mark_val, selected;
1604 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1606 mark_val = file_data_get_mark(fd, n);
1607 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1611 case MTS_MODE_SET: selected = mark_val;
1613 case MTS_MODE_OR: selected = mark_val | selected;
1615 case MTS_MODE_AND: selected = mark_val & selected;
1617 case MTS_MODE_MINUS: selected = !mark_val & selected;
1622 gtk_tree_selection_select_iter(selection, &iter);
1624 gtk_tree_selection_unselect_iter(selection, &iter);
1626 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1630 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1632 GtkTreeModel *store;
1633 GtkTreeSelection *selection;
1638 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1640 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1641 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1645 GtkTreePath *tpath = work->data;
1649 gtk_tree_model_get_iter(store, &iter, tpath);
1650 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1652 /* the change has a very limited range and the standard notification would trigger
1653 complete re-read of the directory - try to do only minimal update instead */
1654 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1658 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1660 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1662 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1666 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1668 vf_refresh_idle(vf);
1672 /* mark functions can have various side effects - update all columns to be sure */
1673 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1674 /* mark functions can change sidecars too */
1675 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1679 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1683 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1688 *-----------------------------------------------------------------------------
1690 *-----------------------------------------------------------------------------
1693 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
1695 GtkTreeViewColumn *column;
1696 GtkCellRenderer *cell;
1699 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1700 if (!column) return;
1702 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1704 list = gtk_tree_view_column_get_cell_renderers(column);
1709 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1710 gtk_tree_view_column_set_visible(column, thumb);
1712 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1713 if (!column) return;
1714 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1716 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1717 if (!column) return;
1718 gtk_tree_view_column_set_visible(column, !multiline);
1720 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1721 if (!column) return;
1722 gtk_tree_view_column_set_visible(column, !multiline);
1725 static gboolean vflist_is_multiline(ViewFile *vf)
1727 return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
1731 static void vflist_populate_view(ViewFile *vf, gboolean force)
1733 GtkTreeStore *store;
1736 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1742 vflist_store_clear(vf);
1747 vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
1749 selected = vflist_selection_get_list(vf);
1751 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force);
1753 if (selected && vflist_selection_count(vf, NULL) == 0)
1755 /* all selected files disappeared */
1756 vflist_select_closest(vf, selected->data);
1759 filelist_free(selected);
1762 vf_thumb_update(vf);
1765 gboolean vflist_refresh(ViewFile *vf)
1768 gboolean ret = TRUE;
1770 old_list = vf->list;
1773 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1776 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1778 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1779 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1780 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1782 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1783 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1786 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1788 vflist_populate_view(vf, FALSE);
1790 filelist_free(old_list);
1791 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1798 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1800 #define CELL_HEIGHT_OVERRIDE 512
1802 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1806 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1807 if (spec && G_IS_PARAM_SPEC_INT(spec))
1809 GParamSpecInt *spec_int;
1811 spec_int = G_PARAM_SPEC_INT(spec);
1812 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1816 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1818 static GdkColor color;
1819 static GtkWidget *done = NULL;
1825 style = gtk_widget_get_style(widget);
1826 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1827 shift_color(&color, -1, 0);
1834 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1835 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1837 ViewFile *vf = data;
1840 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1841 g_object_set(G_OBJECT(cell),
1842 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1843 "cell-background-set", set, NULL);
1846 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1848 GtkTreeViewColumn *column;
1849 GtkCellRenderer *renderer;
1851 column = gtk_tree_view_column_new();
1852 gtk_tree_view_column_set_title(column, title);
1853 gtk_tree_view_column_set_min_width(column, 4);
1857 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1858 renderer = gtk_cell_renderer_text_new();
1861 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1863 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1864 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1866 gtk_tree_view_column_set_expand(column, TRUE);
1870 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1871 renderer = gtk_cell_renderer_pixbuf_new();
1872 cell_renderer_height_override(renderer);
1873 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1874 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1877 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1878 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1879 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1881 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1884 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1886 ViewFile *vf = data;
1887 GtkTreeStore *store;
1888 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1894 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1895 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1898 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1900 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1902 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1905 /* the change has a very limited range and the standard notification would trigger
1906 complete re-read of the directory - try to do only minimal update instead */
1907 file_data_unregister_notify_func(vf_notify_cb, vf);
1908 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1909 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1911 vf_refresh_idle(vf);
1915 /* mark functions can have various side effects - update all columns to be sure */
1916 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1917 /* mark functions can change sidecars too */
1918 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1920 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1922 gtk_tree_path_free(path);
1925 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1927 GtkTreeViewColumn *column;
1928 GtkCellRenderer *renderer;
1929 GtkTreeStore *store;
1932 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1934 renderer = gtk_cell_renderer_toggle_new();
1935 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1937 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1938 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1939 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1941 index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1942 gtk_tree_view_column_set_fixed_width(column, 22);
1943 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1946 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1950 *-----------------------------------------------------------------------------
1952 *-----------------------------------------------------------------------------
1955 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1958 if (!dir_fd) return FALSE;
1959 if (vf->dir_fd == dir_fd) return TRUE;
1961 file_data_unref(vf->dir_fd);
1962 vf->dir_fd = file_data_ref(dir_fd);
1964 /* force complete reload */
1965 vflist_store_clear(vf);
1967 filelist_free(vf->list);
1970 ret = vf_refresh(vf);
1971 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
1975 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1977 ViewFile *vf = data;
1979 file_data_unregister_notify_func(vf_notify_cb, vf);
1981 vflist_select_idle_cancel(vf);
1982 vf_refresh_idle_cancel(vf);
1985 filelist_free(vf->list);
1988 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1990 GtkTreeStore *store;
1991 GtkTreeSelection *selection;
1992 GType flist_types[FILE_COLUMN_COUNT];
1996 vf->info = g_new0(ViewFileInfoList, 1);
1998 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1999 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
2000 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
2001 flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING;
2002 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
2003 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
2004 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
2005 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
2006 flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN;
2007 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
2008 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
2009 flist_types[i] = G_TYPE_BOOLEAN;
2011 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
2013 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2014 g_object_unref(store);
2016 g_signal_connect(G_OBJECT(vf->listview), "row-expanded",
2017 G_CALLBACK(vflist_expand_cb), vf);
2019 g_signal_connect(G_OBJECT(vf->listview), "row-collapsed",
2020 G_CALLBACK(vflist_collapse_cb), vf);
2022 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2023 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
2024 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
2026 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2027 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2031 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2033 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2034 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
2038 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2039 g_assert(column == FILE_VIEW_COLUMN_THUMB);
2042 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE);
2043 g_assert(column == FILE_VIEW_COLUMN_FORMATTED);
2046 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2047 g_assert(column == FILE_VIEW_COLUMN_SIZE);
2050 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2051 g_assert(column == FILE_VIEW_COLUMN_DATE);
2054 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2058 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2060 if (VFLIST(vf)->thumbs_enabled == enable) return;
2062 VFLIST(vf)->thumbs_enabled = enable;
2064 /* vflist_populate_view is better than vf_refresh:
2065 - no need to re-read the directory
2066 - force update because the formatted string has changed
2070 vflist_populate_view(vf, TRUE);
2071 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
2075 void vflist_marks_set(ViewFile *vf, gboolean enable)
2077 GList *columns, *work;
2079 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2084 GtkTreeViewColumn *column = work->data;
2085 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2088 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2089 gtk_tree_view_column_set_visible(column, enable);
2092 g_list_free(columns);
2096 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */