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"
40 #include "ui_tree_edit.h"
41 #include "uri_utils.h"
42 #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 (defined_mouse_buttons(widget, bevent, vf->layout))
674 if (bevent->button == MOUSE_BUTTON_MIDDLE)
676 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
679 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
684 if ((bevent->x != 0 || bevent->y != 0) &&
685 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
686 &tpath, NULL, NULL, NULL))
690 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
691 gtk_tree_model_get_iter(store, &iter, tpath);
692 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
693 gtk_tree_path_free(tpath);
696 if (bevent->button == MOUSE_BUTTON_MIDDLE)
698 if (fd && VFLIST(vf)->click_fd == fd)
700 GtkTreeSelection *selection;
702 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
703 if (vflist_row_is_selected(vf, fd))
705 gtk_tree_selection_unselect_iter(selection, &iter);
709 gtk_tree_selection_select_iter(selection, &iter);
715 if (fd && VFLIST(vf)->click_fd == fd &&
716 !(bevent->state & GDK_SHIFT_MASK ) &&
717 !(bevent->state & GDK_CONTROL_MASK ) &&
718 vflist_row_is_selected(vf, fd))
720 GtkTreeSelection *selection;
722 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
723 gtk_tree_selection_unselect_all(selection);
724 gtk_tree_selection_select_iter(selection, &iter);
725 vflist_move_cursor(vf, &iter);
731 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
733 FileData *read_ahead_fd = NULL;
739 cur_fd = layout_image_get_fd(vf->layout);
740 if (sel_fd == cur_fd) return; /* no change */
742 row = g_list_index(vf->list, sel_fd);
743 // FIXME sidecar data
745 if (sel_fd && options->image.enable_read_ahead && row >= 0)
747 if (row > g_list_index(vf->list, cur_fd) &&
748 (guint) (row + 1) < vf_count(vf, NULL))
750 read_ahead_fd = vf_index_get_data(vf, row + 1);
754 read_ahead_fd = vf_index_get_data(vf, row - 1);
758 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
761 static gboolean vflist_select_idle_cb(gpointer data)
767 VFLIST(vf)->select_idle_id = 0;
773 if (VFLIST(vf)->select_fd)
775 vflist_select_image(vf, VFLIST(vf)->select_fd);
776 VFLIST(vf)->select_fd = NULL;
779 VFLIST(vf)->select_idle_id = 0;
783 static void vflist_select_idle_cancel(ViewFile *vf)
785 if (VFLIST(vf)->select_idle_id)
787 g_source_remove(VFLIST(vf)->select_idle_id);
788 VFLIST(vf)->select_idle_id = 0;
792 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
793 gboolean path_currently_selected, gpointer data)
797 GtkTreePath *cursor_path;
799 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &cursor_path, NULL);
802 gtk_tree_model_get_iter(store, &iter, cursor_path);
803 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
804 gtk_tree_path_free(cursor_path);
808 VFLIST(vf)->select_fd = NULL;
812 !VFLIST(vf)->select_idle_id)
814 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
820 static void vflist_expand_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
823 vflist_set_expanded(vf, iter, TRUE);
826 static void vflist_collapse_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
829 vflist_set_expanded(vf, iter, FALSE);
833 *-----------------------------------------------------------------------------
835 *-----------------------------------------------------------------------------
839 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)
841 gboolean multiline = vflist_is_multiline(vf);
848 text = g_strdup_printf("%s %s\n%s\n%s\n%s", name, expanded ? "" : sidecars, size, time, star_rating);
852 text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time);
857 text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars);
862 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded)
870 gchar *formatted_with_stars;
872 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
874 gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
875 FILE_COLUMN_NAME, &name,
876 FILE_COLUMN_SIDECARS, &sidecars,
877 FILE_COLUMN_SIZE, &size,
878 FILE_COLUMN_DATE, &time,
879 FILE_COLUMN_STAR_RATING, &star_rating,
882 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded, FALSE, NULL);
883 formatted_with_stars = vflist_get_formatted(vf, name, sidecars, size, time, expanded, TRUE, star_rating);
885 gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED, formatted,
886 FILE_COLUMN_EXPANDED, expanded,
888 gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED_WITH_STARS, formatted_with_stars,
889 FILE_COLUMN_EXPANDED, expanded,
898 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
901 gchar *sidecars = NULL;
903 const gchar *time = text_from_time(fd->date);
904 gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
905 const gchar *disabled_grouping;
907 gchar *formatted_with_stars;
908 gboolean expanded = FALSE;
911 if (options->show_star_rating)
913 star_rating = metadata_read_rating_stars(fd);
920 if (fd->sidecar_files) /* expanded has no effect on files without sidecars */
922 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_EXPANDED, &expanded, -1);
925 sidecars = file_data_sc_list_to_string(fd);
927 disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
928 name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
929 size = text_from_size(fd->size);
931 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded, FALSE, NULL);
932 formatted_with_stars = vflist_get_formatted(vf, name, sidecars, size, time, expanded, TRUE, star_rating);
934 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
935 FILE_COLUMN_VERSION, fd->version,
936 FILE_COLUMN_THUMB, fd->thumb_pixbuf,
937 FILE_COLUMN_FORMATTED, formatted,
938 FILE_COLUMN_FORMATTED_WITH_STARS, formatted_with_stars,
939 FILE_COLUMN_SIDECARS, sidecars,
940 FILE_COLUMN_NAME, name,
941 FILE_COLUMN_STAR_RATING, star_rating,
942 FILE_COLUMN_SIZE, size,
943 FILE_COLUMN_DATE, time,
944 #define STORE_SET_IS_SLOW 1
945 #if STORE_SET_IS_SLOW
946 /* this is 3x faster on a directory with 20000 files */
947 FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
948 FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
949 FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
950 FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
951 FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
952 FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
953 FILE_COLUMN_MARKS + 6, file_data_get_mark(fd, 6),
954 FILE_COLUMN_MARKS + 7, file_data_get_mark(fd, 7),
955 FILE_COLUMN_MARKS + 8, file_data_get_mark(fd, 8),
956 FILE_COLUMN_MARKS + 9, file_data_get_mark(fd, 9),
957 #if FILEDATA_MARKS_SIZE != 10
958 #error this needs to be updated
961 FILE_COLUMN_COLOR, FALSE, -1);
963 #if !STORE_SET_IS_SLOW
966 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
967 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
976 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected, gboolean force)
981 gint num_ordered = 0;
982 gint num_prepended = 0;
984 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
990 FileData *fd = work->data;
991 gboolean done = FALSE;
995 FileData *old_fd = NULL;
996 gint old_version = 0;
1000 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1001 FILE_COLUMN_POINTER, &old_fd,
1002 FILE_COLUMN_VERSION, &old_version,
1012 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
1014 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
1016 if (match == 0) g_warning("multiple fd for the same path");
1032 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
1037 here should be used gtk_tree_store_append, but this function seems to be O(n)
1038 and it seems to be much faster to add new entries to the beginning and reorder later
1041 gtk_tree_store_prepend(store, &new, parent_iter);
1044 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
1045 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected, force);
1047 if (g_list_find(selected, fd))
1049 /* renamed files - the same fd appears at different position - select it again*/
1050 GtkTreeSelection *selection;
1051 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1052 gtk_tree_selection_select_iter(selection, &new);
1059 file_data_unref(old_fd);
1060 valid = gtk_tree_store_remove(store, &iter);
1065 if (fd->version != old_version || force)
1067 vflist_setup_iter(vf, store, &iter, fd);
1068 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected, force);
1071 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1082 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1083 file_data_unref(old_fd);
1085 valid = gtk_tree_store_remove(store, &iter);
1088 /* move the prepended entries to the correct position */
1092 gint num_total = num_prepended + num_ordered;
1093 gint *new_order = g_malloc(num_total * sizeof(gint));
1095 for (i = 0; i < num_total; i++)
1097 if (i < num_ordered)
1098 new_order[i] = num_prepended + i;
1100 new_order[i] = num_total - 1 - i;
1102 gtk_tree_store_reorder(store, parent_iter, new_order);
1108 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
1111 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
1113 GtkTreeStore *store;
1116 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1117 if (!vf->list) return;
1123 FileData *fd = work->data;
1124 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1129 vf->sort_method = type;
1130 vf->sort_ascend = ascend;
1132 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1134 new_order = g_malloc(i * sizeof(gint));
1140 FileData *fd = work->data;
1141 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1146 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1147 gtk_tree_store_reorder(store, NULL, new_order);
1150 g_hash_table_destroy(fd_idx_hash);
1154 *-----------------------------------------------------------------------------
1156 *-----------------------------------------------------------------------------
1160 void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1165 FileData *fd = work->data;
1168 if (fd->thumb_pixbuf) (*done)++;
1170 if (fd->sidecar_files)
1172 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1178 void vflist_read_metadata_progress_count(GList *list, gint *count, gint *done)
1183 FileData *fd = work->data;
1186 if (fd->metadata_in_idle_loaded) (*done)++;
1188 if (fd->sidecar_files)
1190 vflist_read_metadata_progress_count(fd->sidecar_files, count, done);
1196 void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
1198 GtkTreeStore *store;
1201 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1203 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1204 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1207 FileData *vflist_thumb_next_fd(ViewFile *vf)
1210 FileData *fd = NULL;
1212 /* first check the visible files */
1214 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1216 GtkTreeModel *store;
1218 gboolean valid = TRUE;
1220 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1221 gtk_tree_model_get_iter(store, &iter, tpath);
1222 gtk_tree_path_free(tpath);
1225 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1229 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1231 if (!nfd->thumb_pixbuf) fd = nfd;
1233 valid = gtk_tree_model_iter_next(store, &iter);
1237 /* then find first undone */
1241 GList *work = vf->list;
1244 FileData *fd_p = work->data;
1245 if (!fd_p->thumb_pixbuf)
1249 GList *work2 = fd_p->sidecar_files;
1251 while (work2 && !fd)
1254 if (!fd_p->thumb_pixbuf) fd = fd_p;
1255 work2 = work2->next;
1266 *-----------------------------------------------------------------------------
1268 *-----------------------------------------------------------------------------
1271 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1274 GList *work, *work2;
1279 FileData *list_fd = work->data;
1280 if (list_fd == fd) return p;
1282 work2 = list_fd->sidecar_files;
1285 /* FIXME: return the same index also for sidecars
1286 it is sufficient for next/prev navigation but it should be rewritten
1287 without using indexes at all
1289 FileData *sidecar_fd = work2->data;
1290 if (sidecar_fd == fd) return p;
1291 work2 = work2->next;
1302 *-----------------------------------------------------------------------------
1304 *-----------------------------------------------------------------------------
1307 void gtk_tree_path_free_wrapper_file_list(void *data, void *useradata)
1309 gtk_tree_path_free(data);
1313 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1315 GtkTreeModel *store;
1316 GtkTreeSelection *selection;
1319 gboolean found = FALSE;
1321 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1322 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1324 while (!found && work)
1326 GtkTreePath *tpath = work->data;
1330 gtk_tree_model_get_iter(store, &iter, tpath);
1331 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1332 if (fd_n == fd) found = TRUE;
1335 g_list_foreach(slist, (GFunc)gtk_tree_path_free_wrapper_file_list, NULL);
1341 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1345 fd = vf_index_get_data(vf, row);
1346 return vflist_row_is_selected(vf, fd);
1349 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1351 GtkTreeModel *store;
1352 GtkTreeSelection *selection;
1356 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1357 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1367 GtkTreePath *tpath = work->data;
1371 gtk_tree_model_get_iter(store, &iter, tpath);
1372 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1381 count = g_list_length(slist);
1382 g_list_foreach(slist, (GFunc)gtk_tree_path_free_wrapper_file_list, NULL);
1388 GList *vflist_selection_get_list(ViewFile *vf)
1390 GtkTreeModel *store;
1391 GtkTreeSelection *selection;
1396 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1397 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1401 GtkTreePath *tpath = work->data;
1405 gtk_tree_model_get_iter(store, &iter, tpath);
1406 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1408 list = g_list_prepend(list, file_data_ref(fd));
1410 if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
1412 /* unexpanded - add whole group */
1413 GList *work2 = fd->sidecar_files;
1416 FileData *sfd = work2->data;
1417 list = g_list_prepend(list, file_data_ref(sfd));
1418 work2 = work2->next;
1424 g_list_foreach(slist, (GFunc)gtk_tree_path_free_wrapper_file_list, NULL);
1427 return g_list_reverse(list);
1430 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1432 GtkTreeModel *store;
1433 GtkTreeSelection *selection;
1438 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1439 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1443 GtkTreePath *tpath = work->data;
1447 gtk_tree_model_get_iter(store, &iter, tpath);
1448 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1450 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1454 g_list_foreach(slist, (GFunc)gtk_tree_path_free_wrapper_file_list, NULL);
1457 return g_list_reverse(list);
1460 void vflist_select_all(ViewFile *vf)
1462 GtkTreeSelection *selection;
1464 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1465 gtk_tree_selection_select_all(selection);
1467 VFLIST(vf)->select_fd = NULL;
1470 void vflist_select_none(ViewFile *vf)
1472 GtkTreeSelection *selection;
1474 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1475 gtk_tree_selection_unselect_all(selection);
1478 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1483 tpath = gtk_tree_model_get_path(store, iter);
1484 result = gtk_tree_path_prev(tpath);
1486 gtk_tree_model_get_iter(store, iter, tpath);
1488 gtk_tree_path_free(tpath);
1493 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1495 if (!gtk_tree_model_get_iter_first(store, iter))
1500 GtkTreeIter next = *iter;
1502 if (gtk_tree_model_iter_next(store, &next))
1511 void vflist_select_invert(ViewFile *vf)
1514 GtkTreeSelection *selection;
1515 GtkTreeModel *store;
1518 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1519 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1521 /* Backward iteration prevents scrolling to the end of the list,
1522 * it scrolls to the first selected row instead. */
1523 valid = tree_model_get_iter_last(store, &iter);
1527 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1530 gtk_tree_selection_unselect_iter(selection, &iter);
1532 gtk_tree_selection_select_iter(selection, &iter);
1534 valid = tree_model_iter_prev(store, &iter);
1538 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1542 if (vflist_find_row(vf, fd, &iter) < 0) return;
1544 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1546 if (!vflist_row_is_selected(vf, fd))
1548 GtkTreeSelection *selection;
1549 GtkTreeModel *store;
1552 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1553 gtk_tree_selection_unselect_all(selection);
1554 gtk_tree_selection_select_iter(selection, &iter);
1555 vflist_move_cursor(vf, &iter);
1557 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1558 tpath = gtk_tree_model_get_path(store, &iter);
1559 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1560 gtk_tree_path_free(tpath);
1564 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1567 FileData *fd = NULL;
1569 if (sel_fd->parent) sel_fd = sel_fd->parent;
1578 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1580 if (match >= 0) break;
1583 if (fd) vflist_select_by_fd(vf, fd);
1587 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1589 GtkTreeModel *store;
1591 GtkTreeSelection *selection;
1595 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1597 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1598 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1600 valid = gtk_tree_model_get_iter_first(store, &iter);
1604 gboolean mark_val, selected;
1605 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1607 mark_val = file_data_get_mark(fd, n);
1608 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1612 case MTS_MODE_SET: selected = mark_val;
1614 case MTS_MODE_OR: selected = mark_val || selected;
1616 case MTS_MODE_AND: selected = mark_val && selected;
1618 case MTS_MODE_MINUS: selected = !mark_val && selected;
1623 gtk_tree_selection_select_iter(selection, &iter);
1625 gtk_tree_selection_unselect_iter(selection, &iter);
1627 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1631 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1633 GtkTreeModel *store;
1634 GtkTreeSelection *selection;
1639 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1641 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1642 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1646 GtkTreePath *tpath = work->data;
1650 gtk_tree_model_get_iter(store, &iter, tpath);
1651 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1653 /* the change has a very limited range and the standard notification would trigger
1654 complete re-read of the directory - try to do only minimal update instead */
1655 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1659 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1661 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1663 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1667 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1669 vf_refresh_idle(vf);
1673 /* mark functions can have various side effects - update all columns to be sure */
1674 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1675 /* mark functions can change sidecars too */
1676 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1680 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1684 g_list_foreach(slist, (GFunc)gtk_tree_path_free_wrapper_file_list, NULL);
1689 *-----------------------------------------------------------------------------
1691 *-----------------------------------------------------------------------------
1694 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
1696 GtkTreeViewColumn *column;
1697 GtkCellRenderer *cell;
1700 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1701 if (!column) return;
1703 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1705 list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1710 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1711 gtk_tree_view_column_set_visible(column, thumb);
1713 if (options->show_star_rating)
1715 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED_WITH_STARS);
1716 if (!column) return;
1717 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1718 gtk_tree_view_column_set_visible(column, TRUE);
1720 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1721 if (!column) return;
1722 gtk_tree_view_column_set_visible(column, FALSE);
1726 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1727 if (!column) return;
1728 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1729 gtk_tree_view_column_set_visible(column, TRUE);
1731 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED_WITH_STARS);
1732 if (!column) return;
1733 gtk_tree_view_column_set_visible(column, FALSE);
1736 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_STAR_RATING);
1737 if (!column) return;
1738 gtk_tree_view_column_set_visible(column, !multiline && options->show_star_rating);
1740 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1741 if (!column) return;
1742 gtk_tree_view_column_set_visible(column, !multiline);
1744 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1745 if (!column) return;
1746 gtk_tree_view_column_set_visible(column, !multiline);
1749 static gboolean vflist_is_multiline(ViewFile *vf)
1751 return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
1755 static void vflist_populate_view(ViewFile *vf, gboolean force)
1757 GtkTreeStore *store;
1760 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1766 vflist_store_clear(vf, FALSE);
1771 vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
1773 selected = vflist_selection_get_list(vf);
1775 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force);
1777 if (selected && vflist_selection_count(vf, NULL) == 0)
1779 /* all selected files disappeared */
1780 vflist_select_closest(vf, selected->data);
1783 filelist_free(selected);
1786 vf_thumb_update(vf);
1789 gboolean vflist_refresh(ViewFile *vf)
1792 gboolean ret = TRUE;
1794 old_list = vf->list;
1797 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1800 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1802 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1804 if (vf->marks_enabled)
1806 // When marks are enabled, lock FileDatas so that we don't end up re-parsing XML
1807 // each time a mark is changed.
1808 file_data_lock_list(vf->list);
1812 // FIXME: only do this when needed (aka when we just switched from
1813 // FIXME: marks-enabled to marks-disabled)
1814 file_data_unlock_list(vf->list);
1817 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1818 vf->list = g_list_first(vf->list);
1819 vf->list = file_data_filter_file_filter_list(vf->list, vf_file_filter_get_filter(vf));
1821 vf->list = g_list_first(vf->list);
1822 vf->list = file_data_filter_class_list(vf->list, vf_class_get_filter(vf));
1824 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1826 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1827 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1830 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1832 vflist_populate_view(vf, FALSE);
1834 DEBUG_1("%s vflist_refresh: free filelist", get_exec_time());
1836 filelist_free(old_list);
1837 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1844 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1846 #define CELL_HEIGHT_OVERRIDE 512
1848 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1852 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1853 if (spec && G_IS_PARAM_SPEC_INT(spec))
1855 GParamSpecInt *spec_int;
1857 spec_int = G_PARAM_SPEC_INT(spec);
1858 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1862 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1864 static GdkColor color;
1865 static GtkWidget *done = NULL;
1871 style = gtk_widget_get_style(widget);
1872 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1873 shift_color(&color, -1, 0);
1880 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1881 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1883 ViewFile *vf = data;
1886 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1887 g_object_set(G_OBJECT(cell),
1888 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1889 "cell-background-set", set, NULL);
1892 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1894 GtkTreeViewColumn *column;
1895 GtkCellRenderer *renderer;
1897 column = gtk_tree_view_column_new();
1898 gtk_tree_view_column_set_title(column, title);
1899 gtk_tree_view_column_set_min_width(column, 4);
1903 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1904 renderer = gtk_cell_renderer_text_new();
1907 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1909 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1910 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1912 gtk_tree_view_column_set_expand(column, TRUE);
1916 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1917 renderer = gtk_cell_renderer_pixbuf_new();
1918 cell_renderer_height_override(renderer);
1919 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1920 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1923 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1924 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1925 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1927 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1930 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1932 ViewFile *vf = data;
1933 GtkTreeStore *store;
1934 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1940 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1941 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1944 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1946 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1948 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1951 /* the change has a very limited range and the standard notification would trigger
1952 complete re-read of the directory - try to do only minimal update instead */
1953 file_data_unregister_notify_func(vf_notify_cb, vf);
1954 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1955 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1957 vf_refresh_idle(vf);
1961 /* mark functions can have various side effects - update all columns to be sure */
1962 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1963 /* mark functions can change sidecars too */
1964 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1966 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1968 gtk_tree_path_free(path);
1971 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1973 GtkTreeViewColumn *column;
1974 GtkCellRenderer *renderer;
1976 renderer = gtk_cell_renderer_toggle_new();
1977 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1979 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1980 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1981 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1983 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1984 gtk_tree_view_column_set_fixed_width(column, 22);
1985 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1988 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1992 *-----------------------------------------------------------------------------
1994 *-----------------------------------------------------------------------------
1997 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
2000 if (!dir_fd) return FALSE;
2001 if (vf->dir_fd == dir_fd) return TRUE;
2003 file_data_unref(vf->dir_fd);
2004 vf->dir_fd = file_data_ref(dir_fd);
2006 /* force complete reload */
2007 vflist_store_clear(vf, TRUE);
2009 filelist_free(vf->list);
2012 ret = vf_refresh(vf);
2013 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
2017 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
2019 ViewFile *vf = data;
2021 file_data_unregister_notify_func(vf_notify_cb, vf);
2023 vflist_select_idle_cancel(vf);
2024 vf_refresh_idle_cancel(vf);
2027 filelist_free(vf->list);
2030 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
2032 GtkTreeStore *store;
2033 GtkTreeSelection *selection;
2034 GType flist_types[FILE_COLUMN_COUNT];
2038 vf->info = g_new0(ViewFileInfoList, 1);
2040 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
2041 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
2042 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
2043 flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING;
2044 flist_types[FILE_COLUMN_FORMATTED_WITH_STARS] = G_TYPE_STRING;
2045 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
2046 flist_types[FILE_COLUMN_STAR_RATING] = G_TYPE_STRING;
2047 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
2048 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
2049 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
2050 flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN;
2051 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
2052 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
2053 flist_types[i] = G_TYPE_BOOLEAN;
2055 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
2057 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2058 g_object_unref(store);
2060 g_signal_connect(G_OBJECT(vf->listview), "row-expanded",
2061 G_CALLBACK(vflist_expand_cb), vf);
2063 g_signal_connect(G_OBJECT(vf->listview), "row-collapsed",
2064 G_CALLBACK(vflist_collapse_cb), vf);
2066 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2067 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
2068 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
2070 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2071 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2073 gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(vf->listview), FILE_COLUMN_FORMATTED);
2077 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2079 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2080 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
2084 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2085 g_assert(column == FILE_VIEW_COLUMN_THUMB);
2088 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE);
2089 g_assert(column == FILE_VIEW_COLUMN_FORMATTED);
2092 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED_WITH_STARS, _("NameStars"), FALSE, FALSE, TRUE);
2093 g_assert(column == FILE_VIEW_COLUMN_FORMATTED_WITH_STARS);
2096 vflist_listview_add_column(vf, FILE_COLUMN_STAR_RATING, _("Stars"), FALSE, FALSE, FALSE);
2097 g_assert(column == FILE_VIEW_COLUMN_STAR_RATING);
2100 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2101 g_assert(column == FILE_VIEW_COLUMN_SIZE);
2104 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2105 g_assert(column == FILE_VIEW_COLUMN_DATE);
2108 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2112 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2114 if (VFLIST(vf)->thumbs_enabled == enable) return;
2116 VFLIST(vf)->thumbs_enabled = enable;
2118 /* vflist_populate_view is better than vf_refresh:
2119 - no need to re-read the directory
2120 - force update because the formatted string has changed
2124 vflist_populate_view(vf, TRUE);
2125 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
2129 void vflist_marks_set(ViewFile *vf, gboolean enable)
2131 GList *columns, *work;
2133 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2138 GtkTreeViewColumn *column = work->data;
2139 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2142 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2143 gtk_tree_view_column_set_visible(column, enable);
2148 // Previously disabled, which means that vf->list is complete
2149 file_data_lock_list(vf->list);
2153 // Previously enabled, which means that vf->list is incomplete
2156 g_list_free(columns);
2159 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */