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"
45 #include <gdk/gdkkeysyms.h> /* for keyboard values */
47 /* Index to tree store */
49 FILE_COLUMN_POINTER = 0,
52 FILE_COLUMN_FORMATTED,
53 FILE_COLUMN_FORMATTED_WITH_STARS,
56 FILE_COLUMN_STAR_RATING,
62 FILE_COLUMN_MARKS_LAST = FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
67 /* Index to tree view */
69 FILE_VIEW_COLUMN_MARKS = 0,
70 FILE_VIEW_COLUMN_MARKS_LAST = FILE_VIEW_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
71 FILE_VIEW_COLUMN_THUMB,
72 FILE_VIEW_COLUMN_FORMATTED,
73 FILE_VIEW_COLUMN_FORMATTED_WITH_STARS,
74 FILE_VIEW_COLUMN_STAR_RATING,
75 FILE_VIEW_COLUMN_SIZE,
76 FILE_VIEW_COLUMN_DATE,
77 FILE_VIEW_COLUMN_COUNT
82 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd);
83 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data);
84 static void vflist_populate_view(ViewFile *vf, gboolean force);
85 static gboolean vflist_is_multiline(ViewFile *vf);
86 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded);
90 *-----------------------------------------------------------------------------
92 *-----------------------------------------------------------------------------
99 } ViewFileFindRowData;
101 static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
103 ViewFileFindRowData *find = data;
105 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
116 static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter)
119 ViewFileFindRowData data = {fd, iter, FALSE, 0};
121 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
122 gtk_tree_model_foreach(store, vflist_find_row_cb, &data);
132 static FileData *vflist_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter)
135 GtkTreeViewColumn *column;
137 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), x, y,
138 &tpath, &column, NULL, NULL))
144 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
145 gtk_tree_model_get_iter(store, &row, tpath);
146 gtk_tree_path_free(tpath);
147 gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &fd, -1);
155 static gboolean vflist_store_clear_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
158 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
160 /* it seems that gtk_tree_store_clear may call some callbacks
161 that use the column. Set the pointer to NULL to be safe. */
162 gtk_tree_store_set(GTK_TREE_STORE(model), iter, FILE_COLUMN_POINTER, NULL, -1);
167 static void vflist_store_clear(ViewFile *vf, gboolean unlock_files)
172 if (unlock_files && vf->marks_enabled)
174 // unlock locked files in this directory
175 filelist_read(vf->dir_fd, &files, NULL);
178 FileData *fd = files->data;
180 file_data_unlock(fd);
181 file_data_unref(fd); // undo the ref that got added in filelist_read
186 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
187 gtk_tree_model_foreach(store, vflist_store_clear_cb, NULL);
188 gtk_tree_store_clear(GTK_TREE_STORE(store));
191 void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set)
196 if (vflist_find_row(vf, fd, &iter) < 0) return;
197 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
198 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
201 static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
206 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
208 tpath = gtk_tree_model_get_path(store, iter);
209 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
210 gtk_tree_path_free(tpath);
215 *-----------------------------------------------------------------------------
217 *-----------------------------------------------------------------------------
220 static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
221 GtkSelectionData *selection_data, guint info,
222 guint time, gpointer data)
227 if (!VFLIST(vf)->click_fd) return;
229 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
231 list = vf_selection_get_list(vf);
235 list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
239 uri_selection_data_set_uris_from_filelist(selection_data, list);
243 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
247 vflist_color_set(vf, VFLIST(vf)->click_fd, TRUE);
249 if (VFLIST(vf)->thumbs_enabled &&
250 VFLIST(vf)->click_fd && VFLIST(vf)->click_fd->thumb_pixbuf)
254 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
255 items = vf_selection_count(vf, NULL);
259 dnd_set_drag_icon(widget, context, VFLIST(vf)->click_fd->thumb_pixbuf, items);
263 static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
267 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
269 if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE)
275 static void vflist_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context,
276 int x, int y, GtkSelectionData *selection,
277 guint info, guint time, gpointer data)
281 if (info == TARGET_TEXT_PLAIN) {
282 FileData *fd = vflist_find_data_by_coord(vf, x, y, NULL);
285 /* Add keywords to file */
286 gchar *str = (gchar *) gtk_selection_data_get_text(selection);
287 GList *kw_list = string_to_keywords_list(str);
289 metadata_append_list(fd, KEYWORD_KEY, kw_list);
290 string_list_free(kw_list);
296 void vflist_dnd_init(ViewFile *vf)
298 gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
299 dnd_file_drag_types, dnd_file_drag_types_count,
300 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
301 gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL,
302 dnd_file_drag_types, dnd_file_drag_types_count,
303 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
305 g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
306 G_CALLBACK(vflist_dnd_get), vf);
307 g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
308 G_CALLBACK(vflist_dnd_begin), vf);
309 g_signal_connect(G_OBJECT(vf->listview), "drag_end",
310 G_CALLBACK(vflist_dnd_end), vf);
311 g_signal_connect(G_OBJECT(vf->listview), "drag_data_received",
312 G_CALLBACK(vflist_drag_data_received), vf);
316 *-----------------------------------------------------------------------------
318 *-----------------------------------------------------------------------------
321 GList *vflist_selection_get_one(ViewFile *vf, FileData *fd)
323 GList *list = g_list_append(NULL, file_data_ref(fd));
325 if (fd->sidecar_files)
327 /* check if the row is expanded */
331 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
332 if (vflist_find_row(vf, fd, &iter) >= 0)
336 tpath = gtk_tree_model_get_path(store, &iter);
337 if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
339 /* unexpanded - add whole group */
340 GList *work = fd->sidecar_files;
343 FileData *sfd = work->data;
344 list = g_list_prepend(list, file_data_ref(sfd));
348 gtk_tree_path_free(tpath);
350 list = g_list_reverse(list);
356 GList *vflist_pop_menu_file_list(ViewFile *vf)
358 if (!VFLIST(vf)->click_fd) return NULL;
360 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
362 return vf_selection_get_list(vf);
364 return vflist_selection_get_one(vf, VFLIST(vf)->click_fd);
368 void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
372 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
376 list = vf_selection_get_list(vf);
377 view_window_new_from_list(list);
382 view_window_new(VFLIST(vf)->click_fd);
386 void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
391 list = vf_pop_menu_file_list(vf);
392 if (options->file_ops.enable_in_place_rename &&
393 list && !list->next && VFLIST(vf)->click_fd)
400 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
401 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
405 tpath = gtk_tree_model_get_path(store, &iter);
406 tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
407 FILE_VIEW_COLUMN_FORMATTED, VFLIST(vf)->click_fd->name,
408 vflist_row_rename_cb, vf);
409 gtk_tree_path_free(tpath);
414 file_util_rename(NULL, list, vf->listview);
417 void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
421 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
424 layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
428 vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled);
432 void vflist_star_rating_set(ViewFile *vf, gboolean enable)
434 GList *columns, *work;
436 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
441 GtkTreeViewColumn *column = work->data;
442 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
445 if (vflist_is_multiline(vf))
447 if (col_idx == FILE_COLUMN_FORMATTED_WITH_STARS)
449 gtk_tree_view_column_set_visible(column, enable);
451 if (col_idx == FILE_COLUMN_FORMATTED)
453 gtk_tree_view_column_set_visible(column, !enable);
458 if (col_idx == FILE_COLUMN_STAR_RATING)
460 gtk_tree_view_column_set_visible(column, enable);
464 g_list_free(columns);
467 void vflist_pop_menu_show_star_rating_cb(GtkWidget *widget, gpointer data)
471 options->show_star_rating = !options->show_star_rating;
473 vflist_populate_view(vf, TRUE);
475 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
476 vflist_star_rating_set(vf, options->show_star_rating);
479 void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
483 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
485 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
488 void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
491 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
492 VFLIST(vf)->click_fd = NULL;
498 *-----------------------------------------------------------------------------
500 *-----------------------------------------------------------------------------
503 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
508 if (!new || !new[0]) return FALSE;
510 new_path = g_build_filename(vf->dir_fd->path, new, NULL);
512 if (strchr(new, G_DIR_SEPARATOR) != NULL)
514 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
515 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
520 gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL);
521 FileData *fd = file_data_new_group(old_path); /* get the fd from cache */
522 file_util_rename_simple(fd, new_path, vf->listview);
532 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
540 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return;
541 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
542 tpath = gtk_tree_model_get_path(store, &iter);
543 tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
544 gtk_tree_path_free(tpath);
546 popup_menu_position_clamp(menu, x, y, 0);
549 gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
554 if (event->keyval != GDK_KEY_Menu) return FALSE;
556 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
562 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
563 gtk_tree_model_get_iter(store, &iter, tpath);
564 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
565 gtk_tree_path_free(tpath);
569 VFLIST(vf)->click_fd = NULL;
572 vf->popup = vf_pop_menu(vf);
573 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
578 gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
584 GtkTreeViewColumn *column;
586 vf->clicked_mark = 0;
588 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
589 &tpath, &column, NULL, NULL))
592 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
594 if (bevent->button == MOUSE_BUTTON_LEFT &&
595 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
598 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
599 vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
601 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
603 gtk_tree_model_get_iter(store, &iter, tpath);
604 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
605 gtk_tree_path_free(tpath);
608 VFLIST(vf)->click_fd = fd;
610 if (bevent->button == MOUSE_BUTTON_RIGHT)
612 vf->popup = vf_pop_menu(vf);
613 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
614 bevent->button, bevent->time);
618 if (!fd) return FALSE;
620 if (bevent->button == MOUSE_BUTTON_MIDDLE)
622 if (!vflist_row_is_selected(vf, fd))
624 vflist_color_set(vf, fd, TRUE);
630 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
631 !(bevent->state & GDK_SHIFT_MASK ) &&
632 !(bevent->state & GDK_CONTROL_MASK ) &&
633 vflist_row_is_selected(vf, fd))
635 GtkTreeSelection *selection;
637 gtk_widget_grab_focus(widget);
640 /* returning FALSE and further processing of the event is needed for
641 correct operation of the expander, to show the sidecar files.
642 It however resets the selection of multiple files. With this condition
643 it should work for both cases */
644 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
645 return (gtk_tree_selection_count_selected_rows(selection) > 1);
648 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
650 if (VFLIST(vf)->click_fd->format_class == FORMAT_CLASS_COLLECTION)
652 collection_window_new(VFLIST(vf)->click_fd->path);
656 if (vf->layout) layout_image_full_screen_start(vf->layout);
663 gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
670 if (defined_mouse_buttons(widget, bevent, vf->layout))
675 if (bevent->button == MOUSE_BUTTON_MIDDLE)
677 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
680 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
685 if ((bevent->x != 0 || bevent->y != 0) &&
686 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
687 &tpath, NULL, NULL, NULL))
691 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
692 gtk_tree_model_get_iter(store, &iter, tpath);
693 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
694 gtk_tree_path_free(tpath);
697 if (bevent->button == MOUSE_BUTTON_MIDDLE)
699 if (fd && VFLIST(vf)->click_fd == fd)
701 GtkTreeSelection *selection;
703 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
704 if (vflist_row_is_selected(vf, fd))
706 gtk_tree_selection_unselect_iter(selection, &iter);
710 gtk_tree_selection_select_iter(selection, &iter);
716 if (fd && VFLIST(vf)->click_fd == fd &&
717 !(bevent->state & GDK_SHIFT_MASK ) &&
718 !(bevent->state & GDK_CONTROL_MASK ) &&
719 vflist_row_is_selected(vf, fd))
721 GtkTreeSelection *selection;
723 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
724 gtk_tree_selection_unselect_all(selection);
725 gtk_tree_selection_select_iter(selection, &iter);
726 vflist_move_cursor(vf, &iter);
732 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
734 FileData *read_ahead_fd = NULL;
740 cur_fd = layout_image_get_fd(vf->layout);
741 if (sel_fd == cur_fd) return; /* no change */
743 row = g_list_index(vf->list, sel_fd);
744 // FIXME sidecar data
746 if (sel_fd && options->image.enable_read_ahead && row >= 0)
748 if (row > g_list_index(vf->list, cur_fd) &&
749 (guint) (row + 1) < vf_count(vf, NULL))
751 read_ahead_fd = vf_index_get_data(vf, row + 1);
755 read_ahead_fd = vf_index_get_data(vf, row - 1);
759 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
762 static gboolean vflist_select_idle_cb(gpointer data)
768 VFLIST(vf)->select_idle_id = 0;
774 if (VFLIST(vf)->select_fd)
776 vflist_select_image(vf, VFLIST(vf)->select_fd);
777 VFLIST(vf)->select_fd = NULL;
780 VFLIST(vf)->select_idle_id = 0;
784 static void vflist_select_idle_cancel(ViewFile *vf)
786 if (VFLIST(vf)->select_idle_id)
788 g_source_remove(VFLIST(vf)->select_idle_id);
789 VFLIST(vf)->select_idle_id = 0;
793 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
794 gboolean path_currently_selected, gpointer data)
799 if (!path_currently_selected &&
800 gtk_tree_model_get_iter(store, &iter, tpath))
802 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
806 VFLIST(vf)->select_fd = NULL;
810 !VFLIST(vf)->select_idle_id)
812 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
818 static void vflist_expand_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
821 vflist_set_expanded(vf, iter, TRUE);
824 static void vflist_collapse_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
827 vflist_set_expanded(vf, iter, FALSE);
831 *-----------------------------------------------------------------------------
833 *-----------------------------------------------------------------------------
837 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)
839 gboolean multiline = vflist_is_multiline(vf);
846 text = g_strdup_printf("%s %s\n%s\n%s\n%s", name, expanded ? "" : sidecars, size, time, star_rating);
850 text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time);
855 text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars);
860 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded)
868 gchar *formatted_with_stars;
870 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
872 gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
873 FILE_COLUMN_NAME, &name,
874 FILE_COLUMN_SIDECARS, &sidecars,
875 FILE_COLUMN_SIZE, &size,
876 FILE_COLUMN_DATE, &time,
877 FILE_COLUMN_STAR_RATING, &star_rating,
880 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded, FALSE, NULL);
881 formatted_with_stars = vflist_get_formatted(vf, name, sidecars, size, time, expanded, TRUE, star_rating);
883 gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED, formatted,
884 FILE_COLUMN_EXPANDED, expanded,
886 gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED_WITH_STARS, formatted_with_stars,
887 FILE_COLUMN_EXPANDED, expanded,
896 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
899 gchar *sidecars = NULL;
901 const gchar *time = text_from_time(fd->date);
902 gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
903 const gchar *disabled_grouping;
905 gchar *formatted_with_stars;
906 gboolean expanded = FALSE;
909 if (options->show_star_rating)
911 star_rating = metadata_read_rating_stars(fd);
918 if (fd->sidecar_files) /* expanded has no effect on files without sidecars */
920 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_EXPANDED, &expanded, -1);
923 sidecars = file_data_sc_list_to_string(fd);
925 disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
926 name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
927 size = text_from_size(fd->size);
929 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded, FALSE, NULL);
930 formatted_with_stars = vflist_get_formatted(vf, name, sidecars, size, time, expanded, TRUE, star_rating);
932 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
933 FILE_COLUMN_VERSION, fd->version,
934 FILE_COLUMN_THUMB, fd->thumb_pixbuf,
935 FILE_COLUMN_FORMATTED, formatted,
936 FILE_COLUMN_FORMATTED_WITH_STARS, formatted_with_stars,
937 FILE_COLUMN_SIDECARS, sidecars,
938 FILE_COLUMN_NAME, name,
939 FILE_COLUMN_STAR_RATING, star_rating,
940 FILE_COLUMN_SIZE, size,
941 FILE_COLUMN_DATE, time,
942 #define STORE_SET_IS_SLOW 1
943 #if STORE_SET_IS_SLOW
944 /* this is 3x faster on a directory with 20000 files */
945 FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
946 FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
947 FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
948 FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
949 FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
950 FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
951 FILE_COLUMN_MARKS + 6, file_data_get_mark(fd, 6),
952 FILE_COLUMN_MARKS + 7, file_data_get_mark(fd, 7),
953 FILE_COLUMN_MARKS + 8, file_data_get_mark(fd, 8),
954 FILE_COLUMN_MARKS + 9, file_data_get_mark(fd, 9),
955 #if FILEDATA_MARKS_SIZE != 10
956 #error this needs to be updated
959 FILE_COLUMN_COLOR, FALSE, -1);
961 #if !STORE_SET_IS_SLOW
964 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
965 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
974 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected, gboolean force)
979 gint num_ordered = 0;
980 gint num_prepended = 0;
982 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
988 FileData *fd = work->data;
989 gboolean done = FALSE;
993 FileData *old_fd = NULL;
994 gint old_version = 0;
998 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
999 FILE_COLUMN_POINTER, &old_fd,
1000 FILE_COLUMN_VERSION, &old_version,
1010 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
1012 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
1014 if (match == 0) g_warning("multiple fd for the same path");
1030 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
1035 here should be used gtk_tree_store_append, but this function seems to be O(n)
1036 and it seems to be much faster to add new entries to the beginning and reorder later
1039 gtk_tree_store_prepend(store, &new, parent_iter);
1042 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
1043 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected, force);
1045 if (g_list_find(selected, fd))
1047 /* renamed files - the same fd appears at different position - select it again*/
1048 GtkTreeSelection *selection;
1049 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1050 gtk_tree_selection_select_iter(selection, &new);
1057 file_data_unref(old_fd);
1058 valid = gtk_tree_store_remove(store, &iter);
1063 if (fd->version != old_version || force)
1065 vflist_setup_iter(vf, store, &iter, fd);
1066 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected, force);
1069 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1080 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1081 file_data_unref(old_fd);
1083 valid = gtk_tree_store_remove(store, &iter);
1086 /* move the prepended entries to the correct position */
1090 gint num_total = num_prepended + num_ordered;
1091 gint *new_order = g_malloc(num_total * sizeof(gint));
1093 for (i = 0; i < num_total; i++)
1095 if (i < num_ordered)
1096 new_order[i] = num_prepended + i;
1098 new_order[i] = num_total - 1 - i;
1100 gtk_tree_store_reorder(store, parent_iter, new_order);
1106 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
1109 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
1111 GtkTreeStore *store;
1114 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1115 if (!vf->list) return;
1121 FileData *fd = work->data;
1122 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1127 vf->sort_method = type;
1128 vf->sort_ascend = ascend;
1130 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1132 new_order = g_malloc(i * sizeof(gint));
1138 FileData *fd = work->data;
1139 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1144 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1145 gtk_tree_store_reorder(store, NULL, new_order);
1148 g_hash_table_destroy(fd_idx_hash);
1152 *-----------------------------------------------------------------------------
1154 *-----------------------------------------------------------------------------
1158 void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1163 FileData *fd = work->data;
1166 if (fd->thumb_pixbuf) (*done)++;
1168 if (fd->sidecar_files)
1170 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1176 void vflist_read_metadata_progress_count(GList *list, gint *count, gint *done)
1181 FileData *fd = work->data;
1184 if (fd->metadata_in_idle_loaded) (*done)++;
1186 if (fd->sidecar_files)
1188 vflist_read_metadata_progress_count(fd->sidecar_files, count, done);
1194 void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
1196 GtkTreeStore *store;
1199 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1201 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1202 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1205 FileData *vflist_thumb_next_fd(ViewFile *vf)
1208 FileData *fd = NULL;
1210 /* first check the visible files */
1212 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1214 GtkTreeModel *store;
1216 gboolean valid = TRUE;
1218 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1219 gtk_tree_model_get_iter(store, &iter, tpath);
1220 gtk_tree_path_free(tpath);
1223 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1227 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1229 if (!nfd->thumb_pixbuf) fd = nfd;
1231 valid = gtk_tree_model_iter_next(store, &iter);
1235 /* then find first undone */
1239 GList *work = vf->list;
1242 FileData *fd_p = work->data;
1243 if (!fd_p->thumb_pixbuf)
1247 GList *work2 = fd_p->sidecar_files;
1249 while (work2 && !fd)
1252 if (!fd_p->thumb_pixbuf) fd = fd_p;
1253 work2 = work2->next;
1264 *-----------------------------------------------------------------------------
1266 *-----------------------------------------------------------------------------
1269 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1272 GList *work, *work2;
1277 FileData *list_fd = work->data;
1278 if (list_fd == fd) return p;
1280 work2 = list_fd->sidecar_files;
1283 /* FIXME: return the same index also for sidecars
1284 it is sufficient for next/prev navigation but it should be rewritten
1285 without using indexes at all
1287 FileData *sidecar_fd = work2->data;
1288 if (sidecar_fd == fd) return p;
1289 work2 = work2->next;
1300 *-----------------------------------------------------------------------------
1302 *-----------------------------------------------------------------------------
1305 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1307 GtkTreeModel *store;
1308 GtkTreeSelection *selection;
1311 gboolean found = FALSE;
1313 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1314 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1316 while (!found && work)
1318 GtkTreePath *tpath = work->data;
1322 gtk_tree_model_get_iter(store, &iter, tpath);
1323 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1324 if (fd_n == fd) found = TRUE;
1327 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1333 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1337 fd = vf_index_get_data(vf, row);
1338 return vflist_row_is_selected(vf, fd);
1341 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1343 GtkTreeModel *store;
1344 GtkTreeSelection *selection;
1348 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1349 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1359 GtkTreePath *tpath = work->data;
1363 gtk_tree_model_get_iter(store, &iter, tpath);
1364 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1373 count = g_list_length(slist);
1374 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1380 GList *vflist_selection_get_list(ViewFile *vf)
1382 GtkTreeModel *store;
1383 GtkTreeSelection *selection;
1388 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1389 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1393 GtkTreePath *tpath = work->data;
1397 gtk_tree_model_get_iter(store, &iter, tpath);
1398 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1400 list = g_list_prepend(list, file_data_ref(fd));
1402 if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
1404 /* unexpanded - add whole group */
1405 GList *work2 = fd->sidecar_files;
1408 FileData *sfd = work2->data;
1409 list = g_list_prepend(list, file_data_ref(sfd));
1410 work2 = work2->next;
1416 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1419 return g_list_reverse(list);
1422 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1424 GtkTreeModel *store;
1425 GtkTreeSelection *selection;
1430 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1431 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1435 GtkTreePath *tpath = work->data;
1439 gtk_tree_model_get_iter(store, &iter, tpath);
1440 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1442 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1446 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1449 return g_list_reverse(list);
1452 void vflist_select_all(ViewFile *vf)
1454 GtkTreeSelection *selection;
1456 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1457 gtk_tree_selection_select_all(selection);
1459 VFLIST(vf)->select_fd = NULL;
1462 void vflist_select_none(ViewFile *vf)
1464 GtkTreeSelection *selection;
1466 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1467 gtk_tree_selection_unselect_all(selection);
1470 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1475 tpath = gtk_tree_model_get_path(store, iter);
1476 result = gtk_tree_path_prev(tpath);
1478 gtk_tree_model_get_iter(store, iter, tpath);
1480 gtk_tree_path_free(tpath);
1485 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1487 if (!gtk_tree_model_get_iter_first(store, iter))
1492 GtkTreeIter next = *iter;
1494 if (gtk_tree_model_iter_next(store, &next))
1503 void vflist_select_invert(ViewFile *vf)
1506 GtkTreeSelection *selection;
1507 GtkTreeModel *store;
1510 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1511 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1513 /* Backward iteration prevents scrolling to the end of the list,
1514 * it scrolls to the first selected row instead. */
1515 valid = tree_model_get_iter_last(store, &iter);
1519 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1522 gtk_tree_selection_unselect_iter(selection, &iter);
1524 gtk_tree_selection_select_iter(selection, &iter);
1526 valid = tree_model_iter_prev(store, &iter);
1530 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1534 if (vflist_find_row(vf, fd, &iter) < 0) return;
1536 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1538 if (!vflist_row_is_selected(vf, fd))
1540 GtkTreeSelection *selection;
1541 GtkTreeModel *store;
1544 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1545 gtk_tree_selection_unselect_all(selection);
1546 gtk_tree_selection_select_iter(selection, &iter);
1547 vflist_move_cursor(vf, &iter);
1549 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1550 tpath = gtk_tree_model_get_path(store, &iter);
1551 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1552 gtk_tree_path_free(tpath);
1556 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1559 FileData *fd = NULL;
1561 if (sel_fd->parent) sel_fd = sel_fd->parent;
1570 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1572 if (match >= 0) break;
1575 if (fd) vflist_select_by_fd(vf, fd);
1579 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1581 GtkTreeModel *store;
1583 GtkTreeSelection *selection;
1587 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1589 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1590 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1592 valid = gtk_tree_model_get_iter_first(store, &iter);
1596 gboolean mark_val, selected;
1597 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1599 mark_val = file_data_get_mark(fd, n);
1600 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1604 case MTS_MODE_SET: selected = mark_val;
1606 case MTS_MODE_OR: selected = mark_val || selected;
1608 case MTS_MODE_AND: selected = mark_val && selected;
1610 case MTS_MODE_MINUS: selected = !mark_val && selected;
1615 gtk_tree_selection_select_iter(selection, &iter);
1617 gtk_tree_selection_unselect_iter(selection, &iter);
1619 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1623 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1625 GtkTreeModel *store;
1626 GtkTreeSelection *selection;
1631 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1633 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1634 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1638 GtkTreePath *tpath = work->data;
1642 gtk_tree_model_get_iter(store, &iter, tpath);
1643 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1645 /* the change has a very limited range and the standard notification would trigger
1646 complete re-read of the directory - try to do only minimal update instead */
1647 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1651 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1653 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1655 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1659 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1661 vf_refresh_idle(vf);
1665 /* mark functions can have various side effects - update all columns to be sure */
1666 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1667 /* mark functions can change sidecars too */
1668 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1672 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1676 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1681 *-----------------------------------------------------------------------------
1683 *-----------------------------------------------------------------------------
1686 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
1688 GtkTreeViewColumn *column;
1689 GtkCellRenderer *cell;
1692 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1693 if (!column) return;
1695 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1697 list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1702 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1703 gtk_tree_view_column_set_visible(column, thumb);
1705 if (options->show_star_rating)
1707 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED_WITH_STARS);
1708 if (!column) return;
1709 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1710 gtk_tree_view_column_set_visible(column, TRUE);
1712 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1713 if (!column) return;
1714 gtk_tree_view_column_set_visible(column, FALSE);
1718 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1719 if (!column) return;
1720 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1721 gtk_tree_view_column_set_visible(column, TRUE);
1723 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED_WITH_STARS);
1724 if (!column) return;
1725 gtk_tree_view_column_set_visible(column, FALSE);
1728 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_STAR_RATING);
1729 if (!column) return;
1730 gtk_tree_view_column_set_visible(column, !multiline && options->show_star_rating);
1732 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1733 if (!column) return;
1734 gtk_tree_view_column_set_visible(column, !multiline);
1736 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1737 if (!column) return;
1738 gtk_tree_view_column_set_visible(column, !multiline);
1741 static gboolean vflist_is_multiline(ViewFile *vf)
1743 return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
1747 static void vflist_populate_view(ViewFile *vf, gboolean force)
1749 GtkTreeStore *store;
1752 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1758 vflist_store_clear(vf, FALSE);
1763 vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
1765 selected = vflist_selection_get_list(vf);
1767 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force);
1769 if (selected && vflist_selection_count(vf, NULL) == 0)
1771 /* all selected files disappeared */
1772 vflist_select_closest(vf, selected->data);
1775 filelist_free(selected);
1778 vf_thumb_update(vf);
1781 gboolean vflist_refresh(ViewFile *vf)
1784 gboolean ret = TRUE;
1786 old_list = vf->list;
1789 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1792 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1794 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1796 if (vf->marks_enabled)
1798 // When marks are enabled, lock FileDatas so that we don't end up re-parsing XML
1799 // each time a mark is changed.
1800 file_data_lock_list(vf->list);
1804 // FIXME: only do this when needed (aka when we just switched from
1805 // FIXME: marks-enabled to marks-disabled)
1806 file_data_unlock_list(vf->list);
1809 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1810 vf->list = g_list_first(vf->list);
1811 vf->list = file_data_filter_file_filter_list(vf->list, vf_file_filter_get_filter(vf));
1813 vf->list = g_list_first(vf->list);
1814 vf->list = file_data_filter_class_list(vf->list, vf_class_get_filter(vf));
1816 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1818 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1819 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1822 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1824 vflist_populate_view(vf, FALSE);
1826 DEBUG_1("%s vflist_refresh: free filelist", get_exec_time());
1828 filelist_free(old_list);
1829 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1836 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1838 #define CELL_HEIGHT_OVERRIDE 512
1840 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1844 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1845 if (spec && G_IS_PARAM_SPEC_INT(spec))
1847 GParamSpecInt *spec_int;
1849 spec_int = G_PARAM_SPEC_INT(spec);
1850 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1854 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1856 static GdkColor color;
1857 static GtkWidget *done = NULL;
1863 style = gtk_widget_get_style(widget);
1864 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1865 shift_color(&color, -1, 0);
1872 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1873 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1875 ViewFile *vf = data;
1878 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1879 g_object_set(G_OBJECT(cell),
1880 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1881 "cell-background-set", set, NULL);
1884 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1886 GtkTreeViewColumn *column;
1887 GtkCellRenderer *renderer;
1889 column = gtk_tree_view_column_new();
1890 gtk_tree_view_column_set_title(column, title);
1891 gtk_tree_view_column_set_min_width(column, 4);
1895 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1896 renderer = gtk_cell_renderer_text_new();
1899 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1901 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1902 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1904 gtk_tree_view_column_set_expand(column, TRUE);
1908 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1909 renderer = gtk_cell_renderer_pixbuf_new();
1910 cell_renderer_height_override(renderer);
1911 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1912 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1915 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1916 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1917 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1919 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1922 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1924 ViewFile *vf = data;
1925 GtkTreeStore *store;
1926 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1932 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1933 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1936 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1938 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1940 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1943 /* the change has a very limited range and the standard notification would trigger
1944 complete re-read of the directory - try to do only minimal update instead */
1945 file_data_unregister_notify_func(vf_notify_cb, vf);
1946 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1947 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1949 vf_refresh_idle(vf);
1953 /* mark functions can have various side effects - update all columns to be sure */
1954 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1955 /* mark functions can change sidecars too */
1956 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1958 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1960 gtk_tree_path_free(path);
1963 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1965 GtkTreeViewColumn *column;
1966 GtkCellRenderer *renderer;
1968 renderer = gtk_cell_renderer_toggle_new();
1969 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1971 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1972 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1973 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1975 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1976 gtk_tree_view_column_set_fixed_width(column, 22);
1977 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1980 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1984 *-----------------------------------------------------------------------------
1986 *-----------------------------------------------------------------------------
1989 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1992 if (!dir_fd) return FALSE;
1993 if (vf->dir_fd == dir_fd) return TRUE;
1995 file_data_unref(vf->dir_fd);
1996 vf->dir_fd = file_data_ref(dir_fd);
1998 /* force complete reload */
1999 vflist_store_clear(vf, TRUE);
2001 filelist_free(vf->list);
2004 ret = vf_refresh(vf);
2005 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
2009 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
2011 ViewFile *vf = data;
2013 file_data_unregister_notify_func(vf_notify_cb, vf);
2015 vflist_select_idle_cancel(vf);
2016 vf_refresh_idle_cancel(vf);
2019 filelist_free(vf->list);
2022 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
2024 GtkTreeStore *store;
2025 GtkTreeSelection *selection;
2026 GType flist_types[FILE_COLUMN_COUNT];
2030 vf->info = g_new0(ViewFileInfoList, 1);
2032 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
2033 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
2034 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
2035 flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING;
2036 flist_types[FILE_COLUMN_FORMATTED_WITH_STARS] = G_TYPE_STRING;
2037 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
2038 flist_types[FILE_COLUMN_STAR_RATING] = G_TYPE_STRING;
2039 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
2040 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
2041 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
2042 flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN;
2043 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
2044 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
2045 flist_types[i] = G_TYPE_BOOLEAN;
2047 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
2049 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2050 g_object_unref(store);
2052 g_signal_connect(G_OBJECT(vf->listview), "row-expanded",
2053 G_CALLBACK(vflist_expand_cb), vf);
2055 g_signal_connect(G_OBJECT(vf->listview), "row-collapsed",
2056 G_CALLBACK(vflist_collapse_cb), vf);
2058 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2059 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
2060 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
2062 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2063 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2065 gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(vf->listview), FILE_COLUMN_FORMATTED);
2069 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2071 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2072 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
2076 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2077 g_assert(column == FILE_VIEW_COLUMN_THUMB);
2080 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE);
2081 g_assert(column == FILE_VIEW_COLUMN_FORMATTED);
2084 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED_WITH_STARS, _("NameStars"), FALSE, FALSE, TRUE);
2085 g_assert(column == FILE_VIEW_COLUMN_FORMATTED_WITH_STARS);
2088 vflist_listview_add_column(vf, FILE_COLUMN_STAR_RATING, _("Stars"), FALSE, FALSE, FALSE);
2089 g_assert(column == FILE_VIEW_COLUMN_STAR_RATING);
2092 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2093 g_assert(column == FILE_VIEW_COLUMN_SIZE);
2096 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2097 g_assert(column == FILE_VIEW_COLUMN_DATE);
2100 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2104 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2106 if (VFLIST(vf)->thumbs_enabled == enable) return;
2108 VFLIST(vf)->thumbs_enabled = enable;
2110 /* vflist_populate_view is better than vf_refresh:
2111 - no need to re-read the directory
2112 - force update because the formatted string has changed
2116 vflist_populate_view(vf, TRUE);
2117 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
2121 void vflist_marks_set(ViewFile *vf, gboolean enable)
2123 GList *columns, *work;
2125 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2130 GtkTreeViewColumn *column = work->data;
2131 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2134 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2135 gtk_tree_view_column_set_visible(column, enable);
2140 // Previously disabled, which means that vf->list is complete
2141 file_data_lock_list(vf->list);
2145 // Previously enabled, which means that vf->list is incomplete
2148 g_list_free(columns);
2151 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */