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"
30 #include "filecluster.h"
32 #include "layout_image.h"
37 #include "ui_fileops.h"
39 #include "ui_tree_edit.h"
40 #include "uri_utils.h"
41 #include "view_file.h"
43 #include <gdk/gdkkeysyms.h> /* for keyboard values */
45 /* Index to tree store */
47 FILE_COLUMN_POINTER = 0,
50 FILE_COLUMN_FORMATTED,
58 FILE_COLUMN_MARKS_LAST = FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
63 /* Index to tree view */
65 FILE_VIEW_COLUMN_MARKS = 0,
66 FILE_VIEW_COLUMN_MARKS_LAST = FILE_VIEW_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
67 FILE_VIEW_COLUMN_THUMB,
68 FILE_VIEW_COLUMN_FORMATTED,
69 FILE_VIEW_COLUMN_SIZE,
70 FILE_VIEW_COLUMN_DATE,
71 FILE_VIEW_COLUMN_COUNT
76 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd);
77 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data);
78 static void vflist_populate_view(ViewFile *vf, gboolean force);
79 static gboolean vflist_is_multiline(ViewFile *vf);
80 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded);
84 *-----------------------------------------------------------------------------
86 *-----------------------------------------------------------------------------
93 } ViewFileFindRowData;
95 static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
97 ViewFileFindRowData *find = data;
99 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
110 static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter)
113 ViewFileFindRowData data = {fd, iter, FALSE, 0};
115 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
116 gtk_tree_model_foreach(store, vflist_find_row_cb, &data);
126 static FileData *vflist_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter)
129 GtkTreeViewColumn *column;
131 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), x, y,
132 &tpath, &column, NULL, NULL))
138 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
139 gtk_tree_model_get_iter(store, &row, tpath);
140 gtk_tree_path_free(tpath);
141 gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &fd, -1);
149 static gboolean vflist_store_clear_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
152 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
154 /* it seems that gtk_tree_store_clear may call some callbacks
155 that use the column. Set the pointer to NULL to be safe. */
156 gtk_tree_store_set(GTK_TREE_STORE(model), iter, FILE_COLUMN_POINTER, NULL, -1);
161 static void vflist_store_clear(ViewFile *vf, gboolean unlock_files)
166 if (unlock_files && vf->marks_enabled)
168 // unlock locked files in this directory
169 filelist_read(vf->dir_fd, &files, NULL);
172 FileData *fd = files->data;
174 file_data_unlock(fd);
175 file_data_unref(fd); // undo the ref that got added in filelist_read
180 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
181 gtk_tree_model_foreach(store, vflist_store_clear_cb, NULL);
182 gtk_tree_store_clear(GTK_TREE_STORE(store));
185 void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set)
190 if (vflist_find_row(vf, fd, &iter) < 0) return;
191 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
192 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
195 static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
200 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
202 tpath = gtk_tree_model_get_path(store, iter);
203 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
204 gtk_tree_path_free(tpath);
209 *-----------------------------------------------------------------------------
211 *-----------------------------------------------------------------------------
214 static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
215 GtkSelectionData *selection_data, guint info,
216 guint time, gpointer data)
221 if (!VFLIST(vf)->click_fd) return;
223 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
225 list = vf_selection_get_list(vf);
229 list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
233 uri_selection_data_set_uris_from_filelist(selection_data, list);
237 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
241 vflist_color_set(vf, VFLIST(vf)->click_fd, TRUE);
243 if (VFLIST(vf)->thumbs_enabled &&
244 VFLIST(vf)->click_fd && VFLIST(vf)->click_fd->thumb_pixbuf)
248 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
249 items = vf_selection_count(vf, NULL);
253 dnd_set_drag_icon(widget, context, VFLIST(vf)->click_fd->thumb_pixbuf, items);
257 static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
261 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
263 if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE)
269 static void vflist_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context,
270 int x, int y, GtkSelectionData *selection,
271 guint info, guint time, gpointer data)
275 if (info == TARGET_TEXT_PLAIN) {
276 FileData *fd = vflist_find_data_by_coord(vf, x, y, NULL);
279 /* Add keywords to file */
280 gchar *str = (gchar *) gtk_selection_data_get_text(selection);
281 GList *kw_list = string_to_keywords_list(str);
283 metadata_append_list(fd, KEYWORD_KEY, kw_list);
284 string_list_free(kw_list);
290 void vflist_dnd_init(ViewFile *vf)
292 gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
293 dnd_file_drag_types, dnd_file_drag_types_count,
294 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
295 gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL,
296 dnd_file_drag_types, dnd_file_drag_types_count,
297 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
299 g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
300 G_CALLBACK(vflist_dnd_get), vf);
301 g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
302 G_CALLBACK(vflist_dnd_begin), vf);
303 g_signal_connect(G_OBJECT(vf->listview), "drag_end",
304 G_CALLBACK(vflist_dnd_end), vf);
305 g_signal_connect(G_OBJECT(vf->listview), "drag_data_received",
306 G_CALLBACK(vflist_drag_data_received), vf);
310 *-----------------------------------------------------------------------------
312 *-----------------------------------------------------------------------------
315 GList *vflist_selection_get_one(ViewFile *vf, FileData *fd)
317 GList *list = g_list_append(NULL, file_data_ref(fd));
319 if (fd->sidecar_files)
321 /* check if the row is expanded */
325 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
326 if (vflist_find_row(vf, fd, &iter) >= 0)
330 tpath = gtk_tree_model_get_path(store, &iter);
331 if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
333 /* unexpanded - add whole group */
334 GList *work = fd->sidecar_files;
337 FileData *sfd = work->data;
338 list = g_list_prepend(list, file_data_ref(sfd));
342 gtk_tree_path_free(tpath);
344 list = g_list_reverse(list);
350 GList *vflist_pop_menu_file_list(ViewFile *vf)
352 if (!VFLIST(vf)->click_fd) return NULL;
354 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
356 return vf_selection_get_list(vf);
358 return vflist_selection_get_one(vf, VFLIST(vf)->click_fd);
362 void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
366 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
370 list = vf_selection_get_list(vf);
371 view_window_new_from_list(list);
376 view_window_new(VFLIST(vf)->click_fd);
380 void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
385 list = vf_pop_menu_file_list(vf);
386 if (options->file_ops.enable_in_place_rename &&
387 list && !list->next && VFLIST(vf)->click_fd)
394 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
395 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
399 tpath = gtk_tree_model_get_path(store, &iter);
400 tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
401 FILE_VIEW_COLUMN_FORMATTED, VFLIST(vf)->click_fd->name,
402 vflist_row_rename_cb, vf);
403 gtk_tree_path_free(tpath);
408 file_util_rename(NULL, list, vf->listview);
411 void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
415 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
418 layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
422 vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled);
426 void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
430 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
432 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
435 void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
438 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
439 VFLIST(vf)->click_fd = NULL;
445 *-----------------------------------------------------------------------------
447 *-----------------------------------------------------------------------------
450 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
455 if (!new || !new[0]) return FALSE;
457 new_path = g_build_filename(vf->dir_fd->path, new, NULL);
459 if (strchr(new, G_DIR_SEPARATOR) != NULL)
461 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
462 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
467 gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL);
468 FileData *fd = file_data_new_group(old_path); /* get the fd from cache */
469 file_util_rename_simple(fd, new_path, vf->listview);
479 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
487 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return;
488 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
489 tpath = gtk_tree_model_get_path(store, &iter);
490 tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
491 gtk_tree_path_free(tpath);
493 popup_menu_position_clamp(menu, x, y, 0);
496 gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
502 // TODO(xsdg): these key combos should be handled by the standard, configurable mechanism.
504 if (event->keyval == GDK_KEY_Insert || event->keyval == GDK_KEY_F2)
506 // First off, get the selected FDs
507 GList *selected_fds = NULL;
509 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
510 if (event->keyval == GDK_KEY_Insert)
512 if (gtk_tree_selection_count_selected_rows(selection) < 2)
514 g_warning("Need at least two items selected to create a cluster.");
520 if (gtk_tree_selection_count_selected_rows(selection) < 1)
522 g_warning("Must have a node selected to flip show_children.");
527 // List of GtkTreePath
528 GList *selected_rows = gtk_tree_selection_get_selected_rows(selection, NULL);
529 GtkTreeModel *store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
531 for (GList *work = selected_rows; work; work = work->next)
534 GtkTreePath *select_path = work->data;
535 gtk_tree_model_get_iter(store, &iter, select_path);
536 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
537 selected_fds = g_list_prepend(selected_fds, file_data_ref(fd));
540 selected_fds = g_list_reverse(selected_fds);
541 g_list_free_full(selected_rows, (GDestroyNotify)gtk_tree_path_free);
544 if (event->keyval == GDK_KEY_Insert)
546 g_warning("Starting a cluster!");
547 FileCluster *fc = fileclusterlist_create_cluster(vf->cluster_list, selected_fds);
550 // TODO(xsdg): mark as in a cluster somehow?
554 else if (event->keyval == GDK_KEY_F2)
556 FileData *fd = selected_fds->data;
559 g_warning("Flipping show_children!");
560 FileCluster *fc = g_hash_table_lookup(vf->cluster_list->clusters, fd);
563 filecluster_toggle_show_children(fc);
569 return TRUE; // Handled event; stop propagating.
572 if (event->keyval != GDK_KEY_Menu) return FALSE;
574 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
580 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
581 gtk_tree_model_get_iter(store, &iter, tpath);
582 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
583 gtk_tree_path_free(tpath);
587 VFLIST(vf)->click_fd = NULL;
590 vf->popup = vf_pop_menu(vf);
591 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
596 gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
602 GtkTreeViewColumn *column;
604 vf->clicked_mark = 0;
606 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
607 &tpath, &column, NULL, NULL))
610 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
612 if (bevent->button == MOUSE_BUTTON_LEFT &&
613 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
616 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
617 vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
619 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
621 gtk_tree_model_get_iter(store, &iter, tpath);
622 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
623 gtk_tree_path_free(tpath);
626 VFLIST(vf)->click_fd = fd;
628 if (bevent->button == MOUSE_BUTTON_RIGHT)
630 vf->popup = vf_pop_menu(vf);
631 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
632 bevent->button, bevent->time);
636 if (!fd) return FALSE;
638 if (bevent->button == MOUSE_BUTTON_MIDDLE)
640 if (!vflist_row_is_selected(vf, fd))
642 vflist_color_set(vf, fd, TRUE);
648 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
649 !(bevent->state & GDK_SHIFT_MASK ) &&
650 !(bevent->state & GDK_CONTROL_MASK ) &&
651 vflist_row_is_selected(vf, fd))
653 GtkTreeSelection *selection;
655 gtk_widget_grab_focus(widget);
658 /* returning FALSE and further processing of the event is needed for
659 correct operation of the expander, to show the sidecar files.
660 It however resets the selection of multiple files. With this condition
661 it should work for both cases */
662 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
663 return (gtk_tree_selection_count_selected_rows(selection) > 1);
666 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
668 if (vf->layout) layout_image_full_screen_start(vf->layout);
674 gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
681 if (bevent->button == MOUSE_BUTTON_MIDDLE)
683 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
686 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
691 if ((bevent->x != 0 || bevent->y != 0) &&
692 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
693 &tpath, NULL, NULL, NULL))
697 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
698 gtk_tree_model_get_iter(store, &iter, tpath);
699 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
700 gtk_tree_path_free(tpath);
703 if (bevent->button == MOUSE_BUTTON_MIDDLE)
705 if (fd && VFLIST(vf)->click_fd == fd)
707 GtkTreeSelection *selection;
709 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
710 if (vflist_row_is_selected(vf, fd))
712 gtk_tree_selection_unselect_iter(selection, &iter);
716 gtk_tree_selection_select_iter(selection, &iter);
722 if (fd && VFLIST(vf)->click_fd == fd &&
723 !(bevent->state & GDK_SHIFT_MASK ) &&
724 !(bevent->state & GDK_CONTROL_MASK ) &&
725 vflist_row_is_selected(vf, fd))
727 GtkTreeSelection *selection;
729 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
730 gtk_tree_selection_unselect_all(selection);
731 gtk_tree_selection_select_iter(selection, &iter);
732 vflist_move_cursor(vf, &iter);
738 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
740 FileData *read_ahead_fd = NULL;
746 cur_fd = layout_image_get_fd(vf->layout);
747 if (sel_fd == cur_fd) return; /* no change */
749 row = g_list_index(vf->list, sel_fd);
750 // FIXME sidecar data
752 if (sel_fd && options->image.enable_read_ahead && row >= 0)
754 if (row > g_list_index(vf->list, cur_fd) &&
755 (guint) (row + 1) < vf_count(vf, NULL))
757 read_ahead_fd = vf_index_get_data(vf, row + 1);
761 read_ahead_fd = vf_index_get_data(vf, row - 1);
765 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
768 static gboolean vflist_select_idle_cb(gpointer data)
774 VFLIST(vf)->select_idle_id = 0;
780 if (VFLIST(vf)->select_fd)
782 vflist_select_image(vf, VFLIST(vf)->select_fd);
783 VFLIST(vf)->select_fd = NULL;
786 VFLIST(vf)->select_idle_id = 0;
790 static void vflist_select_idle_cancel(ViewFile *vf)
792 if (VFLIST(vf)->select_idle_id)
794 g_source_remove(VFLIST(vf)->select_idle_id);
795 VFLIST(vf)->select_idle_id = 0;
799 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
800 gboolean path_currently_selected, gpointer data)
805 if (!path_currently_selected &&
806 gtk_tree_model_get_iter(store, &iter, tpath))
808 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
812 VFLIST(vf)->select_fd = NULL;
816 !VFLIST(vf)->select_idle_id)
818 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
824 static void vflist_expand_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
827 vflist_set_expanded(vf, iter, TRUE);
830 static void vflist_collapse_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
833 vflist_set_expanded(vf, iter, FALSE);
837 *-----------------------------------------------------------------------------
839 *-----------------------------------------------------------------------------
843 static gchar* vflist_get_formatted(ViewFile *vf, const gchar *name, const gchar *sidecars, const gchar *size, const gchar *time, gboolean expanded)
845 gboolean multiline = vflist_is_multiline(vf);
850 text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time);
854 text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars);
859 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded)
868 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
870 gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
871 FILE_COLUMN_NAME, &name,
872 FILE_COLUMN_SIDECARS, &sidecars,
873 FILE_COLUMN_SIZE, &size,
874 FILE_COLUMN_DATE, &time,
876 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
878 gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED, formatted,
879 FILE_COLUMN_EXPANDED, expanded,
888 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
891 gchar *sidecars = NULL;
893 const gchar *time = text_from_time(fd->date);
894 gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
895 const gchar *disabled_grouping;
897 gboolean expanded = FALSE;
899 if (fd->sidecar_files) /* expanded has no effect on files without sidecars */
901 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_EXPANDED, &expanded, -1);
904 sidecars = file_data_sc_list_to_string(fd);
906 disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
907 name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
908 size = text_from_size(fd->size);
910 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded);
912 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
913 FILE_COLUMN_VERSION, fd->version,
914 FILE_COLUMN_THUMB, fd->thumb_pixbuf,
915 FILE_COLUMN_FORMATTED, formatted,
916 FILE_COLUMN_SIDECARS, sidecars,
917 FILE_COLUMN_NAME, name,
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 #if FILEDATA_MARKS_SIZE != 6
930 #error this needs to be updated
933 FILE_COLUMN_COLOR, FALSE, -1);
935 #if !STORE_SET_IS_SLOW
938 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
939 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
948 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected, gboolean force)
953 gint num_ordered = 0;
954 gint num_prepended = 0;
956 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
962 FileData *fd = work->data;
963 gboolean done = FALSE;
967 FileData *old_fd = NULL;
968 gint old_version = 0;
972 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
973 FILE_COLUMN_POINTER, &old_fd,
974 FILE_COLUMN_VERSION, &old_version,
984 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
986 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
988 if (match == 0) g_warning("multiple fd for the same path");
1004 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
1009 here should be used gtk_tree_store_append, but this function seems to be O(n)
1010 and it seems to be much faster to add new entries to the beginning and reorder later
1013 gtk_tree_store_prepend(store, &new, parent_iter);
1016 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
1017 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected, force);
1019 if (g_list_find(selected, fd))
1021 /* renamed files - the same fd appears at different position - select it again*/
1022 GtkTreeSelection *selection;
1023 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1024 gtk_tree_selection_select_iter(selection, &new);
1031 file_data_unref(old_fd);
1032 valid = gtk_tree_store_remove(store, &iter);
1037 if (fd->version != old_version || force)
1039 vflist_setup_iter(vf, store, &iter, fd);
1040 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected, force);
1043 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1054 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1055 file_data_unref(old_fd);
1057 valid = gtk_tree_store_remove(store, &iter);
1060 /* move the prepended entries to the correct position */
1064 gint num_total = num_prepended + num_ordered;
1065 gint *new_order = g_malloc(num_total * sizeof(gint));
1067 for (i = 0; i < num_total; i++)
1069 if (i < num_ordered)
1070 new_order[i] = num_prepended + i;
1072 new_order[i] = num_total - 1 - i;
1074 gtk_tree_store_reorder(store, parent_iter, new_order);
1080 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
1083 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
1085 GtkTreeStore *store;
1088 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1089 if (!vf->list) return;
1095 FileData *fd = work->data;
1096 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1101 vf->sort_method = type;
1102 vf->sort_ascend = ascend;
1104 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1106 new_order = g_malloc(i * sizeof(gint));
1112 FileData *fd = work->data;
1113 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1118 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1119 gtk_tree_store_reorder(store, NULL, new_order);
1122 g_hash_table_destroy(fd_idx_hash);
1126 *-----------------------------------------------------------------------------
1128 *-----------------------------------------------------------------------------
1132 void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1137 FileData *fd = work->data;
1140 if (fd->thumb_pixbuf) (*done)++;
1142 if (fd->sidecar_files)
1144 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1150 void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
1152 GtkTreeStore *store;
1155 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1157 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1158 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1161 FileData *vflist_thumb_next_fd(ViewFile *vf)
1164 FileData *fd = NULL;
1166 /* first check the visible files */
1168 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1170 GtkTreeModel *store;
1172 gboolean valid = TRUE;
1174 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1175 gtk_tree_model_get_iter(store, &iter, tpath);
1176 gtk_tree_path_free(tpath);
1179 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1183 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1185 if (!nfd->thumb_pixbuf) fd = nfd;
1187 valid = gtk_tree_model_iter_next(store, &iter);
1191 /* then find first undone */
1195 GList *work = vf->list;
1198 FileData *fd_p = work->data;
1199 if (!fd_p->thumb_pixbuf)
1203 GList *work2 = fd_p->sidecar_files;
1205 while (work2 && !fd)
1208 if (!fd_p->thumb_pixbuf) fd = fd_p;
1209 work2 = work2->next;
1220 *-----------------------------------------------------------------------------
1222 *-----------------------------------------------------------------------------
1225 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1228 GList *work, *work2;
1233 FileData *list_fd = work->data;
1234 if (list_fd == fd) return p;
1236 work2 = list_fd->sidecar_files;
1239 /* FIXME: return the same index also for sidecars
1240 it is sufficient for next/prev navigation but it should be rewritten
1241 without using indexes at all
1243 FileData *sidecar_fd = work2->data;
1244 if (sidecar_fd == fd) return p;
1245 work2 = work2->next;
1256 *-----------------------------------------------------------------------------
1258 *-----------------------------------------------------------------------------
1261 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1263 GtkTreeModel *store;
1264 GtkTreeSelection *selection;
1267 gboolean found = FALSE;
1269 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1270 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1272 while (!found && work)
1274 GtkTreePath *tpath = work->data;
1278 gtk_tree_model_get_iter(store, &iter, tpath);
1279 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1280 if (fd_n == fd) found = TRUE;
1283 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1289 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1293 fd = vf_index_get_data(vf, row);
1294 return vflist_row_is_selected(vf, fd);
1297 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1299 GtkTreeModel *store;
1300 GtkTreeSelection *selection;
1304 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1305 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1315 GtkTreePath *tpath = work->data;
1319 gtk_tree_model_get_iter(store, &iter, tpath);
1320 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1329 count = g_list_length(slist);
1330 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1336 GList *vflist_selection_get_list(ViewFile *vf)
1338 GtkTreeModel *store;
1339 GtkTreeSelection *selection;
1344 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1345 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1349 GtkTreePath *tpath = work->data;
1353 gtk_tree_model_get_iter(store, &iter, tpath);
1354 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1356 list = g_list_prepend(list, file_data_ref(fd));
1358 if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
1360 /* unexpanded - add whole group */
1361 GList *work2 = fd->sidecar_files;
1364 FileData *sfd = work2->data;
1365 list = g_list_prepend(list, file_data_ref(sfd));
1366 work2 = work2->next;
1372 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1375 return g_list_reverse(list);
1378 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1380 GtkTreeModel *store;
1381 GtkTreeSelection *selection;
1386 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1387 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1391 GtkTreePath *tpath = work->data;
1395 gtk_tree_model_get_iter(store, &iter, tpath);
1396 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1398 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1402 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1405 return g_list_reverse(list);
1408 void vflist_select_all(ViewFile *vf)
1410 GtkTreeSelection *selection;
1412 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1413 gtk_tree_selection_select_all(selection);
1415 VFLIST(vf)->select_fd = NULL;
1418 void vflist_select_none(ViewFile *vf)
1420 GtkTreeSelection *selection;
1422 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1423 gtk_tree_selection_unselect_all(selection);
1426 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1431 tpath = gtk_tree_model_get_path(store, iter);
1432 result = gtk_tree_path_prev(tpath);
1434 gtk_tree_model_get_iter(store, iter, tpath);
1436 gtk_tree_path_free(tpath);
1441 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1443 if (!gtk_tree_model_get_iter_first(store, iter))
1448 GtkTreeIter next = *iter;
1450 if (gtk_tree_model_iter_next(store, &next))
1459 void vflist_select_invert(ViewFile *vf)
1462 GtkTreeSelection *selection;
1463 GtkTreeModel *store;
1466 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1467 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1469 /* Backward iteration prevents scrolling to the end of the list,
1470 * it scrolls to the first selected row instead. */
1471 valid = tree_model_get_iter_last(store, &iter);
1475 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1478 gtk_tree_selection_unselect_iter(selection, &iter);
1480 gtk_tree_selection_select_iter(selection, &iter);
1482 valid = tree_model_iter_prev(store, &iter);
1486 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1490 if (vflist_find_row(vf, fd, &iter) < 0) return;
1492 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1494 if (!vflist_row_is_selected(vf, fd))
1496 GtkTreeSelection *selection;
1497 GtkTreeModel *store;
1500 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1501 gtk_tree_selection_unselect_all(selection);
1502 gtk_tree_selection_select_iter(selection, &iter);
1503 vflist_move_cursor(vf, &iter);
1505 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1506 tpath = gtk_tree_model_get_path(store, &iter);
1507 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1508 gtk_tree_path_free(tpath);
1512 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1515 FileData *fd = NULL;
1517 if (sel_fd->parent) sel_fd = sel_fd->parent;
1526 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1528 if (match >= 0) break;
1531 if (fd) vflist_select_by_fd(vf, fd);
1535 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1537 GtkTreeModel *store;
1539 GtkTreeSelection *selection;
1543 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1545 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1546 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1548 valid = gtk_tree_model_get_iter_first(store, &iter);
1552 gboolean mark_val, selected;
1553 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1555 mark_val = file_data_get_mark(fd, n);
1556 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1560 case MTS_MODE_SET: selected = mark_val;
1562 case MTS_MODE_OR: selected = mark_val || selected;
1564 case MTS_MODE_AND: selected = mark_val && selected;
1566 case MTS_MODE_MINUS: selected = !mark_val && selected;
1571 gtk_tree_selection_select_iter(selection, &iter);
1573 gtk_tree_selection_unselect_iter(selection, &iter);
1575 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1579 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1581 GtkTreeModel *store;
1582 GtkTreeSelection *selection;
1587 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1589 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1590 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1594 GtkTreePath *tpath = work->data;
1598 gtk_tree_model_get_iter(store, &iter, tpath);
1599 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1601 /* the change has a very limited range and the standard notification would trigger
1602 complete re-read of the directory - try to do only minimal update instead */
1603 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1607 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1609 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1611 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1615 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1617 vf_refresh_idle(vf);
1621 /* mark functions can have various side effects - update all columns to be sure */
1622 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1623 /* mark functions can change sidecars too */
1624 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1628 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1632 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1637 *-----------------------------------------------------------------------------
1639 *-----------------------------------------------------------------------------
1642 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
1644 GtkTreeViewColumn *column;
1645 GtkCellRenderer *cell;
1648 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1649 if (!column) return;
1651 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1653 list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1658 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1659 gtk_tree_view_column_set_visible(column, thumb);
1661 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1662 if (!column) return;
1663 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1665 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1666 if (!column) return;
1667 gtk_tree_view_column_set_visible(column, !multiline);
1669 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1670 if (!column) return;
1671 gtk_tree_view_column_set_visible(column, !multiline);
1674 static gboolean vflist_is_multiline(ViewFile *vf)
1676 return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
1680 static void vflist_populate_view(ViewFile *vf, gboolean force)
1682 GtkTreeStore *store;
1685 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1691 vflist_store_clear(vf, FALSE);
1696 vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
1698 selected = vflist_selection_get_list(vf);
1700 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force);
1702 if (selected && vflist_selection_count(vf, NULL) == 0)
1704 /* all selected files disappeared */
1705 vflist_select_closest(vf, selected->data);
1708 filelist_free(selected);
1711 vf_thumb_update(vf);
1714 gboolean vflist_refresh(ViewFile *vf)
1717 gboolean ret = TRUE;
1719 old_list = vf->list;
1722 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1725 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1727 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1729 if (vf->marks_enabled)
1731 // When marks are enabled, lock FileDatas so that we don't end up re-parsing XML
1732 // each time a mark is changed.
1733 file_data_lock_list(vf->list);
1737 // FIXME: only do this when needed (aka when we just switched from
1738 // FIXME: marks-enabled to marks-disabled)
1739 file_data_unlock_list(vf->list);
1742 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1743 vf->list = fileclusterlist_remove_children_from_list(vf->cluster_list, vf->list);
1744 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1746 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1747 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1750 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1752 vflist_populate_view(vf, FALSE);
1754 DEBUG_1("%s vflist_refresh: free filelist", get_exec_time());
1756 filelist_free(old_list);
1757 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1764 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1766 #define CELL_HEIGHT_OVERRIDE 512
1768 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1772 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1773 if (spec && G_IS_PARAM_SPEC_INT(spec))
1775 GParamSpecInt *spec_int;
1777 spec_int = G_PARAM_SPEC_INT(spec);
1778 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1782 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1784 static GdkColor color;
1785 static GtkWidget *done = NULL;
1791 style = gtk_widget_get_style(widget);
1792 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1793 shift_color(&color, -1, 0);
1800 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1801 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1803 ViewFile *vf = data;
1806 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1807 g_object_set(G_OBJECT(cell),
1808 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1809 "cell-background-set", set, NULL);
1812 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1814 GtkTreeViewColumn *column;
1815 GtkCellRenderer *renderer;
1817 column = gtk_tree_view_column_new();
1818 gtk_tree_view_column_set_title(column, title);
1819 gtk_tree_view_column_set_min_width(column, 4);
1823 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1824 renderer = gtk_cell_renderer_text_new();
1827 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1829 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1830 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1832 gtk_tree_view_column_set_expand(column, TRUE);
1836 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1837 renderer = gtk_cell_renderer_pixbuf_new();
1838 cell_renderer_height_override(renderer);
1839 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1840 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1843 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1844 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1845 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1847 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1850 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1852 ViewFile *vf = data;
1853 GtkTreeStore *store;
1854 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1860 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1861 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1864 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1866 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1868 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1871 /* the change has a very limited range and the standard notification would trigger
1872 complete re-read of the directory - try to do only minimal update instead */
1873 file_data_unregister_notify_func(vf_notify_cb, vf);
1874 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1875 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1877 vf_refresh_idle(vf);
1881 /* mark functions can have various side effects - update all columns to be sure */
1882 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1883 /* mark functions can change sidecars too */
1884 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1886 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1888 gtk_tree_path_free(path);
1891 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1893 GtkTreeViewColumn *column;
1894 GtkCellRenderer *renderer;
1896 renderer = gtk_cell_renderer_toggle_new();
1897 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1899 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1900 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1901 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1903 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1904 gtk_tree_view_column_set_fixed_width(column, 22);
1905 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1908 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1912 *-----------------------------------------------------------------------------
1914 *-----------------------------------------------------------------------------
1917 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1920 if (!dir_fd) return FALSE;
1921 if (vf->dir_fd == dir_fd) return TRUE;
1923 file_data_unref(vf->dir_fd);
1924 vf->dir_fd = file_data_ref(dir_fd);
1926 /* force complete reload */
1927 vflist_store_clear(vf, TRUE);
1929 filelist_free(vf->list);
1932 ret = vf_refresh(vf);
1933 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
1937 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1939 ViewFile *vf = data;
1941 file_data_unregister_notify_func(vf_notify_cb, vf);
1943 vflist_select_idle_cancel(vf);
1944 vf_refresh_idle_cancel(vf);
1947 filelist_free(vf->list);
1950 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1952 GtkTreeStore *store;
1953 GtkTreeSelection *selection;
1954 GType flist_types[FILE_COLUMN_COUNT];
1958 vf->info = g_new0(ViewFileInfoList, 1);
1960 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1961 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1962 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1963 flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING;
1964 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1965 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
1966 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1967 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1968 flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN;
1969 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1970 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1971 flist_types[i] = G_TYPE_BOOLEAN;
1973 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
1975 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1976 g_object_unref(store);
1978 g_signal_connect(G_OBJECT(vf->listview), "row-expanded",
1979 G_CALLBACK(vflist_expand_cb), vf);
1981 g_signal_connect(G_OBJECT(vf->listview), "row-collapsed",
1982 G_CALLBACK(vflist_collapse_cb), vf);
1984 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1985 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1986 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
1988 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
1989 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
1993 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
1995 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
1996 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
2000 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2001 g_assert(column == FILE_VIEW_COLUMN_THUMB);
2004 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE);
2005 g_assert(column == FILE_VIEW_COLUMN_FORMATTED);
2008 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2009 g_assert(column == FILE_VIEW_COLUMN_SIZE);
2012 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2013 g_assert(column == FILE_VIEW_COLUMN_DATE);
2016 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2020 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2022 if (VFLIST(vf)->thumbs_enabled == enable) return;
2024 VFLIST(vf)->thumbs_enabled = enable;
2026 /* vflist_populate_view is better than vf_refresh:
2027 - no need to re-read the directory
2028 - force update because the formatted string has changed
2032 vflist_populate_view(vf, TRUE);
2033 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
2037 void vflist_marks_set(ViewFile *vf, gboolean enable)
2039 GList *columns, *work;
2041 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2046 GtkTreeViewColumn *column = work->data;
2047 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2050 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2051 gtk_tree_view_column_set_visible(column, enable);
2056 // Previously disabled, which means that vf->list is complete
2057 file_data_lock_list(vf->list);
2061 // Previously enabled, which means that vf->list is incomplete
2064 g_list_free(columns);
2067 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */