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)
798 GtkTreePath *cursor_path;
800 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &cursor_path, NULL);
803 gtk_tree_model_get_iter(store, &iter, cursor_path);
804 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
805 gtk_tree_path_free(cursor_path);
809 VFLIST(vf)->select_fd = NULL;
813 !VFLIST(vf)->select_idle_id)
815 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
821 static void vflist_expand_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
824 vflist_set_expanded(vf, iter, TRUE);
827 static void vflist_collapse_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
830 vflist_set_expanded(vf, iter, FALSE);
834 *-----------------------------------------------------------------------------
836 *-----------------------------------------------------------------------------
840 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)
842 gboolean multiline = vflist_is_multiline(vf);
849 text = g_strdup_printf("%s %s\n%s\n%s\n%s", name, expanded ? "" : sidecars, size, time, star_rating);
853 text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time);
858 text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars);
863 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded)
871 gchar *formatted_with_stars;
873 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
875 gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
876 FILE_COLUMN_NAME, &name,
877 FILE_COLUMN_SIDECARS, &sidecars,
878 FILE_COLUMN_SIZE, &size,
879 FILE_COLUMN_DATE, &time,
880 FILE_COLUMN_STAR_RATING, &star_rating,
883 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded, FALSE, NULL);
884 formatted_with_stars = vflist_get_formatted(vf, name, sidecars, size, time, expanded, TRUE, star_rating);
886 gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED, formatted,
887 FILE_COLUMN_EXPANDED, expanded,
889 gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED_WITH_STARS, formatted_with_stars,
890 FILE_COLUMN_EXPANDED, expanded,
899 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
902 gchar *sidecars = NULL;
904 const gchar *time = text_from_time(fd->date);
905 gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
906 const gchar *disabled_grouping;
908 gchar *formatted_with_stars;
909 gboolean expanded = FALSE;
912 if (options->show_star_rating)
914 star_rating = metadata_read_rating_stars(fd);
921 if (fd->sidecar_files) /* expanded has no effect on files without sidecars */
923 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_EXPANDED, &expanded, -1);
926 sidecars = file_data_sc_list_to_string(fd);
928 disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
929 name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
930 size = text_from_size(fd->size);
932 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded, FALSE, NULL);
933 formatted_with_stars = vflist_get_formatted(vf, name, sidecars, size, time, expanded, TRUE, star_rating);
935 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
936 FILE_COLUMN_VERSION, fd->version,
937 FILE_COLUMN_THUMB, fd->thumb_pixbuf,
938 FILE_COLUMN_FORMATTED, formatted,
939 FILE_COLUMN_FORMATTED_WITH_STARS, formatted_with_stars,
940 FILE_COLUMN_SIDECARS, sidecars,
941 FILE_COLUMN_NAME, name,
942 FILE_COLUMN_STAR_RATING, star_rating,
943 FILE_COLUMN_SIZE, size,
944 FILE_COLUMN_DATE, time,
945 #define STORE_SET_IS_SLOW 1
946 #if STORE_SET_IS_SLOW
947 /* this is 3x faster on a directory with 20000 files */
948 FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
949 FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
950 FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
951 FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
952 FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
953 FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
954 FILE_COLUMN_MARKS + 6, file_data_get_mark(fd, 6),
955 FILE_COLUMN_MARKS + 7, file_data_get_mark(fd, 7),
956 FILE_COLUMN_MARKS + 8, file_data_get_mark(fd, 8),
957 FILE_COLUMN_MARKS + 9, file_data_get_mark(fd, 9),
958 #if FILEDATA_MARKS_SIZE != 10
959 #error this needs to be updated
962 FILE_COLUMN_COLOR, FALSE, -1);
964 #if !STORE_SET_IS_SLOW
967 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
968 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
977 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected, gboolean force)
982 gint num_ordered = 0;
983 gint num_prepended = 0;
985 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
991 FileData *fd = work->data;
992 gboolean done = FALSE;
996 FileData *old_fd = NULL;
997 gint old_version = 0;
1001 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1002 FILE_COLUMN_POINTER, &old_fd,
1003 FILE_COLUMN_VERSION, &old_version,
1013 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
1015 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
1017 if (match == 0) g_warning("multiple fd for the same path");
1033 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
1038 here should be used gtk_tree_store_append, but this function seems to be O(n)
1039 and it seems to be much faster to add new entries to the beginning and reorder later
1042 gtk_tree_store_prepend(store, &new, parent_iter);
1045 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
1046 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected, force);
1048 if (g_list_find(selected, fd))
1050 /* renamed files - the same fd appears at different position - select it again*/
1051 GtkTreeSelection *selection;
1052 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1053 gtk_tree_selection_select_iter(selection, &new);
1060 file_data_unref(old_fd);
1061 valid = gtk_tree_store_remove(store, &iter);
1066 if (fd->version != old_version || force)
1068 vflist_setup_iter(vf, store, &iter, fd);
1069 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected, force);
1072 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1083 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1084 file_data_unref(old_fd);
1086 valid = gtk_tree_store_remove(store, &iter);
1089 /* move the prepended entries to the correct position */
1093 gint num_total = num_prepended + num_ordered;
1094 gint *new_order = g_malloc(num_total * sizeof(gint));
1096 for (i = 0; i < num_total; i++)
1098 if (i < num_ordered)
1099 new_order[i] = num_prepended + i;
1101 new_order[i] = num_total - 1 - i;
1103 gtk_tree_store_reorder(store, parent_iter, new_order);
1109 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
1112 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
1114 GtkTreeStore *store;
1117 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1118 if (!vf->list) return;
1124 FileData *fd = work->data;
1125 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1130 vf->sort_method = type;
1131 vf->sort_ascend = ascend;
1133 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1135 new_order = g_malloc(i * sizeof(gint));
1141 FileData *fd = work->data;
1142 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1147 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1148 gtk_tree_store_reorder(store, NULL, new_order);
1151 g_hash_table_destroy(fd_idx_hash);
1155 *-----------------------------------------------------------------------------
1157 *-----------------------------------------------------------------------------
1161 void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1166 FileData *fd = work->data;
1169 if (fd->thumb_pixbuf) (*done)++;
1171 if (fd->sidecar_files)
1173 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1179 void vflist_read_metadata_progress_count(GList *list, gint *count, gint *done)
1184 FileData *fd = work->data;
1187 if (fd->metadata_in_idle_loaded) (*done)++;
1189 if (fd->sidecar_files)
1191 vflist_read_metadata_progress_count(fd->sidecar_files, count, done);
1197 void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
1199 GtkTreeStore *store;
1202 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1204 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1205 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1208 FileData *vflist_thumb_next_fd(ViewFile *vf)
1211 FileData *fd = NULL;
1213 /* first check the visible files */
1215 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1217 GtkTreeModel *store;
1219 gboolean valid = TRUE;
1221 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1222 gtk_tree_model_get_iter(store, &iter, tpath);
1223 gtk_tree_path_free(tpath);
1226 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1230 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1232 if (!nfd->thumb_pixbuf) fd = nfd;
1234 valid = gtk_tree_model_iter_next(store, &iter);
1238 /* then find first undone */
1242 GList *work = vf->list;
1245 FileData *fd_p = work->data;
1246 if (!fd_p->thumb_pixbuf)
1250 GList *work2 = fd_p->sidecar_files;
1252 while (work2 && !fd)
1255 if (!fd_p->thumb_pixbuf) fd = fd_p;
1256 work2 = work2->next;
1267 *-----------------------------------------------------------------------------
1269 *-----------------------------------------------------------------------------
1272 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1275 GList *work, *work2;
1280 FileData *list_fd = work->data;
1281 if (list_fd == fd) return p;
1283 work2 = list_fd->sidecar_files;
1286 /* FIXME: return the same index also for sidecars
1287 it is sufficient for next/prev navigation but it should be rewritten
1288 without using indexes at all
1290 FileData *sidecar_fd = work2->data;
1291 if (sidecar_fd == fd) return p;
1292 work2 = work2->next;
1303 *-----------------------------------------------------------------------------
1305 *-----------------------------------------------------------------------------
1308 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1310 GtkTreeModel *store;
1311 GtkTreeSelection *selection;
1314 gboolean found = FALSE;
1316 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1317 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1319 while (!found && work)
1321 GtkTreePath *tpath = work->data;
1325 gtk_tree_model_get_iter(store, &iter, tpath);
1326 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1327 if (fd_n == fd) found = TRUE;
1330 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1336 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1340 fd = vf_index_get_data(vf, row);
1341 return vflist_row_is_selected(vf, fd);
1344 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1346 GtkTreeModel *store;
1347 GtkTreeSelection *selection;
1351 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1352 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1362 GtkTreePath *tpath = work->data;
1366 gtk_tree_model_get_iter(store, &iter, tpath);
1367 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1376 count = g_list_length(slist);
1377 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1383 GList *vflist_selection_get_list(ViewFile *vf)
1385 GtkTreeModel *store;
1386 GtkTreeSelection *selection;
1391 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1392 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1396 GtkTreePath *tpath = work->data;
1400 gtk_tree_model_get_iter(store, &iter, tpath);
1401 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1403 list = g_list_prepend(list, file_data_ref(fd));
1405 if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
1407 /* unexpanded - add whole group */
1408 GList *work2 = fd->sidecar_files;
1411 FileData *sfd = work2->data;
1412 list = g_list_prepend(list, file_data_ref(sfd));
1413 work2 = work2->next;
1419 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1422 return g_list_reverse(list);
1425 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1427 GtkTreeModel *store;
1428 GtkTreeSelection *selection;
1433 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1434 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1438 GtkTreePath *tpath = work->data;
1442 gtk_tree_model_get_iter(store, &iter, tpath);
1443 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1445 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1449 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1452 return g_list_reverse(list);
1455 void vflist_select_all(ViewFile *vf)
1457 GtkTreeSelection *selection;
1459 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1460 gtk_tree_selection_select_all(selection);
1462 VFLIST(vf)->select_fd = NULL;
1465 void vflist_select_none(ViewFile *vf)
1467 GtkTreeSelection *selection;
1469 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1470 gtk_tree_selection_unselect_all(selection);
1473 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1478 tpath = gtk_tree_model_get_path(store, iter);
1479 result = gtk_tree_path_prev(tpath);
1481 gtk_tree_model_get_iter(store, iter, tpath);
1483 gtk_tree_path_free(tpath);
1488 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1490 if (!gtk_tree_model_get_iter_first(store, iter))
1495 GtkTreeIter next = *iter;
1497 if (gtk_tree_model_iter_next(store, &next))
1506 void vflist_select_invert(ViewFile *vf)
1509 GtkTreeSelection *selection;
1510 GtkTreeModel *store;
1513 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1514 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1516 /* Backward iteration prevents scrolling to the end of the list,
1517 * it scrolls to the first selected row instead. */
1518 valid = tree_model_get_iter_last(store, &iter);
1522 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1525 gtk_tree_selection_unselect_iter(selection, &iter);
1527 gtk_tree_selection_select_iter(selection, &iter);
1529 valid = tree_model_iter_prev(store, &iter);
1533 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1537 if (vflist_find_row(vf, fd, &iter) < 0) return;
1539 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1541 if (!vflist_row_is_selected(vf, fd))
1543 GtkTreeSelection *selection;
1544 GtkTreeModel *store;
1547 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1548 gtk_tree_selection_unselect_all(selection);
1549 gtk_tree_selection_select_iter(selection, &iter);
1550 vflist_move_cursor(vf, &iter);
1552 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1553 tpath = gtk_tree_model_get_path(store, &iter);
1554 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1555 gtk_tree_path_free(tpath);
1559 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1562 FileData *fd = NULL;
1564 if (sel_fd->parent) sel_fd = sel_fd->parent;
1573 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1575 if (match >= 0) break;
1578 if (fd) vflist_select_by_fd(vf, fd);
1582 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1584 GtkTreeModel *store;
1586 GtkTreeSelection *selection;
1590 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1592 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1593 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1595 valid = gtk_tree_model_get_iter_first(store, &iter);
1599 gboolean mark_val, selected;
1600 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1602 mark_val = file_data_get_mark(fd, n);
1603 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1607 case MTS_MODE_SET: selected = mark_val;
1609 case MTS_MODE_OR: selected = mark_val || selected;
1611 case MTS_MODE_AND: selected = mark_val && selected;
1613 case MTS_MODE_MINUS: selected = !mark_val && selected;
1618 gtk_tree_selection_select_iter(selection, &iter);
1620 gtk_tree_selection_unselect_iter(selection, &iter);
1622 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1626 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1628 GtkTreeModel *store;
1629 GtkTreeSelection *selection;
1634 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1636 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1637 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1641 GtkTreePath *tpath = work->data;
1645 gtk_tree_model_get_iter(store, &iter, tpath);
1646 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1648 /* the change has a very limited range and the standard notification would trigger
1649 complete re-read of the directory - try to do only minimal update instead */
1650 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1654 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1656 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1658 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1662 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1664 vf_refresh_idle(vf);
1668 /* mark functions can have various side effects - update all columns to be sure */
1669 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1670 /* mark functions can change sidecars too */
1671 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1675 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1679 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1684 *-----------------------------------------------------------------------------
1686 *-----------------------------------------------------------------------------
1689 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
1691 GtkTreeViewColumn *column;
1692 GtkCellRenderer *cell;
1695 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1696 if (!column) return;
1698 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1700 list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1705 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1706 gtk_tree_view_column_set_visible(column, thumb);
1708 if (options->show_star_rating)
1710 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED_WITH_STARS);
1711 if (!column) return;
1712 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1713 gtk_tree_view_column_set_visible(column, TRUE);
1715 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1716 if (!column) return;
1717 gtk_tree_view_column_set_visible(column, FALSE);
1721 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1722 if (!column) return;
1723 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1724 gtk_tree_view_column_set_visible(column, TRUE);
1726 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED_WITH_STARS);
1727 if (!column) return;
1728 gtk_tree_view_column_set_visible(column, FALSE);
1731 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_STAR_RATING);
1732 if (!column) return;
1733 gtk_tree_view_column_set_visible(column, !multiline && options->show_star_rating);
1735 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1736 if (!column) return;
1737 gtk_tree_view_column_set_visible(column, !multiline);
1739 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1740 if (!column) return;
1741 gtk_tree_view_column_set_visible(column, !multiline);
1744 static gboolean vflist_is_multiline(ViewFile *vf)
1746 return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
1750 static void vflist_populate_view(ViewFile *vf, gboolean force)
1752 GtkTreeStore *store;
1755 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1761 vflist_store_clear(vf, FALSE);
1766 vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
1768 selected = vflist_selection_get_list(vf);
1770 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force);
1772 if (selected && vflist_selection_count(vf, NULL) == 0)
1774 /* all selected files disappeared */
1775 vflist_select_closest(vf, selected->data);
1778 filelist_free(selected);
1781 vf_thumb_update(vf);
1784 gboolean vflist_refresh(ViewFile *vf)
1787 gboolean ret = TRUE;
1789 old_list = vf->list;
1792 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1795 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1797 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1799 if (vf->marks_enabled)
1801 // When marks are enabled, lock FileDatas so that we don't end up re-parsing XML
1802 // each time a mark is changed.
1803 file_data_lock_list(vf->list);
1807 // FIXME: only do this when needed (aka when we just switched from
1808 // FIXME: marks-enabled to marks-disabled)
1809 file_data_unlock_list(vf->list);
1812 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1813 vf->list = g_list_first(vf->list);
1814 vf->list = file_data_filter_file_filter_list(vf->list, vf_file_filter_get_filter(vf));
1816 vf->list = g_list_first(vf->list);
1817 vf->list = file_data_filter_class_list(vf->list, vf_class_get_filter(vf));
1819 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1821 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1822 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1825 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1827 vflist_populate_view(vf, FALSE);
1829 DEBUG_1("%s vflist_refresh: free filelist", get_exec_time());
1831 filelist_free(old_list);
1832 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1839 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1841 #define CELL_HEIGHT_OVERRIDE 512
1843 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1847 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1848 if (spec && G_IS_PARAM_SPEC_INT(spec))
1850 GParamSpecInt *spec_int;
1852 spec_int = G_PARAM_SPEC_INT(spec);
1853 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1857 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1859 static GdkColor color;
1860 static GtkWidget *done = NULL;
1866 style = gtk_widget_get_style(widget);
1867 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1868 shift_color(&color, -1, 0);
1875 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1876 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1878 ViewFile *vf = data;
1881 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1882 g_object_set(G_OBJECT(cell),
1883 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1884 "cell-background-set", set, NULL);
1887 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1889 GtkTreeViewColumn *column;
1890 GtkCellRenderer *renderer;
1892 column = gtk_tree_view_column_new();
1893 gtk_tree_view_column_set_title(column, title);
1894 gtk_tree_view_column_set_min_width(column, 4);
1898 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1899 renderer = gtk_cell_renderer_text_new();
1902 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1904 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1905 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1907 gtk_tree_view_column_set_expand(column, TRUE);
1911 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1912 renderer = gtk_cell_renderer_pixbuf_new();
1913 cell_renderer_height_override(renderer);
1914 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1915 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1918 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1919 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1920 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1922 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1925 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1927 ViewFile *vf = data;
1928 GtkTreeStore *store;
1929 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1935 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1936 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1939 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1941 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1943 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1946 /* the change has a very limited range and the standard notification would trigger
1947 complete re-read of the directory - try to do only minimal update instead */
1948 file_data_unregister_notify_func(vf_notify_cb, vf);
1949 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1950 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1952 vf_refresh_idle(vf);
1956 /* mark functions can have various side effects - update all columns to be sure */
1957 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1958 /* mark functions can change sidecars too */
1959 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1961 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1963 gtk_tree_path_free(path);
1966 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1968 GtkTreeViewColumn *column;
1969 GtkCellRenderer *renderer;
1971 renderer = gtk_cell_renderer_toggle_new();
1972 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1974 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1975 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1976 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1978 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1979 gtk_tree_view_column_set_fixed_width(column, 22);
1980 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1983 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1987 *-----------------------------------------------------------------------------
1989 *-----------------------------------------------------------------------------
1992 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1995 if (!dir_fd) return FALSE;
1996 if (vf->dir_fd == dir_fd) return TRUE;
1998 file_data_unref(vf->dir_fd);
1999 vf->dir_fd = file_data_ref(dir_fd);
2001 /* force complete reload */
2002 vflist_store_clear(vf, TRUE);
2004 filelist_free(vf->list);
2007 ret = vf_refresh(vf);
2008 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
2012 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
2014 ViewFile *vf = data;
2016 file_data_unregister_notify_func(vf_notify_cb, vf);
2018 vflist_select_idle_cancel(vf);
2019 vf_refresh_idle_cancel(vf);
2022 filelist_free(vf->list);
2025 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
2027 GtkTreeStore *store;
2028 GtkTreeSelection *selection;
2029 GType flist_types[FILE_COLUMN_COUNT];
2033 vf->info = g_new0(ViewFileInfoList, 1);
2035 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
2036 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
2037 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
2038 flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING;
2039 flist_types[FILE_COLUMN_FORMATTED_WITH_STARS] = G_TYPE_STRING;
2040 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
2041 flist_types[FILE_COLUMN_STAR_RATING] = G_TYPE_STRING;
2042 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
2043 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
2044 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
2045 flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN;
2046 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
2047 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
2048 flist_types[i] = G_TYPE_BOOLEAN;
2050 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
2052 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2053 g_object_unref(store);
2055 g_signal_connect(G_OBJECT(vf->listview), "row-expanded",
2056 G_CALLBACK(vflist_expand_cb), vf);
2058 g_signal_connect(G_OBJECT(vf->listview), "row-collapsed",
2059 G_CALLBACK(vflist_collapse_cb), vf);
2061 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2062 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
2063 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
2065 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2066 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2068 gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(vf->listview), -1);
2072 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2074 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2075 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
2079 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2080 g_assert(column == FILE_VIEW_COLUMN_THUMB);
2083 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE);
2084 g_assert(column == FILE_VIEW_COLUMN_FORMATTED);
2087 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED_WITH_STARS, _("NameStars"), FALSE, FALSE, TRUE);
2088 g_assert(column == FILE_VIEW_COLUMN_FORMATTED_WITH_STARS);
2091 vflist_listview_add_column(vf, FILE_COLUMN_STAR_RATING, _("Stars"), FALSE, FALSE, FALSE);
2092 g_assert(column == FILE_VIEW_COLUMN_STAR_RATING);
2095 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2096 g_assert(column == FILE_VIEW_COLUMN_SIZE);
2099 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2100 g_assert(column == FILE_VIEW_COLUMN_DATE);
2103 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2107 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2109 if (VFLIST(vf)->thumbs_enabled == enable) return;
2111 VFLIST(vf)->thumbs_enabled = enable;
2113 /* vflist_populate_view is better than vf_refresh:
2114 - no need to re-read the directory
2115 - force update because the formatted string has changed
2119 vflist_populate_view(vf, TRUE);
2120 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
2124 void vflist_marks_set(ViewFile *vf, gboolean enable)
2126 GList *columns, *work;
2128 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2133 GtkTreeViewColumn *column = work->data;
2134 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2137 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2138 gtk_tree_view_column_set_visible(column, enable);
2143 // Previously disabled, which means that vf->list is complete
2144 file_data_lock_list(vf->list);
2148 // Previously enabled, which means that vf->list is incomplete
2151 g_list_free(columns);
2154 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */