2 * Copyright (C) 2004 John Ellis
3 * Copyright (C) 2008 - 2016 The Geeqie Team
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "view_file_list.h"
26 #include "cache_maint.h"
32 #include "layout_image.h"
37 #include "ui_fileops.h"
39 #include "ui_tree_edit.h"
40 #include "uri_utils.h"
41 #include "view_file.h"
44 #include <gdk/gdkkeysyms.h> /* for keyboard values */
46 /* Index to tree store */
48 FILE_COLUMN_POINTER = 0,
51 FILE_COLUMN_FORMATTED,
52 FILE_COLUMN_FORMATTED_WITH_STARS,
55 FILE_COLUMN_STAR_RATING,
61 FILE_COLUMN_MARKS_LAST = FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
66 /* Index to tree view */
68 FILE_VIEW_COLUMN_MARKS = 0,
69 FILE_VIEW_COLUMN_MARKS_LAST = FILE_VIEW_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
70 FILE_VIEW_COLUMN_THUMB,
71 FILE_VIEW_COLUMN_FORMATTED,
72 FILE_VIEW_COLUMN_FORMATTED_WITH_STARS,
73 FILE_VIEW_COLUMN_STAR_RATING,
74 FILE_VIEW_COLUMN_SIZE,
75 FILE_VIEW_COLUMN_DATE,
76 FILE_VIEW_COLUMN_COUNT
81 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd);
82 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data);
83 static void vflist_populate_view(ViewFile *vf, gboolean force);
84 static gboolean vflist_is_multiline(ViewFile *vf);
85 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded);
89 *-----------------------------------------------------------------------------
91 *-----------------------------------------------------------------------------
98 } ViewFileFindRowData;
100 static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
102 ViewFileFindRowData *find = data;
104 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
115 static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter)
118 ViewFileFindRowData data = {fd, iter, FALSE, 0};
120 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
121 gtk_tree_model_foreach(store, vflist_find_row_cb, &data);
131 static FileData *vflist_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter)
134 GtkTreeViewColumn *column;
136 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), x, y,
137 &tpath, &column, NULL, NULL))
143 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
144 gtk_tree_model_get_iter(store, &row, tpath);
145 gtk_tree_path_free(tpath);
146 gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &fd, -1);
154 static gboolean vflist_store_clear_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
157 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
159 /* it seems that gtk_tree_store_clear may call some callbacks
160 that use the column. Set the pointer to NULL to be safe. */
161 gtk_tree_store_set(GTK_TREE_STORE(model), iter, FILE_COLUMN_POINTER, NULL, -1);
166 static void vflist_store_clear(ViewFile *vf, gboolean unlock_files)
171 if (unlock_files && vf->marks_enabled)
173 // unlock locked files in this directory
174 filelist_read(vf->dir_fd, &files, NULL);
177 FileData *fd = files->data;
179 file_data_unlock(fd);
180 file_data_unref(fd); // undo the ref that got added in filelist_read
185 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
186 gtk_tree_model_foreach(store, vflist_store_clear_cb, NULL);
187 gtk_tree_store_clear(GTK_TREE_STORE(store));
190 void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set)
195 if (vflist_find_row(vf, fd, &iter) < 0) return;
196 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
197 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
200 static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
205 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
207 tpath = gtk_tree_model_get_path(store, iter);
208 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
209 gtk_tree_path_free(tpath);
214 *-----------------------------------------------------------------------------
216 *-----------------------------------------------------------------------------
219 static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
220 GtkSelectionData *selection_data, guint info,
221 guint time, gpointer data)
226 if (!VFLIST(vf)->click_fd) return;
228 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
230 list = vf_selection_get_list(vf);
234 list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
238 uri_selection_data_set_uris_from_filelist(selection_data, list);
242 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
246 vflist_color_set(vf, VFLIST(vf)->click_fd, TRUE);
248 if (VFLIST(vf)->thumbs_enabled &&
249 VFLIST(vf)->click_fd && VFLIST(vf)->click_fd->thumb_pixbuf)
253 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
254 items = vf_selection_count(vf, NULL);
258 dnd_set_drag_icon(widget, context, VFLIST(vf)->click_fd->thumb_pixbuf, items);
262 static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
266 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
268 if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE)
274 static void vflist_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context,
275 int x, int y, GtkSelectionData *selection,
276 guint info, guint time, gpointer data)
280 if (info == TARGET_TEXT_PLAIN) {
281 FileData *fd = vflist_find_data_by_coord(vf, x, y, NULL);
284 /* Add keywords to file */
285 gchar *str = (gchar *) gtk_selection_data_get_text(selection);
286 GList *kw_list = string_to_keywords_list(str);
288 metadata_append_list(fd, KEYWORD_KEY, kw_list);
289 string_list_free(kw_list);
295 void vflist_dnd_init(ViewFile *vf)
297 gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
298 dnd_file_drag_types, dnd_file_drag_types_count,
299 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
300 gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL,
301 dnd_file_drag_types, dnd_file_drag_types_count,
302 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
304 g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
305 G_CALLBACK(vflist_dnd_get), vf);
306 g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
307 G_CALLBACK(vflist_dnd_begin), vf);
308 g_signal_connect(G_OBJECT(vf->listview), "drag_end",
309 G_CALLBACK(vflist_dnd_end), vf);
310 g_signal_connect(G_OBJECT(vf->listview), "drag_data_received",
311 G_CALLBACK(vflist_drag_data_received), vf);
315 *-----------------------------------------------------------------------------
317 *-----------------------------------------------------------------------------
320 GList *vflist_selection_get_one(ViewFile *vf, FileData *fd)
322 GList *list = g_list_append(NULL, file_data_ref(fd));
324 if (fd->sidecar_files)
326 /* check if the row is expanded */
330 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
331 if (vflist_find_row(vf, fd, &iter) >= 0)
335 tpath = gtk_tree_model_get_path(store, &iter);
336 if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
338 /* unexpanded - add whole group */
339 GList *work = fd->sidecar_files;
342 FileData *sfd = work->data;
343 list = g_list_prepend(list, file_data_ref(sfd));
347 gtk_tree_path_free(tpath);
349 list = g_list_reverse(list);
355 GList *vflist_pop_menu_file_list(ViewFile *vf)
357 if (!VFLIST(vf)->click_fd) return NULL;
359 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
361 return vf_selection_get_list(vf);
363 return vflist_selection_get_one(vf, VFLIST(vf)->click_fd);
367 void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
371 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
375 list = vf_selection_get_list(vf);
376 view_window_new_from_list(list);
381 view_window_new(VFLIST(vf)->click_fd);
385 void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
390 list = vf_pop_menu_file_list(vf);
391 if (options->file_ops.enable_in_place_rename &&
392 list && !list->next && VFLIST(vf)->click_fd)
399 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
400 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
404 tpath = gtk_tree_model_get_path(store, &iter);
405 tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
406 FILE_VIEW_COLUMN_FORMATTED, VFLIST(vf)->click_fd->name,
407 vflist_row_rename_cb, vf);
408 gtk_tree_path_free(tpath);
413 file_util_rename(NULL, list, vf->listview);
416 void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
420 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
423 layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
427 vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled);
431 void vflist_star_rating_set(ViewFile *vf, gboolean enable)
433 GList *columns, *work;
435 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
440 GtkTreeViewColumn *column = work->data;
441 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
444 if (vflist_is_multiline(vf))
446 if (col_idx == FILE_COLUMN_FORMATTED_WITH_STARS)
448 gtk_tree_view_column_set_visible(column, enable);
450 if (col_idx == FILE_COLUMN_FORMATTED)
452 gtk_tree_view_column_set_visible(column, !enable);
457 if (col_idx == FILE_COLUMN_STAR_RATING)
459 gtk_tree_view_column_set_visible(column, enable);
463 g_list_free(columns);
466 void vflist_pop_menu_show_star_rating_cb(GtkWidget *widget, gpointer data)
470 options->show_star_rating = !options->show_star_rating;
472 vflist_populate_view(vf, TRUE);
474 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
475 vflist_star_rating_set(vf, options->show_star_rating);
478 void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
482 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
484 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
487 void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
490 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
491 VFLIST(vf)->click_fd = NULL;
497 *-----------------------------------------------------------------------------
499 *-----------------------------------------------------------------------------
502 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
507 if (!new || !new[0]) return FALSE;
509 new_path = g_build_filename(vf->dir_fd->path, new, NULL);
511 if (strchr(new, G_DIR_SEPARATOR) != NULL)
513 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
514 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
519 gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL);
520 FileData *fd = file_data_new_group(old_path); /* get the fd from cache */
521 file_util_rename_simple(fd, new_path, vf->listview);
531 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
539 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return;
540 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
541 tpath = gtk_tree_model_get_path(store, &iter);
542 tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
543 gtk_tree_path_free(tpath);
545 popup_menu_position_clamp(menu, x, y, 0);
548 gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
553 if (event->keyval != GDK_KEY_Menu) return FALSE;
555 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
561 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
562 gtk_tree_model_get_iter(store, &iter, tpath);
563 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
564 gtk_tree_path_free(tpath);
568 VFLIST(vf)->click_fd = NULL;
571 vf->popup = vf_pop_menu(vf);
572 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
577 gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
583 GtkTreeViewColumn *column;
585 vf->clicked_mark = 0;
587 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
588 &tpath, &column, NULL, NULL))
591 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
593 if (bevent->button == MOUSE_BUTTON_LEFT &&
594 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
597 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
598 vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
600 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
602 gtk_tree_model_get_iter(store, &iter, tpath);
603 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
604 gtk_tree_path_free(tpath);
607 VFLIST(vf)->click_fd = fd;
609 if (bevent->button == MOUSE_BUTTON_RIGHT)
611 vf->popup = vf_pop_menu(vf);
612 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
613 bevent->button, bevent->time);
617 if (!fd) return FALSE;
619 if (bevent->button == MOUSE_BUTTON_MIDDLE)
621 if (!vflist_row_is_selected(vf, fd))
623 vflist_color_set(vf, fd, TRUE);
629 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
630 !(bevent->state & GDK_SHIFT_MASK ) &&
631 !(bevent->state & GDK_CONTROL_MASK ) &&
632 vflist_row_is_selected(vf, fd))
634 GtkTreeSelection *selection;
636 gtk_widget_grab_focus(widget);
639 /* returning FALSE and further processing of the event is needed for
640 correct operation of the expander, to show the sidecar files.
641 It however resets the selection of multiple files. With this condition
642 it should work for both cases */
643 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
644 return (gtk_tree_selection_count_selected_rows(selection) > 1);
647 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
649 if (VFLIST(vf)->click_fd->format_class == FORMAT_CLASS_COLLECTION)
651 collection_window_new(VFLIST(vf)->click_fd->path);
655 if (vf->layout) layout_image_full_screen_start(vf->layout);
662 gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
669 if (bevent->button == MOUSE_BUTTON_MIDDLE)
671 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
674 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
679 if ((bevent->x != 0 || bevent->y != 0) &&
680 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
681 &tpath, NULL, NULL, NULL))
685 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
686 gtk_tree_model_get_iter(store, &iter, tpath);
687 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
688 gtk_tree_path_free(tpath);
691 if (bevent->button == MOUSE_BUTTON_MIDDLE)
693 if (fd && VFLIST(vf)->click_fd == fd)
695 GtkTreeSelection *selection;
697 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
698 if (vflist_row_is_selected(vf, fd))
700 gtk_tree_selection_unselect_iter(selection, &iter);
704 gtk_tree_selection_select_iter(selection, &iter);
710 if (fd && VFLIST(vf)->click_fd == fd &&
711 !(bevent->state & GDK_SHIFT_MASK ) &&
712 !(bevent->state & GDK_CONTROL_MASK ) &&
713 vflist_row_is_selected(vf, fd))
715 GtkTreeSelection *selection;
717 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
718 gtk_tree_selection_unselect_all(selection);
719 gtk_tree_selection_select_iter(selection, &iter);
720 vflist_move_cursor(vf, &iter);
726 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
728 FileData *read_ahead_fd = NULL;
734 cur_fd = layout_image_get_fd(vf->layout);
735 if (sel_fd == cur_fd) return; /* no change */
737 row = g_list_index(vf->list, sel_fd);
738 // FIXME sidecar data
740 if (sel_fd && options->image.enable_read_ahead && row >= 0)
742 if (row > g_list_index(vf->list, cur_fd) &&
743 (guint) (row + 1) < vf_count(vf, NULL))
745 read_ahead_fd = vf_index_get_data(vf, row + 1);
749 read_ahead_fd = vf_index_get_data(vf, row - 1);
753 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
756 static gboolean vflist_select_idle_cb(gpointer data)
762 VFLIST(vf)->select_idle_id = 0;
768 if (VFLIST(vf)->select_fd)
770 vflist_select_image(vf, VFLIST(vf)->select_fd);
771 VFLIST(vf)->select_fd = NULL;
774 VFLIST(vf)->select_idle_id = 0;
778 static void vflist_select_idle_cancel(ViewFile *vf)
780 if (VFLIST(vf)->select_idle_id)
782 g_source_remove(VFLIST(vf)->select_idle_id);
783 VFLIST(vf)->select_idle_id = 0;
787 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
788 gboolean path_currently_selected, gpointer data)
793 if (!path_currently_selected &&
794 gtk_tree_model_get_iter(store, &iter, tpath))
796 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
800 VFLIST(vf)->select_fd = NULL;
804 !VFLIST(vf)->select_idle_id)
806 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
812 static void vflist_expand_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
815 vflist_set_expanded(vf, iter, TRUE);
818 static void vflist_collapse_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
821 vflist_set_expanded(vf, iter, FALSE);
825 *-----------------------------------------------------------------------------
827 *-----------------------------------------------------------------------------
831 static gchar* vflist_get_formatted(ViewFile *vf, const gchar *name, const gchar *sidecars, const gchar *size, const gchar *time, gboolean expanded, gboolean with_stars, const gchar *star_rating)
833 gboolean multiline = vflist_is_multiline(vf);
840 text = g_strdup_printf("%s %s\n%s\n%s\n%s", name, expanded ? "" : sidecars, size, time, star_rating);
844 text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time);
849 text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars);
854 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded)
862 gchar *formatted_with_stars;
864 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
866 gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
867 FILE_COLUMN_NAME, &name,
868 FILE_COLUMN_SIDECARS, &sidecars,
869 FILE_COLUMN_SIZE, &size,
870 FILE_COLUMN_DATE, &time,
871 FILE_COLUMN_STAR_RATING, &star_rating,
874 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded, FALSE, NULL);
875 formatted_with_stars = vflist_get_formatted(vf, name, sidecars, size, time, expanded, TRUE, star_rating);
877 gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED, formatted,
878 FILE_COLUMN_EXPANDED, expanded,
880 gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED_WITH_STARS, formatted_with_stars,
881 FILE_COLUMN_EXPANDED, expanded,
890 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
893 gchar *sidecars = NULL;
895 const gchar *time = text_from_time(fd->date);
896 gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
897 const gchar *disabled_grouping;
899 gchar *formatted_with_stars;
900 gboolean expanded = FALSE;
903 if (options->show_star_rating)
905 star_rating = metadata_read_rating_stars(fd);
912 if (fd->sidecar_files) /* expanded has no effect on files without sidecars */
914 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_EXPANDED, &expanded, -1);
917 sidecars = file_data_sc_list_to_string(fd);
919 disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
920 name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
921 size = text_from_size(fd->size);
923 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded, FALSE, NULL);
924 formatted_with_stars = vflist_get_formatted(vf, name, sidecars, size, time, expanded, TRUE, star_rating);
926 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
927 FILE_COLUMN_VERSION, fd->version,
928 FILE_COLUMN_THUMB, fd->thumb_pixbuf,
929 FILE_COLUMN_FORMATTED, formatted,
930 FILE_COLUMN_FORMATTED_WITH_STARS, formatted_with_stars,
931 FILE_COLUMN_SIDECARS, sidecars,
932 FILE_COLUMN_NAME, name,
933 FILE_COLUMN_STAR_RATING, star_rating,
934 FILE_COLUMN_SIZE, size,
935 FILE_COLUMN_DATE, time,
936 #define STORE_SET_IS_SLOW 1
937 #if STORE_SET_IS_SLOW
938 /* this is 3x faster on a directory with 20000 files */
939 FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
940 FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
941 FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
942 FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
943 FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
944 FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
945 FILE_COLUMN_MARKS + 6, file_data_get_mark(fd, 6),
946 FILE_COLUMN_MARKS + 7, file_data_get_mark(fd, 7),
947 FILE_COLUMN_MARKS + 8, file_data_get_mark(fd, 8),
948 FILE_COLUMN_MARKS + 9, file_data_get_mark(fd, 9),
949 #if FILEDATA_MARKS_SIZE != 10
950 #error this needs to be updated
953 FILE_COLUMN_COLOR, FALSE, -1);
955 #if !STORE_SET_IS_SLOW
958 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
959 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
968 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected, gboolean force)
973 gint num_ordered = 0;
974 gint num_prepended = 0;
976 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
982 FileData *fd = work->data;
983 gboolean done = FALSE;
987 FileData *old_fd = NULL;
988 gint old_version = 0;
992 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
993 FILE_COLUMN_POINTER, &old_fd,
994 FILE_COLUMN_VERSION, &old_version,
1004 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
1006 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
1008 if (match == 0) g_warning("multiple fd for the same path");
1024 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
1029 here should be used gtk_tree_store_append, but this function seems to be O(n)
1030 and it seems to be much faster to add new entries to the beginning and reorder later
1033 gtk_tree_store_prepend(store, &new, parent_iter);
1036 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
1037 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected, force);
1039 if (g_list_find(selected, fd))
1041 /* renamed files - the same fd appears at different position - select it again*/
1042 GtkTreeSelection *selection;
1043 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1044 gtk_tree_selection_select_iter(selection, &new);
1051 file_data_unref(old_fd);
1052 valid = gtk_tree_store_remove(store, &iter);
1057 if (fd->version != old_version || force)
1059 vflist_setup_iter(vf, store, &iter, fd);
1060 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected, force);
1063 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1074 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1075 file_data_unref(old_fd);
1077 valid = gtk_tree_store_remove(store, &iter);
1080 /* move the prepended entries to the correct position */
1084 gint num_total = num_prepended + num_ordered;
1085 gint *new_order = g_malloc(num_total * sizeof(gint));
1087 for (i = 0; i < num_total; i++)
1089 if (i < num_ordered)
1090 new_order[i] = num_prepended + i;
1092 new_order[i] = num_total - 1 - i;
1094 gtk_tree_store_reorder(store, parent_iter, new_order);
1100 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
1103 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
1105 GtkTreeStore *store;
1108 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1109 if (!vf->list) return;
1115 FileData *fd = work->data;
1116 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1121 vf->sort_method = type;
1122 vf->sort_ascend = ascend;
1124 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1126 new_order = g_malloc(i * sizeof(gint));
1132 FileData *fd = work->data;
1133 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1138 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1139 gtk_tree_store_reorder(store, NULL, new_order);
1142 g_hash_table_destroy(fd_idx_hash);
1146 *-----------------------------------------------------------------------------
1148 *-----------------------------------------------------------------------------
1152 void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1157 FileData *fd = work->data;
1160 if (fd->thumb_pixbuf) (*done)++;
1162 if (fd->sidecar_files)
1164 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1170 void vflist_read_metadata_progress_count(GList *list, gint *count, gint *done)
1175 FileData *fd = work->data;
1178 if (fd->metadata_in_idle_loaded) (*done)++;
1180 if (fd->sidecar_files)
1182 vflist_read_metadata_progress_count(fd->sidecar_files, count, done);
1188 void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
1190 GtkTreeStore *store;
1193 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1195 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1196 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1199 FileData *vflist_thumb_next_fd(ViewFile *vf)
1202 FileData *fd = NULL;
1204 /* first check the visible files */
1206 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1208 GtkTreeModel *store;
1210 gboolean valid = TRUE;
1212 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1213 gtk_tree_model_get_iter(store, &iter, tpath);
1214 gtk_tree_path_free(tpath);
1217 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1221 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1223 if (!nfd->thumb_pixbuf) fd = nfd;
1225 valid = gtk_tree_model_iter_next(store, &iter);
1229 /* then find first undone */
1233 GList *work = vf->list;
1236 FileData *fd_p = work->data;
1237 if (!fd_p->thumb_pixbuf)
1241 GList *work2 = fd_p->sidecar_files;
1243 while (work2 && !fd)
1246 if (!fd_p->thumb_pixbuf) fd = fd_p;
1247 work2 = work2->next;
1258 *-----------------------------------------------------------------------------
1260 *-----------------------------------------------------------------------------
1263 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1266 GList *work, *work2;
1271 FileData *list_fd = work->data;
1272 if (list_fd == fd) return p;
1274 work2 = list_fd->sidecar_files;
1277 /* FIXME: return the same index also for sidecars
1278 it is sufficient for next/prev navigation but it should be rewritten
1279 without using indexes at all
1281 FileData *sidecar_fd = work2->data;
1282 if (sidecar_fd == fd) return p;
1283 work2 = work2->next;
1294 *-----------------------------------------------------------------------------
1296 *-----------------------------------------------------------------------------
1299 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1301 GtkTreeModel *store;
1302 GtkTreeSelection *selection;
1305 gboolean found = FALSE;
1307 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1308 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1310 while (!found && work)
1312 GtkTreePath *tpath = work->data;
1316 gtk_tree_model_get_iter(store, &iter, tpath);
1317 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1318 if (fd_n == fd) found = TRUE;
1321 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1327 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1331 fd = vf_index_get_data(vf, row);
1332 return vflist_row_is_selected(vf, fd);
1335 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1337 GtkTreeModel *store;
1338 GtkTreeSelection *selection;
1342 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1343 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1353 GtkTreePath *tpath = work->data;
1357 gtk_tree_model_get_iter(store, &iter, tpath);
1358 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1367 count = g_list_length(slist);
1368 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1374 GList *vflist_selection_get_list(ViewFile *vf)
1376 GtkTreeModel *store;
1377 GtkTreeSelection *selection;
1382 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1383 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1387 GtkTreePath *tpath = work->data;
1391 gtk_tree_model_get_iter(store, &iter, tpath);
1392 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1394 list = g_list_prepend(list, file_data_ref(fd));
1396 if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
1398 /* unexpanded - add whole group */
1399 GList *work2 = fd->sidecar_files;
1402 FileData *sfd = work2->data;
1403 list = g_list_prepend(list, file_data_ref(sfd));
1404 work2 = work2->next;
1410 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1413 return g_list_reverse(list);
1416 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1418 GtkTreeModel *store;
1419 GtkTreeSelection *selection;
1424 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1425 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1429 GtkTreePath *tpath = work->data;
1433 gtk_tree_model_get_iter(store, &iter, tpath);
1434 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1436 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1440 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1443 return g_list_reverse(list);
1446 void vflist_select_all(ViewFile *vf)
1448 GtkTreeSelection *selection;
1450 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1451 gtk_tree_selection_select_all(selection);
1453 VFLIST(vf)->select_fd = NULL;
1456 void vflist_select_none(ViewFile *vf)
1458 GtkTreeSelection *selection;
1460 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1461 gtk_tree_selection_unselect_all(selection);
1464 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1469 tpath = gtk_tree_model_get_path(store, iter);
1470 result = gtk_tree_path_prev(tpath);
1472 gtk_tree_model_get_iter(store, iter, tpath);
1474 gtk_tree_path_free(tpath);
1479 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1481 if (!gtk_tree_model_get_iter_first(store, iter))
1486 GtkTreeIter next = *iter;
1488 if (gtk_tree_model_iter_next(store, &next))
1497 void vflist_select_invert(ViewFile *vf)
1500 GtkTreeSelection *selection;
1501 GtkTreeModel *store;
1504 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1505 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1507 /* Backward iteration prevents scrolling to the end of the list,
1508 * it scrolls to the first selected row instead. */
1509 valid = tree_model_get_iter_last(store, &iter);
1513 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1516 gtk_tree_selection_unselect_iter(selection, &iter);
1518 gtk_tree_selection_select_iter(selection, &iter);
1520 valid = tree_model_iter_prev(store, &iter);
1524 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1528 if (vflist_find_row(vf, fd, &iter) < 0) return;
1530 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1532 if (!vflist_row_is_selected(vf, fd))
1534 GtkTreeSelection *selection;
1535 GtkTreeModel *store;
1538 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1539 gtk_tree_selection_unselect_all(selection);
1540 gtk_tree_selection_select_iter(selection, &iter);
1541 vflist_move_cursor(vf, &iter);
1543 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1544 tpath = gtk_tree_model_get_path(store, &iter);
1545 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1546 gtk_tree_path_free(tpath);
1550 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1553 FileData *fd = NULL;
1555 if (sel_fd->parent) sel_fd = sel_fd->parent;
1564 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1566 if (match >= 0) break;
1569 if (fd) vflist_select_by_fd(vf, fd);
1573 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1575 GtkTreeModel *store;
1577 GtkTreeSelection *selection;
1581 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1583 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1584 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1586 valid = gtk_tree_model_get_iter_first(store, &iter);
1590 gboolean mark_val, selected;
1591 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1593 mark_val = file_data_get_mark(fd, n);
1594 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1598 case MTS_MODE_SET: selected = mark_val;
1600 case MTS_MODE_OR: selected = mark_val || selected;
1602 case MTS_MODE_AND: selected = mark_val && selected;
1604 case MTS_MODE_MINUS: selected = !mark_val && selected;
1609 gtk_tree_selection_select_iter(selection, &iter);
1611 gtk_tree_selection_unselect_iter(selection, &iter);
1613 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1617 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1619 GtkTreeModel *store;
1620 GtkTreeSelection *selection;
1625 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1627 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1628 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1632 GtkTreePath *tpath = work->data;
1636 gtk_tree_model_get_iter(store, &iter, tpath);
1637 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1639 /* the change has a very limited range and the standard notification would trigger
1640 complete re-read of the directory - try to do only minimal update instead */
1641 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1645 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1647 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1649 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1653 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1655 vf_refresh_idle(vf);
1659 /* mark functions can have various side effects - update all columns to be sure */
1660 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1661 /* mark functions can change sidecars too */
1662 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1666 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1670 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1675 *-----------------------------------------------------------------------------
1677 *-----------------------------------------------------------------------------
1680 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
1682 GtkTreeViewColumn *column;
1683 GtkCellRenderer *cell;
1686 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1687 if (!column) return;
1689 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1691 list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1696 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1697 gtk_tree_view_column_set_visible(column, thumb);
1699 if (options->show_star_rating)
1701 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED_WITH_STARS);
1702 if (!column) return;
1703 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1704 gtk_tree_view_column_set_visible(column, TRUE);
1706 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1707 if (!column) return;
1708 gtk_tree_view_column_set_visible(column, FALSE);
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);
1715 gtk_tree_view_column_set_visible(column, TRUE);
1717 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED_WITH_STARS);
1718 if (!column) return;
1719 gtk_tree_view_column_set_visible(column, FALSE);
1722 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_STAR_RATING);
1723 if (!column) return;
1724 gtk_tree_view_column_set_visible(column, !multiline && options->show_star_rating);
1726 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1727 if (!column) return;
1728 gtk_tree_view_column_set_visible(column, !multiline);
1730 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1731 if (!column) return;
1732 gtk_tree_view_column_set_visible(column, !multiline);
1735 static gboolean vflist_is_multiline(ViewFile *vf)
1737 return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
1741 static void vflist_populate_view(ViewFile *vf, gboolean force)
1743 GtkTreeStore *store;
1746 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1752 vflist_store_clear(vf, FALSE);
1757 vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
1759 selected = vflist_selection_get_list(vf);
1761 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force);
1763 if (selected && vflist_selection_count(vf, NULL) == 0)
1765 /* all selected files disappeared */
1766 vflist_select_closest(vf, selected->data);
1769 filelist_free(selected);
1772 vf_thumb_update(vf);
1775 gboolean vflist_refresh(ViewFile *vf)
1778 gboolean ret = TRUE;
1780 old_list = vf->list;
1783 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1786 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1788 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1790 if (vf->marks_enabled)
1792 // When marks are enabled, lock FileDatas so that we don't end up re-parsing XML
1793 // each time a mark is changed.
1794 file_data_lock_list(vf->list);
1798 // FIXME: only do this when needed (aka when we just switched from
1799 // FIXME: marks-enabled to marks-disabled)
1800 file_data_unlock_list(vf->list);
1803 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1804 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1806 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1807 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1810 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1812 vflist_populate_view(vf, FALSE);
1814 DEBUG_1("%s vflist_refresh: free filelist", get_exec_time());
1816 filelist_free(old_list);
1817 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1824 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1826 #define CELL_HEIGHT_OVERRIDE 512
1828 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1832 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1833 if (spec && G_IS_PARAM_SPEC_INT(spec))
1835 GParamSpecInt *spec_int;
1837 spec_int = G_PARAM_SPEC_INT(spec);
1838 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1842 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1844 static GdkColor color;
1845 static GtkWidget *done = NULL;
1851 style = gtk_widget_get_style(widget);
1852 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1853 shift_color(&color, -1, 0);
1860 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1861 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1863 ViewFile *vf = data;
1866 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1867 g_object_set(G_OBJECT(cell),
1868 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1869 "cell-background-set", set, NULL);
1872 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1874 GtkTreeViewColumn *column;
1875 GtkCellRenderer *renderer;
1877 column = gtk_tree_view_column_new();
1878 gtk_tree_view_column_set_title(column, title);
1879 gtk_tree_view_column_set_min_width(column, 4);
1883 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1884 renderer = gtk_cell_renderer_text_new();
1887 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1889 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1890 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1892 gtk_tree_view_column_set_expand(column, TRUE);
1896 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1897 renderer = gtk_cell_renderer_pixbuf_new();
1898 cell_renderer_height_override(renderer);
1899 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1900 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1903 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1904 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1905 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1907 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1910 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1912 ViewFile *vf = data;
1913 GtkTreeStore *store;
1914 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1920 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1921 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1924 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1926 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1928 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1931 /* the change has a very limited range and the standard notification would trigger
1932 complete re-read of the directory - try to do only minimal update instead */
1933 file_data_unregister_notify_func(vf_notify_cb, vf);
1934 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1935 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1937 vf_refresh_idle(vf);
1941 /* mark functions can have various side effects - update all columns to be sure */
1942 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1943 /* mark functions can change sidecars too */
1944 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1946 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1948 gtk_tree_path_free(path);
1951 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1953 GtkTreeViewColumn *column;
1954 GtkCellRenderer *renderer;
1956 renderer = gtk_cell_renderer_toggle_new();
1957 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1959 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1960 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1961 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1963 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1964 gtk_tree_view_column_set_fixed_width(column, 22);
1965 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1968 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1972 *-----------------------------------------------------------------------------
1974 *-----------------------------------------------------------------------------
1977 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1980 if (!dir_fd) return FALSE;
1981 if (vf->dir_fd == dir_fd) return TRUE;
1983 file_data_unref(vf->dir_fd);
1984 vf->dir_fd = file_data_ref(dir_fd);
1986 /* force complete reload */
1987 vflist_store_clear(vf, TRUE);
1989 filelist_free(vf->list);
1992 ret = vf_refresh(vf);
1993 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
1997 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1999 ViewFile *vf = data;
2001 file_data_unregister_notify_func(vf_notify_cb, vf);
2003 vflist_select_idle_cancel(vf);
2004 vf_refresh_idle_cancel(vf);
2007 filelist_free(vf->list);
2010 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
2012 GtkTreeStore *store;
2013 GtkTreeSelection *selection;
2014 GType flist_types[FILE_COLUMN_COUNT];
2018 vf->info = g_new0(ViewFileInfoList, 1);
2020 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
2021 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
2022 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
2023 flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING;
2024 flist_types[FILE_COLUMN_FORMATTED_WITH_STARS] = G_TYPE_STRING;
2025 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
2026 flist_types[FILE_COLUMN_STAR_RATING] = G_TYPE_STRING;
2027 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
2028 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
2029 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
2030 flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN;
2031 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
2032 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
2033 flist_types[i] = G_TYPE_BOOLEAN;
2035 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
2037 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2038 g_object_unref(store);
2040 g_signal_connect(G_OBJECT(vf->listview), "row-expanded",
2041 G_CALLBACK(vflist_expand_cb), vf);
2043 g_signal_connect(G_OBJECT(vf->listview), "row-collapsed",
2044 G_CALLBACK(vflist_collapse_cb), vf);
2046 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2047 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
2048 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
2050 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2051 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2053 gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(vf->listview), FILE_COLUMN_FORMATTED);
2057 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2059 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2060 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
2064 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2065 g_assert(column == FILE_VIEW_COLUMN_THUMB);
2068 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE);
2069 g_assert(column == FILE_VIEW_COLUMN_FORMATTED);
2072 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED_WITH_STARS, _("NameStars"), FALSE, FALSE, TRUE);
2073 g_assert(column == FILE_VIEW_COLUMN_FORMATTED_WITH_STARS);
2076 vflist_listview_add_column(vf, FILE_COLUMN_STAR_RATING, _("Stars"), FALSE, FALSE, FALSE);
2077 g_assert(column == FILE_VIEW_COLUMN_STAR_RATING);
2080 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2081 g_assert(column == FILE_VIEW_COLUMN_SIZE);
2084 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2085 g_assert(column == FILE_VIEW_COLUMN_DATE);
2088 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2092 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2094 if (VFLIST(vf)->thumbs_enabled == enable) return;
2096 VFLIST(vf)->thumbs_enabled = enable;
2098 /* vflist_populate_view is better than vf_refresh:
2099 - no need to re-read the directory
2100 - force update because the formatted string has changed
2104 vflist_populate_view(vf, TRUE);
2105 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
2109 void vflist_marks_set(ViewFile *vf, gboolean enable)
2111 GList *columns, *work;
2113 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2118 GtkTreeViewColumn *column = work->data;
2119 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2122 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2123 gtk_tree_view_column_set_visible(column, enable);
2128 // Previously disabled, which means that vf->list is complete
2129 file_data_lock_list(vf->list);
2133 // Previously enabled, which means that vf->list is incomplete
2136 g_list_free(columns);
2139 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */