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"
38 #include "ui_fileops.h"
41 #include "ui_tree_edit.h"
42 #include "uri_utils.h"
43 #include "view_file.h"
46 #include <gdk/gdkkeysyms.h> /* for keyboard values */
48 /* Index to tree store */
50 FILE_COLUMN_POINTER = 0,
53 FILE_COLUMN_FORMATTED,
54 FILE_COLUMN_FORMATTED_WITH_STARS,
57 FILE_COLUMN_STAR_RATING,
63 FILE_COLUMN_MARKS_LAST = FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
68 /* Index to tree view */
70 FILE_VIEW_COLUMN_MARKS = 0,
71 FILE_VIEW_COLUMN_MARKS_LAST = FILE_VIEW_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
72 FILE_VIEW_COLUMN_THUMB,
73 FILE_VIEW_COLUMN_FORMATTED,
74 FILE_VIEW_COLUMN_FORMATTED_WITH_STARS,
75 FILE_VIEW_COLUMN_STAR_RATING,
76 FILE_VIEW_COLUMN_SIZE,
77 FILE_VIEW_COLUMN_DATE,
78 FILE_VIEW_COLUMN_COUNT
83 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd);
84 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data);
85 static void vflist_populate_view(ViewFile *vf, gboolean force);
86 static gboolean vflist_is_multiline(ViewFile *vf);
87 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded);
91 *-----------------------------------------------------------------------------
93 *-----------------------------------------------------------------------------
100 } ViewFileFindRowData;
102 static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
104 ViewFileFindRowData *find = data;
106 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
117 static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter)
120 ViewFileFindRowData data = {fd, iter, FALSE, 0};
122 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
123 gtk_tree_model_foreach(store, vflist_find_row_cb, &data);
133 static FileData *vflist_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter)
136 GtkTreeViewColumn *column;
138 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), x, y,
139 &tpath, &column, NULL, NULL))
145 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
146 gtk_tree_model_get_iter(store, &row, tpath);
147 gtk_tree_path_free(tpath);
148 gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &fd, -1);
156 static gboolean vflist_store_clear_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
159 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
161 /* it seems that gtk_tree_store_clear may call some callbacks
162 that use the column. Set the pointer to NULL to be safe. */
163 gtk_tree_store_set(GTK_TREE_STORE(model), iter, FILE_COLUMN_POINTER, NULL, -1);
168 static void vflist_store_clear(ViewFile *vf, gboolean unlock_files)
173 if (unlock_files && vf->marks_enabled)
175 // unlock locked files in this directory
176 filelist_read(vf->dir_fd, &files, NULL);
179 FileData *fd = files->data;
181 file_data_unlock(fd);
182 file_data_unref(fd); // undo the ref that got added in filelist_read
187 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
188 gtk_tree_model_foreach(store, vflist_store_clear_cb, NULL);
189 gtk_tree_store_clear(GTK_TREE_STORE(store));
192 void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set)
197 if (vflist_find_row(vf, fd, &iter) < 0) return;
198 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
199 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
202 static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
207 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
209 tpath = gtk_tree_model_get_path(store, iter);
210 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
211 gtk_tree_path_free(tpath);
216 *-----------------------------------------------------------------------------
218 *-----------------------------------------------------------------------------
221 static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
222 GtkSelectionData *selection_data, guint info,
223 guint time, gpointer data)
228 if (!VFLIST(vf)->click_fd) return;
230 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
232 list = vf_selection_get_list(vf);
236 list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
240 uri_selection_data_set_uris_from_filelist(selection_data, list);
244 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
248 vflist_color_set(vf, VFLIST(vf)->click_fd, TRUE);
250 if (VFLIST(vf)->thumbs_enabled &&
251 VFLIST(vf)->click_fd && VFLIST(vf)->click_fd->thumb_pixbuf)
255 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
256 items = vf_selection_count(vf, NULL);
260 dnd_set_drag_icon(widget, context, VFLIST(vf)->click_fd->thumb_pixbuf, items);
264 static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
268 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
270 if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE)
276 static void vflist_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context,
277 int x, int y, GtkSelectionData *selection,
278 guint info, guint time, gpointer data)
282 if (info == TARGET_TEXT_PLAIN) {
283 FileData *fd = vflist_find_data_by_coord(vf, x, y, NULL);
286 /* Add keywords to file */
287 gchar *str = (gchar *) gtk_selection_data_get_text(selection);
288 GList *kw_list = string_to_keywords_list(str);
290 metadata_append_list(fd, KEYWORD_KEY, kw_list);
291 string_list_free(kw_list);
297 void vflist_dnd_init(ViewFile *vf)
299 gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
300 dnd_file_drag_types, dnd_file_drag_types_count,
301 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
302 gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL,
303 dnd_file_drag_types, dnd_file_drag_types_count,
304 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
306 g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
307 G_CALLBACK(vflist_dnd_get), vf);
308 g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
309 G_CALLBACK(vflist_dnd_begin), vf);
310 g_signal_connect(G_OBJECT(vf->listview), "drag_end",
311 G_CALLBACK(vflist_dnd_end), vf);
312 g_signal_connect(G_OBJECT(vf->listview), "drag_data_received",
313 G_CALLBACK(vflist_drag_data_received), vf);
317 *-----------------------------------------------------------------------------
319 *-----------------------------------------------------------------------------
322 GList *vflist_selection_get_one(ViewFile *vf, FileData *fd)
324 GList *list = g_list_append(NULL, file_data_ref(fd));
326 if (fd->sidecar_files)
328 /* check if the row is expanded */
332 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
333 if (vflist_find_row(vf, fd, &iter) >= 0)
337 tpath = gtk_tree_model_get_path(store, &iter);
338 if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
340 /* unexpanded - add whole group */
341 GList *work = fd->sidecar_files;
344 FileData *sfd = work->data;
345 list = g_list_prepend(list, file_data_ref(sfd));
349 gtk_tree_path_free(tpath);
351 list = g_list_reverse(list);
357 GList *vflist_pop_menu_file_list(ViewFile *vf)
359 if (!VFLIST(vf)->click_fd) return NULL;
361 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
363 return vf_selection_get_list(vf);
365 return vflist_selection_get_one(vf, VFLIST(vf)->click_fd);
369 void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
373 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
377 list = vf_selection_get_list(vf);
378 view_window_new_from_list(list);
383 view_window_new(VFLIST(vf)->click_fd);
387 void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
392 list = vf_pop_menu_file_list(vf);
393 if (options->file_ops.enable_in_place_rename &&
394 list && !list->next && VFLIST(vf)->click_fd)
401 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
402 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
406 tpath = gtk_tree_model_get_path(store, &iter);
407 tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
408 FILE_VIEW_COLUMN_FORMATTED, VFLIST(vf)->click_fd->name,
409 vflist_row_rename_cb, vf);
410 gtk_tree_path_free(tpath);
415 file_util_rename(NULL, list, vf->listview);
418 void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
422 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
425 layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
429 vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled);
433 void vflist_star_rating_set(ViewFile *vf, gboolean enable)
435 GList *columns, *work;
437 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
442 GtkTreeViewColumn *column = work->data;
443 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
446 if (vflist_is_multiline(vf))
448 if (col_idx == FILE_COLUMN_FORMATTED_WITH_STARS)
450 gtk_tree_view_column_set_visible(column, enable);
452 if (col_idx == FILE_COLUMN_FORMATTED)
454 gtk_tree_view_column_set_visible(column, !enable);
459 if (col_idx == FILE_COLUMN_STAR_RATING)
461 gtk_tree_view_column_set_visible(column, enable);
465 g_list_free(columns);
468 void vflist_pop_menu_show_star_rating_cb(GtkWidget *widget, gpointer data)
472 options->show_star_rating = !options->show_star_rating;
474 vflist_populate_view(vf, TRUE);
476 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
477 vflist_star_rating_set(vf, options->show_star_rating);
480 void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
484 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
486 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
489 void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
492 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
493 VFLIST(vf)->click_fd = NULL;
499 *-----------------------------------------------------------------------------
501 *-----------------------------------------------------------------------------
504 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
509 if (!new || !new[0]) return FALSE;
511 new_path = g_build_filename(vf->dir_fd->path, new, NULL);
513 if (strchr(new, G_DIR_SEPARATOR) != NULL)
515 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
516 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
521 gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL);
522 FileData *fd = file_data_new_group(old_path); /* get the fd from cache */
523 file_util_rename_simple(fd, new_path, vf->listview);
533 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
541 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return;
542 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
543 tpath = gtk_tree_model_get_path(store, &iter);
544 tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
545 gtk_tree_path_free(tpath);
547 popup_menu_position_clamp(menu, x, y, 0);
550 gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
555 if (event->keyval != GDK_KEY_Menu) return FALSE;
557 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
563 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
564 gtk_tree_model_get_iter(store, &iter, tpath);
565 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
566 gtk_tree_path_free(tpath);
570 VFLIST(vf)->click_fd = NULL;
573 vf->popup = vf_pop_menu(vf);
574 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
579 gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
585 GtkTreeViewColumn *column;
587 vf->clicked_mark = 0;
589 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
590 &tpath, &column, NULL, NULL))
593 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
595 if (bevent->button == MOUSE_BUTTON_LEFT &&
596 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
599 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
600 vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
602 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
604 gtk_tree_model_get_iter(store, &iter, tpath);
605 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
606 gtk_tree_path_free(tpath);
609 VFLIST(vf)->click_fd = fd;
611 if (bevent->button == MOUSE_BUTTON_RIGHT)
613 vf->popup = vf_pop_menu(vf);
614 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
615 bevent->button, bevent->time);
619 if (!fd) return FALSE;
621 if (bevent->button == MOUSE_BUTTON_MIDDLE)
623 if (!vflist_row_is_selected(vf, fd))
625 vflist_color_set(vf, fd, TRUE);
631 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
632 !(bevent->state & GDK_SHIFT_MASK ) &&
633 !(bevent->state & GDK_CONTROL_MASK ) &&
634 vflist_row_is_selected(vf, fd))
636 GtkTreeSelection *selection;
638 gtk_widget_grab_focus(widget);
641 /* returning FALSE and further processing of the event is needed for
642 correct operation of the expander, to show the sidecar files.
643 It however resets the selection of multiple files. With this condition
644 it should work for both cases */
645 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
646 return (gtk_tree_selection_count_selected_rows(selection) > 1);
649 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
651 if (VFLIST(vf)->click_fd->format_class == FORMAT_CLASS_COLLECTION)
653 collection_window_new(VFLIST(vf)->click_fd->path);
657 if (vf->layout) layout_image_full_screen_start(vf->layout);
664 gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
671 if (defined_mouse_buttons(widget, bevent, vf->layout))
676 if (bevent->button == MOUSE_BUTTON_MIDDLE)
678 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
681 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
686 if ((bevent->x != 0 || bevent->y != 0) &&
687 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
688 &tpath, NULL, NULL, NULL))
692 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
693 gtk_tree_model_get_iter(store, &iter, tpath);
694 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
695 gtk_tree_path_free(tpath);
698 if (bevent->button == MOUSE_BUTTON_MIDDLE)
700 if (fd && VFLIST(vf)->click_fd == fd)
702 GtkTreeSelection *selection;
704 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
705 if (vflist_row_is_selected(vf, fd))
707 gtk_tree_selection_unselect_iter(selection, &iter);
711 gtk_tree_selection_select_iter(selection, &iter);
717 if (fd && VFLIST(vf)->click_fd == fd &&
718 !(bevent->state & GDK_SHIFT_MASK ) &&
719 !(bevent->state & GDK_CONTROL_MASK ) &&
720 vflist_row_is_selected(vf, fd))
722 GtkTreeSelection *selection;
724 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
725 gtk_tree_selection_unselect_all(selection);
726 gtk_tree_selection_select_iter(selection, &iter);
727 vflist_move_cursor(vf, &iter);
733 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
735 FileData *read_ahead_fd = NULL;
741 cur_fd = layout_image_get_fd(vf->layout);
742 if (sel_fd == cur_fd) return; /* no change */
744 row = g_list_index(vf->list, sel_fd);
745 // FIXME sidecar data
747 if (sel_fd && options->image.enable_read_ahead && row >= 0)
749 if (row > g_list_index(vf->list, cur_fd) &&
750 (guint) (row + 1) < vf_count(vf, NULL))
752 read_ahead_fd = vf_index_get_data(vf, row + 1);
756 read_ahead_fd = vf_index_get_data(vf, row - 1);
760 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
763 static gboolean vflist_select_idle_cb(gpointer data)
769 VFLIST(vf)->select_idle_id = 0;
775 if (VFLIST(vf)->select_fd)
777 vflist_select_image(vf, VFLIST(vf)->select_fd);
778 VFLIST(vf)->select_fd = NULL;
781 VFLIST(vf)->select_idle_id = 0;
785 static void vflist_select_idle_cancel(ViewFile *vf)
787 if (VFLIST(vf)->select_idle_id)
789 g_source_remove(VFLIST(vf)->select_idle_id);
790 VFLIST(vf)->select_idle_id = 0;
794 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
795 gboolean path_currently_selected, gpointer data)
799 GtkTreePath *cursor_path;
801 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &cursor_path, NULL);
804 gtk_tree_model_get_iter(store, &iter, cursor_path);
805 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
806 gtk_tree_path_free(cursor_path);
810 VFLIST(vf)->select_fd = NULL;
814 !VFLIST(vf)->select_idle_id)
816 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
822 static void vflist_expand_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
825 vflist_set_expanded(vf, iter, TRUE);
828 static void vflist_collapse_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
831 vflist_set_expanded(vf, iter, FALSE);
835 *-----------------------------------------------------------------------------
837 *-----------------------------------------------------------------------------
841 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)
843 gboolean multiline = vflist_is_multiline(vf);
850 text = g_strdup_printf("%s %s\n%s\n%s\n%s", name, expanded ? "" : sidecars, size, time, star_rating);
854 text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time);
859 text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars);
864 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded)
872 gchar *formatted_with_stars;
874 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
876 gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
877 FILE_COLUMN_NAME, &name,
878 FILE_COLUMN_SIDECARS, &sidecars,
879 FILE_COLUMN_SIZE, &size,
880 FILE_COLUMN_DATE, &time,
881 FILE_COLUMN_STAR_RATING, &star_rating,
884 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded, FALSE, NULL);
885 formatted_with_stars = vflist_get_formatted(vf, name, sidecars, size, time, expanded, TRUE, star_rating);
887 gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED, formatted,
888 FILE_COLUMN_EXPANDED, expanded,
890 gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED_WITH_STARS, formatted_with_stars,
891 FILE_COLUMN_EXPANDED, expanded,
898 g_free(formatted_with_stars);
901 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
904 gchar *sidecars = NULL;
906 const gchar *time = text_from_time(fd->date);
907 gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
908 const gchar *disabled_grouping;
910 gchar *formatted_with_stars;
911 gboolean expanded = FALSE;
914 if (options->show_star_rating && fd->rating != STAR_RATING_NOT_READ)
916 star_rating = convert_rating_to_stars(fd->rating);
923 if (fd->sidecar_files) /* expanded has no effect on files without sidecars */
925 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_EXPANDED, &expanded, -1);
928 sidecars = file_data_sc_list_to_string(fd);
930 disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
931 name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
932 size = text_from_size(fd->size);
934 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded, FALSE, NULL);
935 formatted_with_stars = vflist_get_formatted(vf, name, sidecars, size, time, expanded, TRUE, star_rating);
937 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
938 FILE_COLUMN_VERSION, fd->version,
939 FILE_COLUMN_THUMB, fd->thumb_pixbuf,
940 FILE_COLUMN_FORMATTED, formatted,
941 FILE_COLUMN_FORMATTED_WITH_STARS, formatted_with_stars,
942 FILE_COLUMN_SIDECARS, sidecars,
943 FILE_COLUMN_NAME, name,
944 FILE_COLUMN_STAR_RATING, star_rating,
945 FILE_COLUMN_SIZE, size,
946 FILE_COLUMN_DATE, time,
947 #define STORE_SET_IS_SLOW 1
948 #if STORE_SET_IS_SLOW
949 /* this is 3x faster on a directory with 20000 files */
950 FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
951 FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
952 FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
953 FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
954 FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
955 FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
956 FILE_COLUMN_MARKS + 6, file_data_get_mark(fd, 6),
957 FILE_COLUMN_MARKS + 7, file_data_get_mark(fd, 7),
958 FILE_COLUMN_MARKS + 8, file_data_get_mark(fd, 8),
959 FILE_COLUMN_MARKS + 9, file_data_get_mark(fd, 9),
960 #if FILEDATA_MARKS_SIZE != 10
961 #error this needs to be updated
964 FILE_COLUMN_COLOR, FALSE, -1);
966 #if !STORE_SET_IS_SLOW
969 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
970 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
979 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected, gboolean force)
984 gint num_ordered = 0;
985 gint num_prepended = 0;
987 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
993 FileData *fd = work->data;
994 gboolean done = FALSE;
998 FileData *old_fd = NULL;
999 gint old_version = 0;
1003 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1004 FILE_COLUMN_POINTER, &old_fd,
1005 FILE_COLUMN_VERSION, &old_version,
1015 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
1017 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
1019 if (match == 0) g_warning("multiple fd for the same path");
1035 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
1040 here should be used gtk_tree_store_append, but this function seems to be O(n)
1041 and it seems to be much faster to add new entries to the beginning and reorder later
1044 gtk_tree_store_prepend(store, &new, parent_iter);
1047 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
1048 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected, force);
1050 if (g_list_find(selected, fd))
1052 /* renamed files - the same fd appears at different position - select it again*/
1053 GtkTreeSelection *selection;
1054 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1055 gtk_tree_selection_select_iter(selection, &new);
1062 file_data_unref(old_fd);
1063 valid = gtk_tree_store_remove(store, &iter);
1068 if (fd->version != old_version || force)
1070 vflist_setup_iter(vf, store, &iter, fd);
1071 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected, force);
1074 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1085 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1086 file_data_unref(old_fd);
1088 valid = gtk_tree_store_remove(store, &iter);
1091 /* move the prepended entries to the correct position */
1095 gint num_total = num_prepended + num_ordered;
1096 gint *new_order = g_malloc(num_total * sizeof(gint));
1098 for (i = 0; i < num_total; i++)
1100 if (i < num_ordered)
1101 new_order[i] = num_prepended + i;
1103 new_order[i] = num_total - 1 - i;
1105 gtk_tree_store_reorder(store, parent_iter, new_order);
1111 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
1114 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
1116 GtkTreeStore *store;
1119 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1120 if (!vf->list) return;
1126 FileData *fd = work->data;
1127 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1132 vf->sort_method = type;
1133 vf->sort_ascend = ascend;
1135 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1137 new_order = g_malloc(i * sizeof(gint));
1143 FileData *fd = work->data;
1144 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1149 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1150 gtk_tree_store_reorder(store, NULL, new_order);
1153 g_hash_table_destroy(fd_idx_hash);
1157 *-----------------------------------------------------------------------------
1159 *-----------------------------------------------------------------------------
1163 void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1168 FileData *fd = work->data;
1171 if (fd->thumb_pixbuf) (*done)++;
1173 if (fd->sidecar_files)
1175 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1181 void vflist_read_metadata_progress_count(GList *list, gint *count, gint *done)
1186 FileData *fd = work->data;
1189 if (fd->metadata_in_idle_loaded) (*done)++;
1191 if (fd->sidecar_files)
1193 vflist_read_metadata_progress_count(fd->sidecar_files, count, done);
1199 void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
1201 GtkTreeStore *store;
1204 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1206 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1207 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1210 FileData *vflist_thumb_next_fd(ViewFile *vf)
1213 FileData *fd = NULL;
1215 /* first check the visible files */
1217 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1219 GtkTreeModel *store;
1221 gboolean valid = TRUE;
1223 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1224 gtk_tree_model_get_iter(store, &iter, tpath);
1225 gtk_tree_path_free(tpath);
1228 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1232 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1234 if (!nfd->thumb_pixbuf) fd = nfd;
1236 valid = gtk_tree_model_iter_next(store, &iter);
1240 /* then find first undone */
1244 GList *work = vf->list;
1247 FileData *fd_p = work->data;
1248 if (!fd_p->thumb_pixbuf)
1252 GList *work2 = fd_p->sidecar_files;
1254 while (work2 && !fd)
1257 if (!fd_p->thumb_pixbuf) fd = fd_p;
1258 work2 = work2->next;
1268 void vflist_set_star_fd(ViewFile *vf, FileData *fd)
1270 GtkTreeStore *store;
1277 gchar *formatted_with_stars;
1280 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1282 star_rating = metadata_read_rating_stars(fd);
1284 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1285 gtk_tree_store_set(store, &iter, FILE_COLUMN_STAR_RATING, star_rating, -1);
1287 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1288 FILE_COLUMN_NAME, &name,
1289 FILE_COLUMN_SIDECARS, &sidecars,
1290 FILE_COLUMN_SIZE, &size,
1291 FILE_COLUMN_DATE, &time,
1292 FILE_COLUMN_EXPANDED, &expanded,
1295 formatted_with_stars = vflist_get_formatted(vf, name, sidecars, size, time, expanded, TRUE, star_rating);
1297 gtk_tree_store_set(store, &iter, FILE_COLUMN_FORMATTED_WITH_STARS, formatted_with_stars,
1298 FILE_COLUMN_EXPANDED, expanded,
1301 g_free(star_rating);
1302 g_free(formatted_with_stars);
1305 FileData *vflist_star_next_fd(ViewFile *vf)
1308 FileData *fd = NULL;
1309 FileData *nfd = NULL;
1311 /* first check the visible files */
1313 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1315 GtkTreeModel *store;
1317 gboolean valid = TRUE;
1319 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1320 gtk_tree_model_get_iter(store, &iter, tpath);
1321 gtk_tree_path_free(tpath);
1324 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1326 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1328 if (nfd && nfd->rating == STAR_RATING_NOT_READ)
1333 valid = gtk_tree_model_iter_next(store, &iter);
1338 vf->stars_filedata = fd;
1340 if (vf->stars_id == 0)
1342 vf->stars_id = g_idle_add_full(G_PRIORITY_LOW, vf_stars_cb, vf, NULL);
1347 /* then find first undone */
1351 GList *work = vf->list;
1355 FileData *fd_p = work->data;
1357 if (fd_p && fd_p->rating == STAR_RATING_NOT_READ)
1371 vf->stars_filedata = fd;
1373 if (vf->stars_id == 0)
1375 vf->stars_id = g_idle_add_full(G_PRIORITY_LOW, vf_stars_cb, vf, NULL);
1384 *-----------------------------------------------------------------------------
1386 *-----------------------------------------------------------------------------
1389 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1392 GList *work, *work2;
1397 FileData *list_fd = work->data;
1398 if (list_fd == fd) return p;
1400 work2 = list_fd->sidecar_files;
1403 /* FIXME: return the same index also for sidecars
1404 it is sufficient for next/prev navigation but it should be rewritten
1405 without using indexes at all
1407 FileData *sidecar_fd = work2->data;
1408 if (sidecar_fd == fd) return p;
1409 work2 = work2->next;
1420 *-----------------------------------------------------------------------------
1422 *-----------------------------------------------------------------------------
1425 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1427 GtkTreeModel *store;
1428 GtkTreeSelection *selection;
1431 gboolean found = FALSE;
1433 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1434 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1436 while (!found && work)
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_n, -1);
1444 if (fd_n == fd) found = TRUE;
1447 g_list_foreach(slist, (GFunc)tree_path_free_wrapper, NULL);
1453 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1457 fd = vf_index_get_data(vf, row);
1458 return vflist_row_is_selected(vf, fd);
1461 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1463 GtkTreeModel *store;
1464 GtkTreeSelection *selection;
1468 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1469 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1479 GtkTreePath *tpath = work->data;
1483 gtk_tree_model_get_iter(store, &iter, tpath);
1484 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1493 count = g_list_length(slist);
1494 g_list_foreach(slist, (GFunc)tree_path_free_wrapper, NULL);
1500 GList *vflist_selection_get_list(ViewFile *vf)
1502 GtkTreeModel *store;
1503 GtkTreeSelection *selection;
1508 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1509 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1513 GtkTreePath *tpath = work->data;
1517 gtk_tree_model_get_iter(store, &iter, tpath);
1518 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1520 list = g_list_prepend(list, file_data_ref(fd));
1522 if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
1524 /* unexpanded - add whole group */
1525 GList *work2 = fd->sidecar_files;
1528 FileData *sfd = work2->data;
1529 list = g_list_prepend(list, file_data_ref(sfd));
1530 work2 = work2->next;
1536 g_list_foreach(slist, (GFunc)tree_path_free_wrapper, NULL);
1539 return g_list_reverse(list);
1542 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1544 GtkTreeModel *store;
1545 GtkTreeSelection *selection;
1550 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1551 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1555 GtkTreePath *tpath = work->data;
1559 gtk_tree_model_get_iter(store, &iter, tpath);
1560 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1562 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1566 g_list_foreach(slist, (GFunc)tree_path_free_wrapper, NULL);
1569 return g_list_reverse(list);
1572 void vflist_select_all(ViewFile *vf)
1574 GtkTreeSelection *selection;
1576 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1577 gtk_tree_selection_select_all(selection);
1579 VFLIST(vf)->select_fd = NULL;
1582 void vflist_select_none(ViewFile *vf)
1584 GtkTreeSelection *selection;
1586 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1587 gtk_tree_selection_unselect_all(selection);
1590 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1595 tpath = gtk_tree_model_get_path(store, iter);
1596 result = gtk_tree_path_prev(tpath);
1598 gtk_tree_model_get_iter(store, iter, tpath);
1600 gtk_tree_path_free(tpath);
1605 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1607 if (!gtk_tree_model_get_iter_first(store, iter))
1612 GtkTreeIter next = *iter;
1614 if (gtk_tree_model_iter_next(store, &next))
1623 void vflist_select_invert(ViewFile *vf)
1626 GtkTreeSelection *selection;
1627 GtkTreeModel *store;
1630 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1631 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1633 /* Backward iteration prevents scrolling to the end of the list,
1634 * it scrolls to the first selected row instead. */
1635 valid = tree_model_get_iter_last(store, &iter);
1639 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1642 gtk_tree_selection_unselect_iter(selection, &iter);
1644 gtk_tree_selection_select_iter(selection, &iter);
1646 valid = tree_model_iter_prev(store, &iter);
1650 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1654 if (vflist_find_row(vf, fd, &iter) < 0) return;
1656 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1658 if (!vflist_row_is_selected(vf, fd))
1660 GtkTreeSelection *selection;
1661 GtkTreeModel *store;
1664 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1665 gtk_tree_selection_unselect_all(selection);
1666 gtk_tree_selection_select_iter(selection, &iter);
1667 vflist_move_cursor(vf, &iter);
1669 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1670 tpath = gtk_tree_model_get_path(store, &iter);
1671 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1672 gtk_tree_path_free(tpath);
1676 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1679 FileData *fd = NULL;
1681 if (sel_fd->parent) sel_fd = sel_fd->parent;
1690 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1692 if (match >= 0) break;
1695 if (fd) vflist_select_by_fd(vf, fd);
1699 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1701 GtkTreeModel *store;
1703 GtkTreeSelection *selection;
1707 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1709 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1710 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1712 valid = gtk_tree_model_get_iter_first(store, &iter);
1716 gboolean mark_val, selected;
1717 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1719 mark_val = file_data_get_mark(fd, n);
1720 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1724 case MTS_MODE_SET: selected = mark_val;
1726 case MTS_MODE_OR: selected = mark_val || selected;
1728 case MTS_MODE_AND: selected = mark_val && selected;
1730 case MTS_MODE_MINUS: selected = !mark_val && selected;
1735 gtk_tree_selection_select_iter(selection, &iter);
1737 gtk_tree_selection_unselect_iter(selection, &iter);
1739 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1743 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1745 GtkTreeModel *store;
1746 GtkTreeSelection *selection;
1751 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1753 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1754 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1758 GtkTreePath *tpath = work->data;
1762 gtk_tree_model_get_iter(store, &iter, tpath);
1763 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1765 /* the change has a very limited range and the standard notification would trigger
1766 complete re-read of the directory - try to do only minimal update instead */
1767 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1771 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1773 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1775 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1779 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1781 vf_refresh_idle(vf);
1785 /* mark functions can have various side effects - update all columns to be sure */
1786 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1787 /* mark functions can change sidecars too */
1788 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1792 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1796 g_list_foreach(slist, (GFunc)tree_path_free_wrapper, NULL);
1801 *-----------------------------------------------------------------------------
1803 *-----------------------------------------------------------------------------
1806 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
1808 GtkTreeViewColumn *column;
1809 GtkCellRenderer *cell;
1812 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1813 if (!column) return;
1815 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1817 list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1822 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1823 gtk_tree_view_column_set_visible(column, thumb);
1825 if (options->show_star_rating)
1827 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED_WITH_STARS);
1828 if (!column) return;
1829 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1830 gtk_tree_view_column_set_visible(column, TRUE);
1832 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1833 if (!column) return;
1834 gtk_tree_view_column_set_visible(column, FALSE);
1838 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1839 if (!column) return;
1840 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1841 gtk_tree_view_column_set_visible(column, TRUE);
1843 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED_WITH_STARS);
1844 if (!column) return;
1845 gtk_tree_view_column_set_visible(column, FALSE);
1848 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_STAR_RATING);
1849 if (!column) return;
1850 gtk_tree_view_column_set_visible(column, !multiline && options->show_star_rating);
1852 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1853 if (!column) return;
1854 gtk_tree_view_column_set_visible(column, !multiline);
1856 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1857 if (!column) return;
1858 gtk_tree_view_column_set_visible(column, !multiline);
1861 static gboolean vflist_is_multiline(ViewFile *vf)
1863 return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
1867 static void vflist_populate_view(ViewFile *vf, gboolean force)
1869 GtkTreeStore *store;
1872 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1879 vflist_store_clear(vf, FALSE);
1884 vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
1886 selected = vflist_selection_get_list(vf);
1888 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force);
1890 if (selected && vflist_selection_count(vf, NULL) == 0)
1892 /* all selected files disappeared */
1893 vflist_select_closest(vf, selected->data);
1896 filelist_free(selected);
1899 vf_thumb_update(vf);
1903 gboolean vflist_refresh(ViewFile *vf)
1906 gboolean ret = TRUE;
1908 old_list = vf->list;
1911 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1914 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1916 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1918 if (vf->marks_enabled)
1920 // When marks are enabled, lock FileDatas so that we don't end up re-parsing XML
1921 // each time a mark is changed.
1922 file_data_lock_list(vf->list);
1926 // FIXME: only do this when needed (aka when we just switched from
1927 // FIXME: marks-enabled to marks-disabled)
1928 file_data_unlock_list(vf->list);
1931 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1932 vf->list = g_list_first(vf->list);
1933 vf->list = file_data_filter_file_filter_list(vf->list, vf_file_filter_get_filter(vf));
1935 vf->list = g_list_first(vf->list);
1936 vf->list = file_data_filter_class_list(vf->list, vf_class_get_filter(vf));
1938 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1940 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1941 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1944 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1946 vflist_populate_view(vf, FALSE);
1948 DEBUG_1("%s vflist_refresh: free filelist", get_exec_time());
1950 filelist_free(old_list);
1951 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1958 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1960 #define CELL_HEIGHT_OVERRIDE 512
1962 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1966 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1967 if (spec && G_IS_PARAM_SPEC_INT(spec))
1969 GParamSpecInt *spec_int;
1971 spec_int = G_PARAM_SPEC_INT(spec);
1972 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1976 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1978 static GdkColor color;
1979 static GtkWidget *done = NULL;
1985 style = gtk_widget_get_style(widget);
1986 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1987 shift_color(&color, -1, 0);
1994 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1995 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1997 ViewFile *vf = data;
2000 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
2001 g_object_set(G_OBJECT(cell),
2002 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
2003 "cell-background-set", set, NULL);
2006 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
2008 GtkTreeViewColumn *column;
2009 GtkCellRenderer *renderer;
2011 column = gtk_tree_view_column_new();
2012 gtk_tree_view_column_set_title(column, title);
2013 gtk_tree_view_column_set_min_width(column, 4);
2017 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
2018 renderer = gtk_cell_renderer_text_new();
2021 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
2023 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2024 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
2026 gtk_tree_view_column_set_expand(column, TRUE);
2030 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2031 renderer = gtk_cell_renderer_pixbuf_new();
2032 cell_renderer_height_override(renderer);
2033 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2034 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
2037 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
2038 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
2039 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
2041 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
2044 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
2046 ViewFile *vf = data;
2047 GtkTreeStore *store;
2048 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
2054 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2055 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
2058 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
2060 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
2062 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
2065 /* the change has a very limited range and the standard notification would trigger
2066 complete re-read of the directory - try to do only minimal update instead */
2067 file_data_unregister_notify_func(vf_notify_cb, vf);
2068 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
2069 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
2071 vf_refresh_idle(vf);
2075 /* mark functions can have various side effects - update all columns to be sure */
2076 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
2077 /* mark functions can change sidecars too */
2078 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
2080 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2082 gtk_tree_path_free(path);
2085 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
2087 GtkTreeViewColumn *column;
2088 GtkCellRenderer *renderer;
2090 renderer = gtk_cell_renderer_toggle_new();
2091 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
2093 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2094 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
2095 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
2097 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
2098 gtk_tree_view_column_set_fixed_width(column, 22);
2099 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
2102 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
2106 *-----------------------------------------------------------------------------
2108 *-----------------------------------------------------------------------------
2111 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
2114 if (!dir_fd) return FALSE;
2115 if (vf->dir_fd == dir_fd) return TRUE;
2117 file_data_unref(vf->dir_fd);
2118 vf->dir_fd = file_data_ref(dir_fd);
2120 /* force complete reload */
2121 vflist_store_clear(vf, TRUE);
2123 filelist_free(vf->list);
2126 ret = vf_refresh(vf);
2127 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
2131 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
2133 ViewFile *vf = data;
2135 file_data_unregister_notify_func(vf_notify_cb, vf);
2137 vflist_select_idle_cancel(vf);
2138 vf_refresh_idle_cancel(vf);
2142 filelist_free(vf->list);
2145 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
2147 GtkTreeStore *store;
2148 GtkTreeSelection *selection;
2149 GType flist_types[FILE_COLUMN_COUNT];
2153 vf->info = g_new0(ViewFileInfoList, 1);
2155 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
2156 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
2157 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
2158 flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING;
2159 flist_types[FILE_COLUMN_FORMATTED_WITH_STARS] = G_TYPE_STRING;
2160 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
2161 flist_types[FILE_COLUMN_STAR_RATING] = G_TYPE_STRING;
2162 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
2163 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
2164 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
2165 flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN;
2166 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
2167 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
2168 flist_types[i] = G_TYPE_BOOLEAN;
2170 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
2172 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2173 g_object_unref(store);
2175 g_signal_connect(G_OBJECT(vf->listview), "row-expanded",
2176 G_CALLBACK(vflist_expand_cb), vf);
2178 g_signal_connect(G_OBJECT(vf->listview), "row-collapsed",
2179 G_CALLBACK(vflist_collapse_cb), vf);
2181 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2182 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
2183 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
2185 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2186 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2188 gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(vf->listview), -1);
2192 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2194 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2195 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
2199 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2200 g_assert(column == FILE_VIEW_COLUMN_THUMB);
2203 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE);
2204 g_assert(column == FILE_VIEW_COLUMN_FORMATTED);
2207 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED_WITH_STARS, _("NameStars"), FALSE, FALSE, TRUE);
2208 g_assert(column == FILE_VIEW_COLUMN_FORMATTED_WITH_STARS);
2211 vflist_listview_add_column(vf, FILE_COLUMN_STAR_RATING, _("Stars"), FALSE, FALSE, FALSE);
2212 g_assert(column == FILE_VIEW_COLUMN_STAR_RATING);
2215 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2216 g_assert(column == FILE_VIEW_COLUMN_SIZE);
2219 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2220 g_assert(column == FILE_VIEW_COLUMN_DATE);
2223 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2227 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2229 if (VFLIST(vf)->thumbs_enabled == enable) return;
2231 VFLIST(vf)->thumbs_enabled = enable;
2233 /* vflist_populate_view is better than vf_refresh:
2234 - no need to re-read the directory
2235 - force update because the formatted string has changed
2239 vflist_populate_view(vf, TRUE);
2240 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
2244 void vflist_marks_set(ViewFile *vf, gboolean enable)
2246 GList *columns, *work;
2248 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2253 GtkTreeViewColumn *column = work->data;
2254 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2257 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2258 gtk_tree_view_column_set_visible(column, enable);
2263 // Previously disabled, which means that vf->list is complete
2264 file_data_lock_list(vf->list);
2268 // Previously enabled, which means that vf->list is incomplete
2271 g_list_free(columns);
2274 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */