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"
29 #include "layout-image.h"
33 #include "ui-fileops.h"
35 #include "ui-tree-edit.h"
36 #include "uri-utils.h"
37 #include "view-file.h"
39 /* Index to tree store */
41 FILE_COLUMN_POINTER = 0,
44 FILE_COLUMN_FORMATTED,
45 FILE_COLUMN_FORMATTED_WITH_STARS,
48 FILE_COLUMN_STAR_RATING,
54 FILE_COLUMN_MARKS_LAST = FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
59 /* Index to tree view */
61 FILE_VIEW_COLUMN_MARKS = 0,
62 FILE_VIEW_COLUMN_MARKS_LAST = FILE_VIEW_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
63 FILE_VIEW_COLUMN_THUMB,
64 FILE_VIEW_COLUMN_FORMATTED,
65 FILE_VIEW_COLUMN_FORMATTED_WITH_STARS,
66 FILE_VIEW_COLUMN_STAR_RATING,
67 FILE_VIEW_COLUMN_SIZE,
68 FILE_VIEW_COLUMN_DATE,
69 FILE_VIEW_COLUMN_COUNT
74 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd);
75 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old_name, const gchar *new_name, gpointer data);
76 static void vflist_populate_view(ViewFile *vf, gboolean force);
77 static gboolean vflist_is_multiline(ViewFile *vf);
78 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded);
82 *-----------------------------------------------------------------------------
84 *-----------------------------------------------------------------------------
91 } ViewFileFindRowData;
93 static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *UNUSED(path), GtkTreeIter *iter, gpointer data)
95 ViewFileFindRowData *find = (ViewFileFindRowData *)data;
97 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
108 static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter)
111 ViewFileFindRowData data = {fd, iter, FALSE, 0};
113 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
114 gtk_tree_model_foreach(store, vflist_find_row_cb, &data);
124 static FileData *vflist_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *UNUSED(iter))
127 GtkTreeViewColumn *column;
129 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), x, y,
130 &tpath, &column, NULL, NULL))
136 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
137 gtk_tree_model_get_iter(store, &row, tpath);
138 gtk_tree_path_free(tpath);
139 gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &fd, -1);
147 static gboolean vflist_store_clear_cb(GtkTreeModel *model, GtkTreePath *UNUSED(path), GtkTreeIter *iter, gpointer UNUSED(data))
150 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
152 /* it seems that gtk_tree_store_clear may call some callbacks
153 that use the column. Set the pointer to NULL to be safe. */
154 gtk_tree_store_set(GTK_TREE_STORE(model), iter, FILE_COLUMN_POINTER, NULL, -1);
159 static void vflist_store_clear(ViewFile *vf, gboolean unlock_files)
164 if (unlock_files && vf->marks_enabled)
166 // unlock locked files in this directory
167 filelist_read(vf->dir_fd, &files, NULL);
170 FileData *fd = static_cast<FileData *>(files->data);
172 file_data_unlock(fd);
173 file_data_unref(fd); // undo the ref that got added in filelist_read
178 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
179 gtk_tree_model_foreach(store, vflist_store_clear_cb, NULL);
180 gtk_tree_store_clear(GTK_TREE_STORE(store));
183 void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set)
188 if (vflist_find_row(vf, fd, &iter) < 0) return;
189 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
190 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
193 static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
198 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
200 tpath = gtk_tree_model_get_path(store, iter);
201 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
202 gtk_tree_path_free(tpath);
207 *-----------------------------------------------------------------------------
209 *-----------------------------------------------------------------------------
212 static void vflist_dnd_get(GtkWidget *UNUSED(widget), GdkDragContext *UNUSED(context),
213 GtkSelectionData *selection_data, guint UNUSED(info),
214 guint UNUSED(time), gpointer data)
216 ViewFile *vf = (ViewFile *)data;
219 if (!VFLIST(vf)->click_fd) return;
221 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
223 list = vf_selection_get_list(vf);
227 list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
231 uri_selection_data_set_uris_from_filelist(selection_data, list);
235 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
237 ViewFile *vf = (ViewFile *)data;
239 vflist_color_set(vf, VFLIST(vf)->click_fd, TRUE);
241 if (VFLIST(vf)->thumbs_enabled &&
242 VFLIST(vf)->click_fd && VFLIST(vf)->click_fd->thumb_pixbuf)
246 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
247 items = vf_selection_count(vf, NULL);
251 dnd_set_drag_icon(widget, context, VFLIST(vf)->click_fd->thumb_pixbuf, items);
255 static void vflist_dnd_end(GtkWidget *UNUSED(widget), GdkDragContext *context, gpointer data)
257 ViewFile *vf = (ViewFile *)data;
259 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
261 if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE)
267 static void vflist_drag_data_received(GtkWidget *UNUSED(entry_widget), GdkDragContext *UNUSED(context),
268 int x, int y, GtkSelectionData *selection,
269 guint info, guint UNUSED(time), gpointer data)
271 ViewFile *vf = (ViewFile *)data;
273 if (info == TARGET_TEXT_PLAIN) {
274 FileData *fd = vflist_find_data_by_coord(vf, x, y, NULL);
277 /* Add keywords to file */
278 gchar *str = (gchar *) gtk_selection_data_get_text(selection);
279 GList *kw_list = string_to_keywords_list(str);
281 metadata_append_list(fd, KEYWORD_KEY, kw_list);
282 string_list_free(kw_list);
288 void vflist_dnd_init(ViewFile *vf)
290 gtk_drag_source_set(vf->listview, static_cast<GdkModifierType>(GDK_BUTTON1_MASK | GDK_BUTTON2_MASK),
291 dnd_file_drag_types, dnd_file_drag_types_count,
292 static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK));
293 gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL,
294 dnd_file_drag_types, dnd_file_drag_types_count,
295 static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK));
297 g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
298 G_CALLBACK(vflist_dnd_get), vf);
299 g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
300 G_CALLBACK(vflist_dnd_begin), vf);
301 g_signal_connect(G_OBJECT(vf->listview), "drag_end",
302 G_CALLBACK(vflist_dnd_end), vf);
303 g_signal_connect(G_OBJECT(vf->listview), "drag_data_received",
304 G_CALLBACK(vflist_drag_data_received), vf);
308 *-----------------------------------------------------------------------------
310 *-----------------------------------------------------------------------------
313 GList *vflist_selection_get_one(ViewFile *vf, FileData *fd)
315 GList *list = g_list_append(NULL, file_data_ref(fd));
317 if (fd->sidecar_files)
319 /* check if the row is expanded */
323 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
324 if (vflist_find_row(vf, fd, &iter) >= 0)
328 tpath = gtk_tree_model_get_path(store, &iter);
329 if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
331 /* unexpanded - add whole group */
332 GList *work = fd->sidecar_files;
335 FileData *sfd = (FileData *)work->data;
336 list = g_list_prepend(list, file_data_ref(sfd));
340 gtk_tree_path_free(tpath);
342 list = g_list_reverse(list);
348 GList *vflist_pop_menu_file_list(ViewFile *vf)
350 if (!VFLIST(vf)->click_fd) return NULL;
352 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
354 return vf_selection_get_list(vf);
356 return vflist_selection_get_one(vf, VFLIST(vf)->click_fd);
360 void vflist_pop_menu_view_cb(GtkWidget *UNUSED(widget), gpointer data)
362 ViewFile *vf = (ViewFile *)data;
364 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
368 list = vf_selection_get_list(vf);
369 view_window_new_from_list(list);
374 view_window_new(VFLIST(vf)->click_fd);
378 void vflist_pop_menu_rename_cb(GtkWidget *UNUSED(widget), gpointer data)
380 ViewFile *vf = (ViewFile *)data;
383 list = vf_pop_menu_file_list(vf);
384 if (options->file_ops.enable_in_place_rename &&
385 list && !list->next && VFLIST(vf)->click_fd)
392 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
393 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
397 tpath = gtk_tree_model_get_path(store, &iter);
398 tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
399 FILE_VIEW_COLUMN_FORMATTED, VFLIST(vf)->click_fd->name,
400 vflist_row_rename_cb, vf);
401 gtk_tree_path_free(tpath);
406 file_util_rename(NULL, list, vf->listview);
409 void vflist_pop_menu_thumbs_cb(GtkWidget *UNUSED(widget), gpointer data)
411 ViewFile *vf = (ViewFile *)data;
413 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
416 layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
420 vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled);
424 void vflist_star_rating_set(ViewFile *vf, gboolean enable)
426 GList *columns, *work;
428 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
433 GtkTreeViewColumn *column = (GtkTreeViewColumn *)work->data;
434 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
437 if (vflist_is_multiline(vf))
439 if (col_idx == FILE_COLUMN_FORMATTED_WITH_STARS)
441 gtk_tree_view_column_set_visible(column, enable);
443 if (col_idx == FILE_COLUMN_FORMATTED)
445 gtk_tree_view_column_set_visible(column, !enable);
450 if (col_idx == FILE_COLUMN_STAR_RATING)
452 gtk_tree_view_column_set_visible(column, enable);
456 g_list_free(columns);
459 void vflist_pop_menu_show_star_rating_cb(GtkWidget *UNUSED(widget), gpointer data)
461 ViewFile *vf = (ViewFile *)data;
463 options->show_star_rating = !options->show_star_rating;
465 vflist_populate_view(vf, TRUE);
467 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
468 vflist_star_rating_set(vf, options->show_star_rating);
471 void vflist_pop_menu_refresh_cb(GtkWidget *UNUSED(widget), gpointer data)
473 ViewFile *vf = (ViewFile *)data;
475 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
477 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
480 void vflist_popup_destroy_cb(GtkWidget *UNUSED(widget), gpointer data)
482 ViewFile *vf = (ViewFile *)data;
483 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
484 VFLIST(vf)->click_fd = NULL;
490 *-----------------------------------------------------------------------------
492 *-----------------------------------------------------------------------------
495 static gboolean vflist_row_rename_cb(TreeEditData *UNUSED(td), const gchar *old_name, const gchar *new_name, gpointer data)
497 ViewFile *vf = (ViewFile *)data;
500 if (!new_name || !new_name[0]) return FALSE;
502 new_path = g_build_filename(vf->dir_fd->path, new_name, NULL);
504 if (strchr(new_name, G_DIR_SEPARATOR) != NULL)
506 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new_name);
507 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
512 gchar *old_path = g_build_filename(vf->dir_fd->path, old_name, NULL);
513 FileData *fd = file_data_new_group(old_path); /* get the fd from cache */
514 file_util_rename_simple(fd, new_path, vf->listview);
524 gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
526 ViewFile *vf = (ViewFile *)data;
529 if (event->keyval != GDK_KEY_Menu) return FALSE;
531 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
537 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
538 gtk_tree_model_get_iter(store, &iter, tpath);
539 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
540 gtk_tree_path_free(tpath);
544 VFLIST(vf)->click_fd = NULL;
547 vf->popup = vf_pop_menu(vf);
548 gtk_menu_popup_at_widget(GTK_MENU(vf->popup), widget, GDK_GRAVITY_EAST, GDK_GRAVITY_CENTER, NULL);
553 gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
555 ViewFile *vf = (ViewFile *)data;
559 GtkTreeViewColumn *column;
561 vf->clicked_mark = 0;
563 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
564 &tpath, &column, NULL, NULL))
567 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
569 if (bevent->button == MOUSE_BUTTON_LEFT &&
570 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
573 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
574 vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
576 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
578 gtk_tree_model_get_iter(store, &iter, tpath);
579 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
580 gtk_tree_path_free(tpath);
583 VFLIST(vf)->click_fd = fd;
585 if (bevent->button == MOUSE_BUTTON_RIGHT)
587 vf->popup = vf_pop_menu(vf);
588 gtk_menu_popup_at_pointer(GTK_MENU(vf->popup), NULL);
592 if (!fd) return FALSE;
594 if (bevent->button == MOUSE_BUTTON_MIDDLE)
596 if (!vflist_row_is_selected(vf, fd))
598 vflist_color_set(vf, fd, TRUE);
604 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
605 !(bevent->state & GDK_SHIFT_MASK ) &&
606 !(bevent->state & GDK_CONTROL_MASK ) &&
607 vflist_row_is_selected(vf, fd))
609 GtkTreeSelection *selection;
611 gtk_widget_grab_focus(widget);
614 /* returning FALSE and further processing of the event is needed for
615 correct operation of the expander, to show the sidecar files.
616 It however resets the selection of multiple files. With this condition
617 it should work for both cases */
618 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
619 return (gtk_tree_selection_count_selected_rows(selection) > 1);
622 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
624 if (VFLIST(vf)->click_fd->format_class == FORMAT_CLASS_COLLECTION)
626 collection_window_new(VFLIST(vf)->click_fd->path);
630 if (vf->layout) layout_image_full_screen_start(vf->layout);
637 gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
639 ViewFile *vf = (ViewFile *)data;
644 if (defined_mouse_buttons(widget, bevent, vf->layout))
649 if (bevent->button == MOUSE_BUTTON_MIDDLE)
651 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
654 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
659 if ((bevent->x != 0 || bevent->y != 0) &&
660 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
661 &tpath, NULL, NULL, NULL))
665 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
666 gtk_tree_model_get_iter(store, &iter, tpath);
667 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
668 gtk_tree_path_free(tpath);
671 if (bevent->button == MOUSE_BUTTON_MIDDLE)
673 if (fd && VFLIST(vf)->click_fd == fd)
675 GtkTreeSelection *selection;
677 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
678 if (vflist_row_is_selected(vf, fd))
680 gtk_tree_selection_unselect_iter(selection, &iter);
684 gtk_tree_selection_select_iter(selection, &iter);
690 if (fd && VFLIST(vf)->click_fd == fd &&
691 !(bevent->state & GDK_SHIFT_MASK ) &&
692 !(bevent->state & GDK_CONTROL_MASK ) &&
693 vflist_row_is_selected(vf, fd))
695 GtkTreeSelection *selection;
697 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
698 gtk_tree_selection_unselect_all(selection);
699 gtk_tree_selection_select_iter(selection, &iter);
700 vflist_move_cursor(vf, &iter);
706 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
708 FileData *read_ahead_fd = NULL;
714 cur_fd = layout_image_get_fd(vf->layout);
715 if (sel_fd == cur_fd) return; /* no change */
717 row = g_list_index(vf->list, sel_fd);
718 /** @FIXME sidecar data */
720 if (sel_fd && options->image.enable_read_ahead && row >= 0)
722 if (row > g_list_index(vf->list, cur_fd) &&
723 (guint) (row + 1) < vf_count(vf, NULL))
725 read_ahead_fd = vf_index_get_data(vf, row + 1);
729 read_ahead_fd = vf_index_get_data(vf, row - 1);
733 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
736 static gboolean vflist_select_idle_cb(gpointer data)
738 ViewFile *vf = (ViewFile *)data;
742 VFLIST(vf)->select_idle_id = 0;
748 if (VFLIST(vf)->select_fd)
750 vflist_select_image(vf, VFLIST(vf)->select_fd);
751 VFLIST(vf)->select_fd = NULL;
754 VFLIST(vf)->select_idle_id = 0;
758 static void vflist_select_idle_cancel(ViewFile *vf)
760 if (VFLIST(vf)->select_idle_id)
762 g_source_remove(VFLIST(vf)->select_idle_id);
763 VFLIST(vf)->select_idle_id = 0;
767 static gboolean vflist_select_cb(GtkTreeSelection *UNUSED(selection), GtkTreeModel *store, GtkTreePath *tpath, gboolean path_currently_selected, gpointer data)
769 ViewFile *vf = (ViewFile *)data;
771 GtkTreePath *cursor_path;
773 VFLIST(vf)->select_fd = NULL;
775 if (!path_currently_selected && gtk_tree_model_get_iter(store, &iter, tpath))
777 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &cursor_path, NULL);
780 gtk_tree_model_get_iter(store, &iter, cursor_path);
781 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
782 gtk_tree_path_free(cursor_path);
787 !VFLIST(vf)->select_idle_id)
789 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
795 static void vflist_expand_cb(GtkTreeView *UNUSED(tree_view), GtkTreeIter *iter, GtkTreePath *UNUSED(path), gpointer data)
797 ViewFile *vf = (ViewFile *)data;
798 vflist_set_expanded(vf, iter, TRUE);
801 static void vflist_collapse_cb(GtkTreeView *UNUSED(tree_view), GtkTreeIter *iter, GtkTreePath *UNUSED(path), gpointer data)
803 ViewFile *vf = (ViewFile *)data;
804 vflist_set_expanded(vf, iter, FALSE);
808 *-----------------------------------------------------------------------------
810 *-----------------------------------------------------------------------------
814 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)
816 gboolean multiline = vflist_is_multiline(vf);
823 text = g_strdup_printf("%s %s\n%s\n%s\n%s", name, expanded ? "" : sidecars, size, time, star_rating);
827 text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time);
832 text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars);
837 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded)
845 gchar *formatted_with_stars;
847 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
849 gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
850 FILE_COLUMN_NAME, &name,
851 FILE_COLUMN_SIDECARS, &sidecars,
852 FILE_COLUMN_SIZE, &size,
853 FILE_COLUMN_DATE, &time,
854 FILE_COLUMN_STAR_RATING, &star_rating,
857 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded, FALSE, NULL);
858 formatted_with_stars = vflist_get_formatted(vf, name, sidecars, size, time, expanded, TRUE, star_rating);
860 gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED, formatted,
861 FILE_COLUMN_EXPANDED, expanded,
863 gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED_WITH_STARS, formatted_with_stars,
864 FILE_COLUMN_EXPANDED, expanded,
871 g_free(formatted_with_stars);
874 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
877 gchar *sidecars = NULL;
879 const gchar *time = text_from_time(fd->date);
880 const gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
881 const gchar *disabled_grouping;
883 gchar *formatted_with_stars;
884 gboolean expanded = FALSE;
887 if (options->show_star_rating && fd->rating != STAR_RATING_NOT_READ)
889 star_rating = convert_rating_to_stars(fd->rating);
896 if (fd->sidecar_files) /* expanded has no effect on files without sidecars */
898 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_EXPANDED, &expanded, -1);
901 sidecars = file_data_sc_list_to_string(fd);
903 disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
904 name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
905 size = text_from_size(fd->size);
907 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded, FALSE, NULL);
908 formatted_with_stars = vflist_get_formatted(vf, name, sidecars, size, time, expanded, TRUE, star_rating);
910 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
911 FILE_COLUMN_VERSION, fd->version,
912 FILE_COLUMN_THUMB, fd->thumb_pixbuf,
913 FILE_COLUMN_FORMATTED, formatted,
914 FILE_COLUMN_FORMATTED_WITH_STARS, formatted_with_stars,
915 FILE_COLUMN_SIDECARS, sidecars,
916 FILE_COLUMN_NAME, name,
917 FILE_COLUMN_STAR_RATING, star_rating,
918 FILE_COLUMN_SIZE, size,
919 FILE_COLUMN_DATE, time,
920 #define STORE_SET_IS_SLOW 1
921 #if STORE_SET_IS_SLOW
922 /* this is 3x faster on a directory with 20000 files */
923 FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
924 FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
925 FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
926 FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
927 FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
928 FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
929 FILE_COLUMN_MARKS + 6, file_data_get_mark(fd, 6),
930 FILE_COLUMN_MARKS + 7, file_data_get_mark(fd, 7),
931 FILE_COLUMN_MARKS + 8, file_data_get_mark(fd, 8),
932 FILE_COLUMN_MARKS + 9, file_data_get_mark(fd, 9),
933 #if FILEDATA_MARKS_SIZE != 10
934 #error this needs to be updated
937 FILE_COLUMN_COLOR, FALSE, -1);
939 #if !STORE_SET_IS_SLOW
942 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
943 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
952 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected, gboolean force)
957 gint num_ordered = 0;
958 gint num_prepended = 0;
960 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
966 FileData *fd = (FileData *)work->data;
967 gboolean done = FALSE;
971 FileData *old_fd = NULL;
972 gint old_version = 0;
976 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
977 FILE_COLUMN_POINTER, &old_fd,
978 FILE_COLUMN_VERSION, &old_version,
988 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
990 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
992 if (match == 0) g_warning("multiple fd for the same path");
1003 GtkTreeIter new_iter;
1008 gtk_tree_store_insert_before(store, &new_iter, parent_iter, &iter);
1013 here should be used gtk_tree_store_append, but this function seems to be O(n)
1014 and it seems to be much faster to add new entries to the beginning and reorder later
1017 gtk_tree_store_prepend(store, &new_iter, parent_iter);
1020 vflist_setup_iter(vf, store, &new_iter, file_data_ref(fd));
1021 vflist_setup_iter_recursive(vf, store, &new_iter, fd->sidecar_files, selected, force);
1023 if (g_list_find(selected, fd))
1025 /* renamed files - the same fd appears at different position - select it again*/
1026 GtkTreeSelection *selection;
1027 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1028 gtk_tree_selection_select_iter(selection, &new_iter);
1035 file_data_unref(old_fd);
1036 valid = gtk_tree_store_remove(store, &iter);
1041 if (fd->version != old_version || force)
1043 vflist_setup_iter(vf, store, &iter, fd);
1044 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected, force);
1047 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1058 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1059 file_data_unref(old_fd);
1061 valid = gtk_tree_store_remove(store, &iter);
1064 /* move the prepended entries to the correct position */
1068 gint num_total = num_prepended + num_ordered;
1069 gint *new_order = static_cast<gint *>(g_malloc(num_total * sizeof(gint)));
1071 for (i = 0; i < num_total; i++)
1073 if (i < num_ordered)
1074 new_order[i] = num_prepended + i;
1076 new_order[i] = num_total - 1 - i;
1078 gtk_tree_store_reorder(store, parent_iter, new_order);
1084 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
1087 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
1089 GtkTreeStore *store;
1092 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1093 if (!vf->list) return;
1099 FileData *fd = (FileData *)work->data;
1100 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1105 vf->sort_method = type;
1106 vf->sort_ascend = ascend;
1108 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1110 new_order = static_cast<gint *>(g_malloc(i * sizeof(gint)));
1116 FileData *fd = (FileData *)work->data;
1117 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1122 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1123 gtk_tree_store_reorder(store, NULL, new_order);
1126 g_hash_table_destroy(fd_idx_hash);
1130 *-----------------------------------------------------------------------------
1132 *-----------------------------------------------------------------------------
1136 void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1141 FileData *fd = (FileData *)work->data;
1144 if (fd->thumb_pixbuf) (*done)++;
1146 if (fd->sidecar_files)
1148 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1154 void vflist_read_metadata_progress_count(GList *list, gint *count, gint *done)
1159 FileData *fd = (FileData *)work->data;
1162 if (fd->metadata_in_idle_loaded) (*done)++;
1164 if (fd->sidecar_files)
1166 vflist_read_metadata_progress_count(fd->sidecar_files, count, done);
1172 void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
1174 GtkTreeStore *store;
1177 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1179 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1180 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1183 FileData *vflist_thumb_next_fd(ViewFile *vf)
1186 FileData *fd = NULL;
1188 /* first check the visible files */
1190 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1192 GtkTreeModel *store;
1194 gboolean valid = TRUE;
1196 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1197 gtk_tree_model_get_iter(store, &iter, tpath);
1198 gtk_tree_path_free(tpath);
1201 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1205 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1207 if (!nfd->thumb_pixbuf) fd = nfd;
1209 valid = gtk_tree_model_iter_next(store, &iter);
1213 /* then find first undone */
1217 GList *work = vf->list;
1220 FileData *fd_p = (FileData *)work->data;
1221 if (!fd_p->thumb_pixbuf)
1225 GList *work2 = fd_p->sidecar_files;
1227 while (work2 && !fd)
1229 fd_p = static_cast<FileData *>(work2->data);
1230 if (!fd_p->thumb_pixbuf) fd = fd_p;
1231 work2 = work2->next;
1241 void vflist_set_star_fd(ViewFile *vf, FileData *fd)
1243 GtkTreeStore *store;
1250 gchar *formatted_with_stars;
1253 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1255 star_rating = metadata_read_rating_stars(fd);
1257 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1258 gtk_tree_store_set(store, &iter, FILE_COLUMN_STAR_RATING, star_rating, -1);
1260 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1261 FILE_COLUMN_NAME, &name,
1262 FILE_COLUMN_SIDECARS, &sidecars,
1263 FILE_COLUMN_SIZE, &size,
1264 FILE_COLUMN_DATE, &time,
1265 FILE_COLUMN_EXPANDED, &expanded,
1268 formatted_with_stars = vflist_get_formatted(vf, name, sidecars, size, time, expanded, TRUE, star_rating);
1270 gtk_tree_store_set(store, &iter, FILE_COLUMN_FORMATTED_WITH_STARS, formatted_with_stars,
1271 FILE_COLUMN_EXPANDED, expanded,
1274 g_free(star_rating);
1275 g_free(formatted_with_stars);
1278 FileData *vflist_star_next_fd(ViewFile *vf)
1281 FileData *fd = NULL;
1282 FileData *nfd = NULL;
1284 /* first check the visible files */
1286 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1288 GtkTreeModel *store;
1290 gboolean valid = TRUE;
1292 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1293 gtk_tree_model_get_iter(store, &iter, tpath);
1294 gtk_tree_path_free(tpath);
1297 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1299 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1301 if (nfd && nfd->rating == STAR_RATING_NOT_READ)
1306 valid = gtk_tree_model_iter_next(store, &iter);
1311 vf->stars_filedata = fd;
1313 if (vf->stars_id == 0)
1315 vf->stars_id = g_idle_add_full(G_PRIORITY_LOW, vf_stars_cb, vf, NULL);
1320 /* then find first undone */
1324 GList *work = vf->list;
1328 FileData *fd_p = (FileData *)work->data;
1330 if (fd_p && fd_p->rating == STAR_RATING_NOT_READ)
1344 vf->stars_filedata = fd;
1346 if (vf->stars_id == 0)
1348 vf->stars_id = g_idle_add_full(G_PRIORITY_LOW, vf_stars_cb, vf, NULL);
1357 *-----------------------------------------------------------------------------
1359 *-----------------------------------------------------------------------------
1362 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1365 GList *work, *work2;
1370 FileData *list_fd = (FileData *)work->data;
1371 if (list_fd == fd) return p;
1373 work2 = list_fd->sidecar_files;
1376 /** @FIXME return the same index also for sidecars
1377 it is sufficient for next/prev navigation but it should be rewritten
1378 without using indexes at all
1380 FileData *sidecar_fd = static_cast<FileData *>(work2->data);
1381 if (sidecar_fd == fd) return p;
1382 work2 = work2->next;
1393 *-----------------------------------------------------------------------------
1395 *-----------------------------------------------------------------------------
1398 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1400 GtkTreeModel *store;
1401 GtkTreeSelection *selection;
1404 gboolean found = FALSE;
1406 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1407 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1409 while (!found && work)
1411 GtkTreePath *tpath = (GtkTreePath *)work->data;
1415 gtk_tree_model_get_iter(store, &iter, tpath);
1416 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1417 if (fd_n == fd) found = TRUE;
1420 g_list_foreach(slist, (GFunc)tree_path_free_wrapper, NULL);
1426 //gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1430 //fd = vf_index_get_data(vf, row);
1431 //return vflist_row_is_selected(vf, fd);
1434 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1436 GtkTreeModel *store;
1437 GtkTreeSelection *selection;
1441 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1442 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1452 GtkTreePath *tpath = (GtkTreePath *)work->data;
1456 gtk_tree_model_get_iter(store, &iter, tpath);
1457 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1466 count = g_list_length(slist);
1467 g_list_foreach(slist, (GFunc)tree_path_free_wrapper, NULL);
1473 GList *vflist_selection_get_list(ViewFile *vf)
1475 GtkTreeModel *store;
1476 GtkTreeSelection *selection;
1481 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1482 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1486 GtkTreePath *tpath = (GtkTreePath *)work->data;
1490 gtk_tree_model_get_iter(store, &iter, tpath);
1491 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1493 list = g_list_prepend(list, file_data_ref(fd));
1495 if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
1497 /* unexpanded - add whole group */
1498 GList *work2 = fd->sidecar_files;
1501 FileData *sfd = static_cast<FileData *>(work2->data);
1502 list = g_list_prepend(list, file_data_ref(sfd));
1503 work2 = work2->next;
1509 g_list_foreach(slist, (GFunc)tree_path_free_wrapper, NULL);
1512 return g_list_reverse(list);
1515 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1517 GtkTreeModel *store;
1518 GtkTreeSelection *selection;
1523 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1524 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1528 GtkTreePath *tpath = (GtkTreePath *)work->data;
1532 gtk_tree_model_get_iter(store, &iter, tpath);
1533 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1535 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1539 g_list_foreach(slist, (GFunc)tree_path_free_wrapper, NULL);
1542 return g_list_reverse(list);
1545 void vflist_select_all(ViewFile *vf)
1547 GtkTreeSelection *selection;
1549 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1550 gtk_tree_selection_select_all(selection);
1552 VFLIST(vf)->select_fd = NULL;
1555 void vflist_select_none(ViewFile *vf)
1557 GtkTreeSelection *selection;
1559 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1560 gtk_tree_selection_unselect_all(selection);
1563 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1568 tpath = gtk_tree_model_get_path(store, iter);
1569 result = gtk_tree_path_prev(tpath);
1571 gtk_tree_model_get_iter(store, iter, tpath);
1573 gtk_tree_path_free(tpath);
1578 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1580 if (!gtk_tree_model_get_iter_first(store, iter))
1585 GtkTreeIter next = *iter;
1587 if (gtk_tree_model_iter_next(store, &next))
1596 void vflist_select_invert(ViewFile *vf)
1599 GtkTreeSelection *selection;
1600 GtkTreeModel *store;
1603 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1604 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1606 /* Backward iteration prevents scrolling to the end of the list,
1607 * it scrolls to the first selected row instead. */
1608 valid = tree_model_get_iter_last(store, &iter);
1612 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1615 gtk_tree_selection_unselect_iter(selection, &iter);
1617 gtk_tree_selection_select_iter(selection, &iter);
1619 valid = tree_model_iter_prev(store, &iter);
1623 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1627 if (vflist_find_row(vf, fd, &iter) < 0) return;
1629 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1631 if (!vflist_row_is_selected(vf, fd))
1633 GtkTreeSelection *selection;
1634 GtkTreeModel *store;
1637 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1638 gtk_tree_selection_unselect_all(selection);
1639 gtk_tree_selection_select_iter(selection, &iter);
1640 vflist_move_cursor(vf, &iter);
1642 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1643 tpath = gtk_tree_model_get_path(store, &iter);
1644 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1645 gtk_tree_path_free(tpath);
1649 void vflist_select_list(ViewFile *vf, GList *list)
1660 fd = static_cast<FileData *>(work->data);
1662 if (vflist_find_row(vf, fd, &iter) < 0) return;
1663 if (!vflist_row_is_selected(vf, fd))
1665 GtkTreeSelection *selection;
1667 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1668 gtk_tree_selection_select_iter(selection, &iter);
1674 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1677 FileData *fd = NULL;
1679 if (sel_fd->parent) sel_fd = sel_fd->parent;
1685 fd = static_cast<FileData *>(work->data);
1688 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1690 if (match >= 0) break;
1693 if (fd) vflist_select_by_fd(vf, fd);
1697 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1699 GtkTreeModel *store;
1701 GtkTreeSelection *selection;
1705 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1707 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1708 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1710 valid = gtk_tree_model_get_iter_first(store, &iter);
1714 gboolean mark_val, selected;
1715 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1717 mark_val = file_data_get_mark(fd, n);
1718 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1722 case MTS_MODE_SET: selected = mark_val;
1724 case MTS_MODE_OR: selected = mark_val || selected;
1726 case MTS_MODE_AND: selected = mark_val && selected;
1728 case MTS_MODE_MINUS: selected = !mark_val && selected;
1733 gtk_tree_selection_select_iter(selection, &iter);
1735 gtk_tree_selection_unselect_iter(selection, &iter);
1737 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1741 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1743 GtkTreeModel *store;
1744 GtkTreeSelection *selection;
1749 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1751 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1752 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1756 GtkTreePath *tpath = (GtkTreePath *)work->data;
1760 gtk_tree_model_get_iter(store, &iter, tpath);
1761 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1763 /* the change has a very limited range and the standard notification would trigger
1764 complete re-read of the directory - try to do only minimal update instead */
1765 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1769 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1771 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1773 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1777 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1779 vf_refresh_idle(vf);
1783 /* mark functions can have various side effects - update all columns to be sure */
1784 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1785 /* mark functions can change sidecars too */
1786 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1790 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1794 g_list_foreach(slist, (GFunc)tree_path_free_wrapper, NULL);
1799 *-----------------------------------------------------------------------------
1801 *-----------------------------------------------------------------------------
1804 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
1806 GtkTreeViewColumn *column;
1807 GtkCellRenderer *cell;
1810 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1811 if (!column) return;
1813 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1815 list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1817 cell = static_cast<GtkCellRenderer *>(list->data);
1820 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1821 gtk_tree_view_column_set_visible(column, thumb);
1823 if (options->show_star_rating)
1825 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED_WITH_STARS);
1826 if (!column) return;
1827 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1828 gtk_tree_view_column_set_visible(column, TRUE);
1830 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1831 if (!column) return;
1832 gtk_tree_view_column_set_visible(column, FALSE);
1836 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1837 if (!column) return;
1838 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1839 gtk_tree_view_column_set_visible(column, TRUE);
1841 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED_WITH_STARS);
1842 if (!column) return;
1843 gtk_tree_view_column_set_visible(column, FALSE);
1846 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_STAR_RATING);
1847 if (!column) return;
1848 gtk_tree_view_column_set_visible(column, !multiline && options->show_star_rating);
1850 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1851 if (!column) return;
1852 gtk_tree_view_column_set_visible(column, !multiline);
1854 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1855 if (!column) return;
1856 gtk_tree_view_column_set_visible(column, !multiline);
1859 static gboolean vflist_is_multiline(ViewFile *vf)
1861 return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
1865 static void vflist_populate_view(ViewFile *vf, gboolean force)
1867 GtkTreeStore *store;
1870 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1877 vflist_store_clear(vf, FALSE);
1882 vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
1884 selected = vflist_selection_get_list(vf);
1886 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force);
1888 if (selected && vflist_selection_count(vf, NULL) == 0)
1890 /* all selected files disappeared */
1891 vflist_select_closest(vf, static_cast<FileData *>(selected->data));
1894 filelist_free(selected);
1897 vf_thumb_update(vf);
1901 gboolean vflist_refresh(ViewFile *vf)
1904 gboolean ret = TRUE;
1906 old_list = vf->list;
1909 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1912 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1914 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1916 if (vf->marks_enabled)
1918 // When marks are enabled, lock FileDatas so that we don't end up re-parsing XML
1919 // each time a mark is changed.
1920 file_data_lock_list(vf->list);
1924 /** @FIXME only do this when needed (aka when we just switched from */
1925 /** @FIXME marks-enabled to marks-disabled) */
1926 file_data_unlock_list(vf->list);
1929 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1930 vf->list = g_list_first(vf->list);
1931 vf->list = file_data_filter_file_filter_list(vf->list, vf_file_filter_get_filter(vf));
1933 vf->list = g_list_first(vf->list);
1934 vf->list = file_data_filter_class_list(vf->list, vf_class_get_filter(vf));
1936 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1938 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1939 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1942 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1944 vflist_populate_view(vf, FALSE);
1946 DEBUG_1("%s vflist_refresh: free filelist", get_exec_time());
1948 filelist_free(old_list);
1949 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1956 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1958 #define CELL_HEIGHT_OVERRIDE 512
1960 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1964 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1965 if (spec && G_IS_PARAM_SPEC_INT(spec))
1967 GParamSpecInt *spec_int;
1969 spec_int = G_PARAM_SPEC_INT(spec);
1970 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1974 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1976 static GdkColor color;
1977 static GtkWidget *done = NULL;
1983 style = gtk_widget_get_style(widget);
1984 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1985 shift_color(&color, -1, 0);
1992 static void vflist_listview_color_cb(GtkTreeViewColumn *UNUSED(tree_column), GtkCellRenderer *cell,
1993 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1995 ViewFile *vf = (ViewFile *)data;
1998 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1999 g_object_set(G_OBJECT(cell),
2000 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
2001 "cell-background-set", set, NULL);
2004 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
2006 GtkTreeViewColumn *column;
2007 GtkCellRenderer *renderer;
2009 column = gtk_tree_view_column_new();
2010 gtk_tree_view_column_set_title(column, title);
2011 gtk_tree_view_column_set_min_width(column, 4);
2015 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
2016 renderer = gtk_cell_renderer_text_new();
2019 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
2021 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2022 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
2024 gtk_tree_view_column_set_expand(column, TRUE);
2028 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2029 renderer = gtk_cell_renderer_pixbuf_new();
2030 cell_renderer_height_override(renderer);
2031 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2032 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
2035 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
2036 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
2037 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
2039 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
2042 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
2044 ViewFile *vf = (ViewFile *)data;
2045 GtkTreeStore *store;
2046 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
2052 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2053 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
2056 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
2058 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
2060 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
2063 /* the change has a very limited range and the standard notification would trigger
2064 complete re-read of the directory - try to do only minimal update instead */
2065 file_data_unregister_notify_func(vf_notify_cb, vf);
2066 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
2067 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
2069 vf_refresh_idle(vf);
2073 /* mark functions can have various side effects - update all columns to be sure */
2074 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
2075 /* mark functions can change sidecars too */
2076 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
2078 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2080 gtk_tree_path_free(path);
2083 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
2085 GtkTreeViewColumn *column;
2086 GtkCellRenderer *renderer;
2088 renderer = gtk_cell_renderer_toggle_new();
2089 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
2091 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2092 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
2093 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
2095 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
2096 gtk_tree_view_column_set_fixed_width(column, 22);
2097 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
2100 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
2104 *-----------------------------------------------------------------------------
2106 *-----------------------------------------------------------------------------
2109 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
2112 if (!dir_fd) return FALSE;
2113 if (vf->dir_fd == dir_fd) return TRUE;
2115 file_data_unref(vf->dir_fd);
2116 vf->dir_fd = file_data_ref(dir_fd);
2118 /* force complete reload */
2119 vflist_store_clear(vf, TRUE);
2121 filelist_free(vf->list);
2124 ret = vf_refresh(vf);
2125 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
2129 void vflist_destroy_cb(GtkWidget *UNUSED(widget), gpointer data)
2131 ViewFile *vf = (ViewFile *)data;
2133 file_data_unregister_notify_func(vf_notify_cb, vf);
2135 vflist_select_idle_cancel(vf);
2136 vf_refresh_idle_cancel(vf);
2140 filelist_free(vf->list);
2143 ViewFile *vflist_new(ViewFile *vf, FileData *UNUSED(dir_fd))
2145 GtkTreeStore *store;
2146 GtkTreeSelection *selection;
2147 GType flist_types[FILE_COLUMN_COUNT];
2151 vf->info = g_new0(ViewFileInfoList, 1);
2153 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
2154 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
2155 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
2156 flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING;
2157 flist_types[FILE_COLUMN_FORMATTED_WITH_STARS] = G_TYPE_STRING;
2158 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
2159 flist_types[FILE_COLUMN_STAR_RATING] = G_TYPE_STRING;
2160 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
2161 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
2162 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
2163 flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN;
2164 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
2165 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
2166 flist_types[i] = G_TYPE_BOOLEAN;
2168 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
2170 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2171 g_object_unref(store);
2173 g_signal_connect(G_OBJECT(vf->listview), "row-expanded",
2174 G_CALLBACK(vflist_expand_cb), vf);
2176 g_signal_connect(G_OBJECT(vf->listview), "row-collapsed",
2177 G_CALLBACK(vflist_collapse_cb), vf);
2179 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2180 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
2181 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
2183 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2184 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2186 gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(vf->listview), -1);
2190 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2192 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2193 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
2197 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2198 g_assert(column == FILE_VIEW_COLUMN_THUMB);
2201 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE);
2202 g_assert(column == FILE_VIEW_COLUMN_FORMATTED);
2205 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED_WITH_STARS, _("NameStars"), FALSE, FALSE, TRUE);
2206 g_assert(column == FILE_VIEW_COLUMN_FORMATTED_WITH_STARS);
2209 vflist_listview_add_column(vf, FILE_COLUMN_STAR_RATING, _("Stars"), FALSE, FALSE, FALSE);
2210 g_assert(column == FILE_VIEW_COLUMN_STAR_RATING);
2213 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2214 g_assert(column == FILE_VIEW_COLUMN_SIZE);
2217 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2218 g_assert(column == FILE_VIEW_COLUMN_DATE);
2221 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2225 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2227 if (VFLIST(vf)->thumbs_enabled == enable) return;
2229 VFLIST(vf)->thumbs_enabled = enable;
2231 /* vflist_populate_view is better than vf_refresh:
2232 - no need to re-read the directory
2233 - force update because the formatted string has changed
2237 vflist_populate_view(vf, TRUE);
2238 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
2242 void vflist_marks_set(ViewFile *vf, gboolean enable)
2244 GList *columns, *work;
2246 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2251 GtkTreeViewColumn *column = (GtkTreeViewColumn *)work->data;
2252 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2255 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2256 gtk_tree_view_column_set_visible(column, enable);
2261 // Previously disabled, which means that vf->list is complete
2262 file_data_lock_list(vf->list);
2266 // Previously enabled, which means that vf->list is incomplete
2269 g_list_free(columns);
2272 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */