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"
31 #include "layout_image.h"
36 #include "ui_fileops.h"
38 #include "ui_tree_edit.h"
39 #include "uri_utils.h"
40 #include "view_file.h"
42 #include <gdk/gdkkeysyms.h> /* for keyboard values */
44 /* Index to tree store */
46 FILE_COLUMN_POINTER = 0,
49 FILE_COLUMN_FORMATTED,
50 FILE_COLUMN_FORMATTED_WITH_STARS,
53 FILE_COLUMN_STAR_RATING,
59 FILE_COLUMN_MARKS_LAST = FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
64 /* Index to tree view */
66 FILE_VIEW_COLUMN_MARKS = 0,
67 FILE_VIEW_COLUMN_MARKS_LAST = FILE_VIEW_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
68 FILE_VIEW_COLUMN_THUMB,
69 FILE_VIEW_COLUMN_FORMATTED,
70 FILE_VIEW_COLUMN_FORMATTED_WITH_STARS,
71 FILE_VIEW_COLUMN_STAR_RATING,
72 FILE_VIEW_COLUMN_SIZE,
73 FILE_VIEW_COLUMN_DATE,
74 FILE_VIEW_COLUMN_COUNT
79 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd);
80 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data);
81 static void vflist_populate_view(ViewFile *vf, gboolean force);
82 static gboolean vflist_is_multiline(ViewFile *vf);
83 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded);
87 *-----------------------------------------------------------------------------
89 *-----------------------------------------------------------------------------
96 } ViewFileFindRowData;
98 static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
100 ViewFileFindRowData *find = data;
102 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
113 static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter)
116 ViewFileFindRowData data = {fd, iter, FALSE, 0};
118 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
119 gtk_tree_model_foreach(store, vflist_find_row_cb, &data);
129 static FileData *vflist_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter)
132 GtkTreeViewColumn *column;
134 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), x, y,
135 &tpath, &column, NULL, NULL))
141 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
142 gtk_tree_model_get_iter(store, &row, tpath);
143 gtk_tree_path_free(tpath);
144 gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &fd, -1);
152 static gboolean vflist_store_clear_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
155 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
157 /* it seems that gtk_tree_store_clear may call some callbacks
158 that use the column. Set the pointer to NULL to be safe. */
159 gtk_tree_store_set(GTK_TREE_STORE(model), iter, FILE_COLUMN_POINTER, NULL, -1);
164 static void vflist_store_clear(ViewFile *vf, gboolean unlock_files)
169 if (unlock_files && vf->marks_enabled)
171 // unlock locked files in this directory
172 filelist_read(vf->dir_fd, &files, NULL);
175 FileData *fd = files->data;
177 file_data_unlock(fd);
178 file_data_unref(fd); // undo the ref that got added in filelist_read
183 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
184 gtk_tree_model_foreach(store, vflist_store_clear_cb, NULL);
185 gtk_tree_store_clear(GTK_TREE_STORE(store));
188 void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set)
193 if (vflist_find_row(vf, fd, &iter) < 0) return;
194 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
195 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
198 static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
203 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
205 tpath = gtk_tree_model_get_path(store, iter);
206 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
207 gtk_tree_path_free(tpath);
212 *-----------------------------------------------------------------------------
214 *-----------------------------------------------------------------------------
217 static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
218 GtkSelectionData *selection_data, guint info,
219 guint time, gpointer data)
224 if (!VFLIST(vf)->click_fd) return;
226 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
228 list = vf_selection_get_list(vf);
232 list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
236 uri_selection_data_set_uris_from_filelist(selection_data, list);
240 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
244 vflist_color_set(vf, VFLIST(vf)->click_fd, TRUE);
246 if (VFLIST(vf)->thumbs_enabled &&
247 VFLIST(vf)->click_fd && VFLIST(vf)->click_fd->thumb_pixbuf)
251 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
252 items = vf_selection_count(vf, NULL);
256 dnd_set_drag_icon(widget, context, VFLIST(vf)->click_fd->thumb_pixbuf, items);
260 static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
264 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
266 if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE)
272 static void vflist_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context,
273 int x, int y, GtkSelectionData *selection,
274 guint info, guint time, gpointer data)
278 if (info == TARGET_TEXT_PLAIN) {
279 FileData *fd = vflist_find_data_by_coord(vf, x, y, NULL);
282 /* Add keywords to file */
283 gchar *str = (gchar *) gtk_selection_data_get_text(selection);
284 GList *kw_list = string_to_keywords_list(str);
286 metadata_append_list(fd, KEYWORD_KEY, kw_list);
287 string_list_free(kw_list);
293 void vflist_dnd_init(ViewFile *vf)
295 gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
296 dnd_file_drag_types, dnd_file_drag_types_count,
297 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
298 gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL,
299 dnd_file_drag_types, dnd_file_drag_types_count,
300 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
302 g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
303 G_CALLBACK(vflist_dnd_get), vf);
304 g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
305 G_CALLBACK(vflist_dnd_begin), vf);
306 g_signal_connect(G_OBJECT(vf->listview), "drag_end",
307 G_CALLBACK(vflist_dnd_end), vf);
308 g_signal_connect(G_OBJECT(vf->listview), "drag_data_received",
309 G_CALLBACK(vflist_drag_data_received), vf);
313 *-----------------------------------------------------------------------------
315 *-----------------------------------------------------------------------------
318 GList *vflist_selection_get_one(ViewFile *vf, FileData *fd)
320 GList *list = g_list_append(NULL, file_data_ref(fd));
322 if (fd->sidecar_files)
324 /* check if the row is expanded */
328 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
329 if (vflist_find_row(vf, fd, &iter) >= 0)
333 tpath = gtk_tree_model_get_path(store, &iter);
334 if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
336 /* unexpanded - add whole group */
337 GList *work = fd->sidecar_files;
340 FileData *sfd = work->data;
341 list = g_list_prepend(list, file_data_ref(sfd));
345 gtk_tree_path_free(tpath);
347 list = g_list_reverse(list);
353 GList *vflist_pop_menu_file_list(ViewFile *vf)
355 if (!VFLIST(vf)->click_fd) return NULL;
357 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
359 return vf_selection_get_list(vf);
361 return vflist_selection_get_one(vf, VFLIST(vf)->click_fd);
365 void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
369 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
373 list = vf_selection_get_list(vf);
374 view_window_new_from_list(list);
379 view_window_new(VFLIST(vf)->click_fd);
383 void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
388 list = vf_pop_menu_file_list(vf);
389 if (options->file_ops.enable_in_place_rename &&
390 list && !list->next && VFLIST(vf)->click_fd)
397 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
398 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
402 tpath = gtk_tree_model_get_path(store, &iter);
403 tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
404 FILE_VIEW_COLUMN_FORMATTED, VFLIST(vf)->click_fd->name,
405 vflist_row_rename_cb, vf);
406 gtk_tree_path_free(tpath);
411 file_util_rename(NULL, list, vf->listview);
414 void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
418 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
421 layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
425 vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled);
429 void vflist_star_rating_set(ViewFile *vf, gboolean enable)
431 GList *columns, *work;
433 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
438 GtkTreeViewColumn *column = work->data;
439 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
442 if (vflist_is_multiline(vf))
444 if (col_idx == FILE_COLUMN_FORMATTED_WITH_STARS)
446 gtk_tree_view_column_set_visible(column, enable);
448 if (col_idx == FILE_COLUMN_FORMATTED)
450 gtk_tree_view_column_set_visible(column, !enable);
455 if (col_idx == FILE_COLUMN_STAR_RATING)
457 gtk_tree_view_column_set_visible(column, enable);
461 g_list_free(columns);
464 void vflist_pop_menu_show_star_rating_cb(GtkWidget *widget, gpointer data)
468 options->show_star_rating = !options->show_star_rating;
470 vflist_populate_view(vf, TRUE);
472 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
473 vflist_star_rating_set(vf, options->show_star_rating);
476 void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
480 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
482 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
485 void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
488 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
489 VFLIST(vf)->click_fd = NULL;
495 *-----------------------------------------------------------------------------
497 *-----------------------------------------------------------------------------
500 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
505 if (!new || !new[0]) return FALSE;
507 new_path = g_build_filename(vf->dir_fd->path, new, NULL);
509 if (strchr(new, G_DIR_SEPARATOR) != NULL)
511 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
512 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
517 gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL);
518 FileData *fd = file_data_new_group(old_path); /* get the fd from cache */
519 file_util_rename_simple(fd, new_path, vf->listview);
529 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
537 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return;
538 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
539 tpath = gtk_tree_model_get_path(store, &iter);
540 tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
541 gtk_tree_path_free(tpath);
543 popup_menu_position_clamp(menu, x, y, 0);
546 gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
551 if (event->keyval != GDK_KEY_Menu) return FALSE;
553 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
559 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
560 gtk_tree_model_get_iter(store, &iter, tpath);
561 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
562 gtk_tree_path_free(tpath);
566 VFLIST(vf)->click_fd = NULL;
569 vf->popup = vf_pop_menu(vf);
570 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
575 gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
581 GtkTreeViewColumn *column;
583 vf->clicked_mark = 0;
585 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
586 &tpath, &column, NULL, NULL))
589 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
591 if (bevent->button == MOUSE_BUTTON_LEFT &&
592 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
595 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
596 vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
598 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
600 gtk_tree_model_get_iter(store, &iter, tpath);
601 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
602 gtk_tree_path_free(tpath);
605 VFLIST(vf)->click_fd = fd;
607 if (bevent->button == MOUSE_BUTTON_RIGHT)
609 vf->popup = vf_pop_menu(vf);
610 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
611 bevent->button, bevent->time);
615 if (!fd) return FALSE;
617 if (bevent->button == MOUSE_BUTTON_MIDDLE)
619 if (!vflist_row_is_selected(vf, fd))
621 vflist_color_set(vf, fd, TRUE);
627 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
628 !(bevent->state & GDK_SHIFT_MASK ) &&
629 !(bevent->state & GDK_CONTROL_MASK ) &&
630 vflist_row_is_selected(vf, fd))
632 GtkTreeSelection *selection;
634 gtk_widget_grab_focus(widget);
637 /* returning FALSE and further processing of the event is needed for
638 correct operation of the expander, to show the sidecar files.
639 It however resets the selection of multiple files. With this condition
640 it should work for both cases */
641 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
642 return (gtk_tree_selection_count_selected_rows(selection) > 1);
645 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
647 if (vf->layout) layout_image_full_screen_start(vf->layout);
653 gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
660 if (bevent->button == MOUSE_BUTTON_MIDDLE)
662 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
665 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
670 if ((bevent->x != 0 || bevent->y != 0) &&
671 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
672 &tpath, NULL, NULL, NULL))
676 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
677 gtk_tree_model_get_iter(store, &iter, tpath);
678 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
679 gtk_tree_path_free(tpath);
682 if (bevent->button == MOUSE_BUTTON_MIDDLE)
684 if (fd && VFLIST(vf)->click_fd == fd)
686 GtkTreeSelection *selection;
688 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
689 if (vflist_row_is_selected(vf, fd))
691 gtk_tree_selection_unselect_iter(selection, &iter);
695 gtk_tree_selection_select_iter(selection, &iter);
701 if (fd && VFLIST(vf)->click_fd == fd &&
702 !(bevent->state & GDK_SHIFT_MASK ) &&
703 !(bevent->state & GDK_CONTROL_MASK ) &&
704 vflist_row_is_selected(vf, fd))
706 GtkTreeSelection *selection;
708 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
709 gtk_tree_selection_unselect_all(selection);
710 gtk_tree_selection_select_iter(selection, &iter);
711 vflist_move_cursor(vf, &iter);
717 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
719 FileData *read_ahead_fd = NULL;
725 cur_fd = layout_image_get_fd(vf->layout);
726 if (sel_fd == cur_fd) return; /* no change */
728 row = g_list_index(vf->list, sel_fd);
729 // FIXME sidecar data
731 if (sel_fd && options->image.enable_read_ahead && row >= 0)
733 if (row > g_list_index(vf->list, cur_fd) &&
734 (guint) (row + 1) < vf_count(vf, NULL))
736 read_ahead_fd = vf_index_get_data(vf, row + 1);
740 read_ahead_fd = vf_index_get_data(vf, row - 1);
744 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
747 static gboolean vflist_select_idle_cb(gpointer data)
753 VFLIST(vf)->select_idle_id = 0;
759 if (VFLIST(vf)->select_fd)
761 vflist_select_image(vf, VFLIST(vf)->select_fd);
762 VFLIST(vf)->select_fd = NULL;
765 VFLIST(vf)->select_idle_id = 0;
769 static void vflist_select_idle_cancel(ViewFile *vf)
771 if (VFLIST(vf)->select_idle_id)
773 g_source_remove(VFLIST(vf)->select_idle_id);
774 VFLIST(vf)->select_idle_id = 0;
778 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
779 gboolean path_currently_selected, gpointer data)
784 if (!path_currently_selected &&
785 gtk_tree_model_get_iter(store, &iter, tpath))
787 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
791 VFLIST(vf)->select_fd = NULL;
795 !VFLIST(vf)->select_idle_id)
797 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
803 static void vflist_expand_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
806 vflist_set_expanded(vf, iter, TRUE);
809 static void vflist_collapse_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
812 vflist_set_expanded(vf, iter, FALSE);
816 *-----------------------------------------------------------------------------
818 *-----------------------------------------------------------------------------
822 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)
824 gboolean multiline = vflist_is_multiline(vf);
831 text = g_strdup_printf("%s %s\n%s\n%s\n%s", name, expanded ? "" : sidecars, size, time, star_rating);
835 text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time);
840 text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars);
845 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded)
853 gchar *formatted_with_stars;
855 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
857 gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
858 FILE_COLUMN_NAME, &name,
859 FILE_COLUMN_SIDECARS, &sidecars,
860 FILE_COLUMN_SIZE, &size,
861 FILE_COLUMN_DATE, &time,
862 FILE_COLUMN_STAR_RATING, &star_rating,
865 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded, FALSE, NULL);
866 formatted_with_stars = vflist_get_formatted(vf, name, sidecars, size, time, expanded, TRUE, star_rating);
868 gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED, formatted,
869 FILE_COLUMN_EXPANDED, expanded,
871 gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED_WITH_STARS, formatted_with_stars,
872 FILE_COLUMN_EXPANDED, expanded,
881 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
884 gchar *sidecars = NULL;
886 const gchar *time = text_from_time(fd->date);
887 gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
888 const gchar *disabled_grouping;
890 gchar *formatted_with_stars;
891 gboolean expanded = FALSE;
894 if (options->show_star_rating)
896 star_rating = metadata_read_rating_stars(fd);
903 if (fd->sidecar_files) /* expanded has no effect on files without sidecars */
905 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_EXPANDED, &expanded, -1);
908 sidecars = file_data_sc_list_to_string(fd);
910 disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
911 name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
912 size = text_from_size(fd->size);
914 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded, FALSE, NULL);
915 formatted_with_stars = vflist_get_formatted(vf, name, sidecars, size, time, expanded, TRUE, star_rating);
917 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
918 FILE_COLUMN_VERSION, fd->version,
919 FILE_COLUMN_THUMB, fd->thumb_pixbuf,
920 FILE_COLUMN_FORMATTED, formatted,
921 FILE_COLUMN_FORMATTED_WITH_STARS, formatted_with_stars,
922 FILE_COLUMN_SIDECARS, sidecars,
923 FILE_COLUMN_NAME, name,
924 FILE_COLUMN_STAR_RATING, star_rating,
925 FILE_COLUMN_SIZE, size,
926 FILE_COLUMN_DATE, time,
927 #define STORE_SET_IS_SLOW 1
928 #if STORE_SET_IS_SLOW
929 /* this is 3x faster on a directory with 20000 files */
930 FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
931 FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
932 FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
933 FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
934 FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
935 FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
936 FILE_COLUMN_MARKS + 6, file_data_get_mark(fd, 6),
937 FILE_COLUMN_MARKS + 7, file_data_get_mark(fd, 7),
938 FILE_COLUMN_MARKS + 8, file_data_get_mark(fd, 8),
939 FILE_COLUMN_MARKS + 9, file_data_get_mark(fd, 9),
940 #if FILEDATA_MARKS_SIZE != 10
941 #error this needs to be updated
944 FILE_COLUMN_COLOR, FALSE, -1);
946 #if !STORE_SET_IS_SLOW
949 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
950 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
959 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected, gboolean force)
964 gint num_ordered = 0;
965 gint num_prepended = 0;
967 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
973 FileData *fd = work->data;
974 gboolean done = FALSE;
978 FileData *old_fd = NULL;
979 gint old_version = 0;
983 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
984 FILE_COLUMN_POINTER, &old_fd,
985 FILE_COLUMN_VERSION, &old_version,
995 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
997 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
999 if (match == 0) g_warning("multiple fd for the same path");
1015 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
1020 here should be used gtk_tree_store_append, but this function seems to be O(n)
1021 and it seems to be much faster to add new entries to the beginning and reorder later
1024 gtk_tree_store_prepend(store, &new, parent_iter);
1027 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
1028 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected, force);
1030 if (g_list_find(selected, fd))
1032 /* renamed files - the same fd appears at different position - select it again*/
1033 GtkTreeSelection *selection;
1034 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1035 gtk_tree_selection_select_iter(selection, &new);
1042 file_data_unref(old_fd);
1043 valid = gtk_tree_store_remove(store, &iter);
1048 if (fd->version != old_version || force)
1050 vflist_setup_iter(vf, store, &iter, fd);
1051 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected, force);
1054 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1065 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1066 file_data_unref(old_fd);
1068 valid = gtk_tree_store_remove(store, &iter);
1071 /* move the prepended entries to the correct position */
1075 gint num_total = num_prepended + num_ordered;
1076 gint *new_order = g_malloc(num_total * sizeof(gint));
1078 for (i = 0; i < num_total; i++)
1080 if (i < num_ordered)
1081 new_order[i] = num_prepended + i;
1083 new_order[i] = num_total - 1 - i;
1085 gtk_tree_store_reorder(store, parent_iter, new_order);
1091 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
1094 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
1096 GtkTreeStore *store;
1099 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1100 if (!vf->list) return;
1106 FileData *fd = work->data;
1107 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1112 vf->sort_method = type;
1113 vf->sort_ascend = ascend;
1115 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1117 new_order = g_malloc(i * sizeof(gint));
1123 FileData *fd = work->data;
1124 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1129 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1130 gtk_tree_store_reorder(store, NULL, new_order);
1133 g_hash_table_destroy(fd_idx_hash);
1137 *-----------------------------------------------------------------------------
1139 *-----------------------------------------------------------------------------
1143 void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1148 FileData *fd = work->data;
1151 if (fd->thumb_pixbuf) (*done)++;
1153 if (fd->sidecar_files)
1155 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1161 void vflist_read_metadata_progress_count(GList *list, gint *count, gint *done)
1166 FileData *fd = work->data;
1169 if (fd->metadata_in_idle_loaded) (*done)++;
1171 if (fd->sidecar_files)
1173 vflist_read_metadata_progress_count(fd->sidecar_files, count, done);
1179 void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
1181 GtkTreeStore *store;
1184 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1186 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1187 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1190 FileData *vflist_thumb_next_fd(ViewFile *vf)
1193 FileData *fd = NULL;
1195 /* first check the visible files */
1197 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1199 GtkTreeModel *store;
1201 gboolean valid = TRUE;
1203 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1204 gtk_tree_model_get_iter(store, &iter, tpath);
1205 gtk_tree_path_free(tpath);
1208 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1212 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1214 if (!nfd->thumb_pixbuf) fd = nfd;
1216 valid = gtk_tree_model_iter_next(store, &iter);
1220 /* then find first undone */
1224 GList *work = vf->list;
1227 FileData *fd_p = work->data;
1228 if (!fd_p->thumb_pixbuf)
1232 GList *work2 = fd_p->sidecar_files;
1234 while (work2 && !fd)
1237 if (!fd_p->thumb_pixbuf) fd = fd_p;
1238 work2 = work2->next;
1249 *-----------------------------------------------------------------------------
1251 *-----------------------------------------------------------------------------
1254 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1257 GList *work, *work2;
1262 FileData *list_fd = work->data;
1263 if (list_fd == fd) return p;
1265 work2 = list_fd->sidecar_files;
1268 /* FIXME: return the same index also for sidecars
1269 it is sufficient for next/prev navigation but it should be rewritten
1270 without using indexes at all
1272 FileData *sidecar_fd = work2->data;
1273 if (sidecar_fd == fd) return p;
1274 work2 = work2->next;
1285 *-----------------------------------------------------------------------------
1287 *-----------------------------------------------------------------------------
1290 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1292 GtkTreeModel *store;
1293 GtkTreeSelection *selection;
1296 gboolean found = FALSE;
1298 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1299 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1301 while (!found && work)
1303 GtkTreePath *tpath = work->data;
1307 gtk_tree_model_get_iter(store, &iter, tpath);
1308 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1309 if (fd_n == fd) found = TRUE;
1312 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1318 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1322 fd = vf_index_get_data(vf, row);
1323 return vflist_row_is_selected(vf, fd);
1326 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1328 GtkTreeModel *store;
1329 GtkTreeSelection *selection;
1333 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1334 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1344 GtkTreePath *tpath = work->data;
1348 gtk_tree_model_get_iter(store, &iter, tpath);
1349 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1358 count = g_list_length(slist);
1359 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1365 GList *vflist_selection_get_list(ViewFile *vf)
1367 GtkTreeModel *store;
1368 GtkTreeSelection *selection;
1373 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1374 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1378 GtkTreePath *tpath = work->data;
1382 gtk_tree_model_get_iter(store, &iter, tpath);
1383 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1385 list = g_list_prepend(list, file_data_ref(fd));
1387 if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
1389 /* unexpanded - add whole group */
1390 GList *work2 = fd->sidecar_files;
1393 FileData *sfd = work2->data;
1394 list = g_list_prepend(list, file_data_ref(sfd));
1395 work2 = work2->next;
1401 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1404 return g_list_reverse(list);
1407 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1409 GtkTreeModel *store;
1410 GtkTreeSelection *selection;
1415 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1416 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1420 GtkTreePath *tpath = work->data;
1424 gtk_tree_model_get_iter(store, &iter, tpath);
1425 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1427 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1431 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1434 return g_list_reverse(list);
1437 void vflist_select_all(ViewFile *vf)
1439 GtkTreeSelection *selection;
1441 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1442 gtk_tree_selection_select_all(selection);
1444 VFLIST(vf)->select_fd = NULL;
1447 void vflist_select_none(ViewFile *vf)
1449 GtkTreeSelection *selection;
1451 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1452 gtk_tree_selection_unselect_all(selection);
1455 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1460 tpath = gtk_tree_model_get_path(store, iter);
1461 result = gtk_tree_path_prev(tpath);
1463 gtk_tree_model_get_iter(store, iter, tpath);
1465 gtk_tree_path_free(tpath);
1470 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1472 if (!gtk_tree_model_get_iter_first(store, iter))
1477 GtkTreeIter next = *iter;
1479 if (gtk_tree_model_iter_next(store, &next))
1488 void vflist_select_invert(ViewFile *vf)
1491 GtkTreeSelection *selection;
1492 GtkTreeModel *store;
1495 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1496 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1498 /* Backward iteration prevents scrolling to the end of the list,
1499 * it scrolls to the first selected row instead. */
1500 valid = tree_model_get_iter_last(store, &iter);
1504 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1507 gtk_tree_selection_unselect_iter(selection, &iter);
1509 gtk_tree_selection_select_iter(selection, &iter);
1511 valid = tree_model_iter_prev(store, &iter);
1515 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1519 if (vflist_find_row(vf, fd, &iter) < 0) return;
1521 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1523 if (!vflist_row_is_selected(vf, fd))
1525 GtkTreeSelection *selection;
1526 GtkTreeModel *store;
1529 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1530 gtk_tree_selection_unselect_all(selection);
1531 gtk_tree_selection_select_iter(selection, &iter);
1532 vflist_move_cursor(vf, &iter);
1534 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1535 tpath = gtk_tree_model_get_path(store, &iter);
1536 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1537 gtk_tree_path_free(tpath);
1541 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1544 FileData *fd = NULL;
1546 if (sel_fd->parent) sel_fd = sel_fd->parent;
1555 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1557 if (match >= 0) break;
1560 if (fd) vflist_select_by_fd(vf, fd);
1564 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1566 GtkTreeModel *store;
1568 GtkTreeSelection *selection;
1572 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1574 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1575 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1577 valid = gtk_tree_model_get_iter_first(store, &iter);
1581 gboolean mark_val, selected;
1582 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1584 mark_val = file_data_get_mark(fd, n);
1585 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1589 case MTS_MODE_SET: selected = mark_val;
1591 case MTS_MODE_OR: selected = mark_val || selected;
1593 case MTS_MODE_AND: selected = mark_val && selected;
1595 case MTS_MODE_MINUS: selected = !mark_val && selected;
1600 gtk_tree_selection_select_iter(selection, &iter);
1602 gtk_tree_selection_unselect_iter(selection, &iter);
1604 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1608 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1610 GtkTreeModel *store;
1611 GtkTreeSelection *selection;
1616 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1618 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1619 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1623 GtkTreePath *tpath = work->data;
1627 gtk_tree_model_get_iter(store, &iter, tpath);
1628 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1630 /* the change has a very limited range and the standard notification would trigger
1631 complete re-read of the directory - try to do only minimal update instead */
1632 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1636 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1638 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1640 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1644 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1646 vf_refresh_idle(vf);
1650 /* mark functions can have various side effects - update all columns to be sure */
1651 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1652 /* mark functions can change sidecars too */
1653 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1657 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1661 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1666 *-----------------------------------------------------------------------------
1668 *-----------------------------------------------------------------------------
1671 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
1673 GtkTreeViewColumn *column;
1674 GtkCellRenderer *cell;
1677 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1678 if (!column) return;
1680 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1682 list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1687 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1688 gtk_tree_view_column_set_visible(column, thumb);
1690 if (options->show_star_rating)
1692 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED_WITH_STARS);
1693 if (!column) return;
1694 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1695 gtk_tree_view_column_set_visible(column, TRUE);
1697 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1698 if (!column) return;
1699 gtk_tree_view_column_set_visible(column, FALSE);
1703 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1704 if (!column) return;
1705 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1706 gtk_tree_view_column_set_visible(column, TRUE);
1708 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED_WITH_STARS);
1709 if (!column) return;
1710 gtk_tree_view_column_set_visible(column, FALSE);
1713 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_STAR_RATING);
1714 if (!column) return;
1715 gtk_tree_view_column_set_visible(column, !multiline && options->show_star_rating);
1717 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1718 if (!column) return;
1719 gtk_tree_view_column_set_visible(column, !multiline);
1721 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1722 if (!column) return;
1723 gtk_tree_view_column_set_visible(column, !multiline);
1726 static gboolean vflist_is_multiline(ViewFile *vf)
1728 return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
1732 static void vflist_populate_view(ViewFile *vf, gboolean force)
1734 GtkTreeStore *store;
1737 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1743 vflist_store_clear(vf, FALSE);
1748 vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
1750 selected = vflist_selection_get_list(vf);
1752 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force);
1754 if (selected && vflist_selection_count(vf, NULL) == 0)
1756 /* all selected files disappeared */
1757 vflist_select_closest(vf, selected->data);
1760 filelist_free(selected);
1763 vf_thumb_update(vf);
1766 gboolean vflist_refresh(ViewFile *vf)
1769 gboolean ret = TRUE;
1771 old_list = vf->list;
1774 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1777 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1779 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1781 if (vf->marks_enabled)
1783 // When marks are enabled, lock FileDatas so that we don't end up re-parsing XML
1784 // each time a mark is changed.
1785 file_data_lock_list(vf->list);
1789 // FIXME: only do this when needed (aka when we just switched from
1790 // FIXME: marks-enabled to marks-disabled)
1791 file_data_unlock_list(vf->list);
1794 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1795 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1797 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1798 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1801 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1803 vflist_populate_view(vf, FALSE);
1805 DEBUG_1("%s vflist_refresh: free filelist", get_exec_time());
1807 filelist_free(old_list);
1808 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1815 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1817 #define CELL_HEIGHT_OVERRIDE 512
1819 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1823 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1824 if (spec && G_IS_PARAM_SPEC_INT(spec))
1826 GParamSpecInt *spec_int;
1828 spec_int = G_PARAM_SPEC_INT(spec);
1829 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1833 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1835 static GdkColor color;
1836 static GtkWidget *done = NULL;
1842 style = gtk_widget_get_style(widget);
1843 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1844 shift_color(&color, -1, 0);
1851 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1852 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1854 ViewFile *vf = data;
1857 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1858 g_object_set(G_OBJECT(cell),
1859 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1860 "cell-background-set", set, NULL);
1863 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1865 GtkTreeViewColumn *column;
1866 GtkCellRenderer *renderer;
1868 column = gtk_tree_view_column_new();
1869 gtk_tree_view_column_set_title(column, title);
1870 gtk_tree_view_column_set_min_width(column, 4);
1874 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1875 renderer = gtk_cell_renderer_text_new();
1878 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1880 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1881 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1883 gtk_tree_view_column_set_expand(column, TRUE);
1887 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1888 renderer = gtk_cell_renderer_pixbuf_new();
1889 cell_renderer_height_override(renderer);
1890 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1891 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1894 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1895 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1896 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1898 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1901 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1903 ViewFile *vf = data;
1904 GtkTreeStore *store;
1905 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1911 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1912 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1915 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1917 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1919 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1922 /* the change has a very limited range and the standard notification would trigger
1923 complete re-read of the directory - try to do only minimal update instead */
1924 file_data_unregister_notify_func(vf_notify_cb, vf);
1925 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1926 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1928 vf_refresh_idle(vf);
1932 /* mark functions can have various side effects - update all columns to be sure */
1933 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1934 /* mark functions can change sidecars too */
1935 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1937 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1939 gtk_tree_path_free(path);
1942 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1944 GtkTreeViewColumn *column;
1945 GtkCellRenderer *renderer;
1947 renderer = gtk_cell_renderer_toggle_new();
1948 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1950 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1951 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1952 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1954 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1955 gtk_tree_view_column_set_fixed_width(column, 22);
1956 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1959 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1963 *-----------------------------------------------------------------------------
1965 *-----------------------------------------------------------------------------
1968 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1971 if (!dir_fd) return FALSE;
1972 if (vf->dir_fd == dir_fd) return TRUE;
1974 file_data_unref(vf->dir_fd);
1975 vf->dir_fd = file_data_ref(dir_fd);
1977 /* force complete reload */
1978 vflist_store_clear(vf, TRUE);
1980 filelist_free(vf->list);
1983 ret = vf_refresh(vf);
1984 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
1988 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1990 ViewFile *vf = data;
1992 file_data_unregister_notify_func(vf_notify_cb, vf);
1994 vflist_select_idle_cancel(vf);
1995 vf_refresh_idle_cancel(vf);
1998 filelist_free(vf->list);
2001 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
2003 GtkTreeStore *store;
2004 GtkTreeSelection *selection;
2005 GType flist_types[FILE_COLUMN_COUNT];
2009 vf->info = g_new0(ViewFileInfoList, 1);
2011 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
2012 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
2013 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
2014 flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING;
2015 flist_types[FILE_COLUMN_FORMATTED_WITH_STARS] = G_TYPE_STRING;
2016 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
2017 flist_types[FILE_COLUMN_STAR_RATING] = G_TYPE_STRING;
2018 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
2019 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
2020 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
2021 flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN;
2022 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
2023 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
2024 flist_types[i] = G_TYPE_BOOLEAN;
2026 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
2028 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2029 g_object_unref(store);
2031 g_signal_connect(G_OBJECT(vf->listview), "row-expanded",
2032 G_CALLBACK(vflist_expand_cb), vf);
2034 g_signal_connect(G_OBJECT(vf->listview), "row-collapsed",
2035 G_CALLBACK(vflist_collapse_cb), vf);
2037 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2038 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
2039 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
2041 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2042 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2046 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2048 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2049 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
2053 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2054 g_assert(column == FILE_VIEW_COLUMN_THUMB);
2057 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE);
2058 g_assert(column == FILE_VIEW_COLUMN_FORMATTED);
2061 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED_WITH_STARS, _("NameStars"), FALSE, FALSE, TRUE);
2062 g_assert(column == FILE_VIEW_COLUMN_FORMATTED_WITH_STARS);
2065 vflist_listview_add_column(vf, FILE_COLUMN_STAR_RATING, _("Stars"), FALSE, FALSE, FALSE);
2066 g_assert(column == FILE_VIEW_COLUMN_STAR_RATING);
2069 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2070 g_assert(column == FILE_VIEW_COLUMN_SIZE);
2073 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2074 g_assert(column == FILE_VIEW_COLUMN_DATE);
2077 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2081 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2083 if (VFLIST(vf)->thumbs_enabled == enable) return;
2085 VFLIST(vf)->thumbs_enabled = enable;
2087 /* vflist_populate_view is better than vf_refresh:
2088 - no need to re-read the directory
2089 - force update because the formatted string has changed
2093 vflist_populate_view(vf, TRUE);
2094 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
2098 void vflist_marks_set(ViewFile *vf, gboolean enable)
2100 GList *columns, *work;
2102 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2107 GtkTreeViewColumn *column = work->data;
2108 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2111 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2112 gtk_tree_view_column_set_visible(column, enable);
2117 // Previously disabled, which means that vf->list is complete
2118 file_data_lock_list(vf->list);
2122 // Previously enabled, which means that vf->list is incomplete
2125 g_list_free(columns);
2128 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */