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,
985 /* always sort sidecars by name */
986 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE);
990 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
993 if (match == 0) g_warning("multiple fd for the same path");
1009 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
1014 here should be used gtk_tree_store_append, but this function seems to be O(n)
1015 and it seems to be much faster to add new entries to the beginning and reorder later
1018 gtk_tree_store_prepend(store, &new, parent_iter);
1021 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
1022 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected, force);
1024 if (g_list_find(selected, fd))
1026 /* renamed files - the same fd appears at different position - select it again*/
1027 GtkTreeSelection *selection;
1028 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1029 gtk_tree_selection_select_iter(selection, &new);
1036 file_data_unref(old_fd);
1037 valid = gtk_tree_store_remove(store, &iter);
1042 if (fd->version != old_version || force)
1044 vflist_setup_iter(vf, store, &iter, fd);
1045 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected, force);
1048 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1059 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1060 file_data_unref(old_fd);
1062 valid = gtk_tree_store_remove(store, &iter);
1065 /* move the prepended entries to the correct position */
1069 gint num_total = num_prepended + num_ordered;
1070 gint *new_order = g_malloc(num_total * sizeof(gint));
1072 for (i = 0; i < num_total; i++)
1074 if (i < num_ordered)
1075 new_order[i] = num_prepended + i;
1077 new_order[i] = num_total - 1 - i;
1079 gtk_tree_store_reorder(store, parent_iter, new_order);
1085 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
1088 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
1090 GtkTreeStore *store;
1093 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1094 if (!vf->list) return;
1100 FileData *fd = work->data;
1101 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1106 vf->sort_method = type;
1107 vf->sort_ascend = ascend;
1109 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1111 new_order = g_malloc(i * sizeof(gint));
1117 FileData *fd = work->data;
1118 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1123 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1124 gtk_tree_store_reorder(store, NULL, new_order);
1127 g_hash_table_destroy(fd_idx_hash);
1131 *-----------------------------------------------------------------------------
1133 *-----------------------------------------------------------------------------
1137 void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1142 FileData *fd = work->data;
1145 if (fd->thumb_pixbuf) (*done)++;
1147 if (fd->sidecar_files)
1149 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1155 void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
1157 GtkTreeStore *store;
1160 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1162 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1163 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1166 FileData *vflist_thumb_next_fd(ViewFile *vf)
1169 FileData *fd = NULL;
1171 /* first check the visible files */
1173 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1175 GtkTreeModel *store;
1177 gboolean valid = TRUE;
1179 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1180 gtk_tree_model_get_iter(store, &iter, tpath);
1181 gtk_tree_path_free(tpath);
1184 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1188 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1190 if (!nfd->thumb_pixbuf) fd = nfd;
1192 valid = gtk_tree_model_iter_next(store, &iter);
1196 /* then find first undone */
1200 GList *work = vf->list;
1203 FileData *fd_p = work->data;
1204 if (!fd_p->thumb_pixbuf)
1208 GList *work2 = fd_p->sidecar_files;
1210 while (work2 && !fd)
1213 if (!fd_p->thumb_pixbuf) fd = fd_p;
1214 work2 = work2->next;
1225 *-----------------------------------------------------------------------------
1227 *-----------------------------------------------------------------------------
1230 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1233 GList *work, *work2;
1238 FileData *list_fd = work->data;
1239 if (list_fd == fd) return p;
1241 work2 = list_fd->sidecar_files;
1244 /* FIXME: return the same index also for sidecars
1245 it is sufficient for next/prev navigation but it should be rewritten
1246 without using indexes at all
1248 FileData *sidecar_fd = work2->data;
1249 if (sidecar_fd == fd) return p;
1250 work2 = work2->next;
1261 *-----------------------------------------------------------------------------
1263 *-----------------------------------------------------------------------------
1266 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1268 GtkTreeModel *store;
1269 GtkTreeSelection *selection;
1272 gboolean found = FALSE;
1274 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1275 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1277 while (!found && work)
1279 GtkTreePath *tpath = work->data;
1283 gtk_tree_model_get_iter(store, &iter, tpath);
1284 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1285 if (fd_n == fd) found = TRUE;
1288 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1294 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1298 fd = vf_index_get_data(vf, row);
1299 return vflist_row_is_selected(vf, fd);
1302 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1304 GtkTreeModel *store;
1305 GtkTreeSelection *selection;
1309 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1310 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1320 GtkTreePath *tpath = work->data;
1324 gtk_tree_model_get_iter(store, &iter, tpath);
1325 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1334 count = g_list_length(slist);
1335 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1341 GList *vflist_selection_get_list(ViewFile *vf)
1343 GtkTreeModel *store;
1344 GtkTreeSelection *selection;
1349 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1350 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1354 GtkTreePath *tpath = work->data;
1358 gtk_tree_model_get_iter(store, &iter, tpath);
1359 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1361 list = g_list_prepend(list, file_data_ref(fd));
1363 if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
1365 /* unexpanded - add whole group */
1366 GList *work2 = fd->sidecar_files;
1369 FileData *sfd = work2->data;
1370 list = g_list_prepend(list, file_data_ref(sfd));
1371 work2 = work2->next;
1377 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1380 return g_list_reverse(list);
1383 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1385 GtkTreeModel *store;
1386 GtkTreeSelection *selection;
1391 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1392 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1396 GtkTreePath *tpath = work->data;
1400 gtk_tree_model_get_iter(store, &iter, tpath);
1401 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1403 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1407 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1410 return g_list_reverse(list);
1413 void vflist_select_all(ViewFile *vf)
1415 GtkTreeSelection *selection;
1417 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1418 gtk_tree_selection_select_all(selection);
1420 VFLIST(vf)->select_fd = NULL;
1423 void vflist_select_none(ViewFile *vf)
1425 GtkTreeSelection *selection;
1427 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1428 gtk_tree_selection_unselect_all(selection);
1431 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1436 tpath = gtk_tree_model_get_path(store, iter);
1437 result = gtk_tree_path_prev(tpath);
1439 gtk_tree_model_get_iter(store, iter, tpath);
1441 gtk_tree_path_free(tpath);
1446 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1448 if (!gtk_tree_model_get_iter_first(store, iter))
1453 GtkTreeIter next = *iter;
1455 if (gtk_tree_model_iter_next(store, &next))
1464 void vflist_select_invert(ViewFile *vf)
1467 GtkTreeSelection *selection;
1468 GtkTreeModel *store;
1471 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1472 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1474 /* Backward iteration prevents scrolling to the end of the list,
1475 * it scrolls to the first selected row instead. */
1476 valid = tree_model_get_iter_last(store, &iter);
1480 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1483 gtk_tree_selection_unselect_iter(selection, &iter);
1485 gtk_tree_selection_select_iter(selection, &iter);
1487 valid = tree_model_iter_prev(store, &iter);
1491 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1495 if (vflist_find_row(vf, fd, &iter) < 0) return;
1497 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1499 if (!vflist_row_is_selected(vf, fd))
1501 GtkTreeSelection *selection;
1502 GtkTreeModel *store;
1505 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1506 gtk_tree_selection_unselect_all(selection);
1507 gtk_tree_selection_select_iter(selection, &iter);
1508 vflist_move_cursor(vf, &iter);
1510 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1511 tpath = gtk_tree_model_get_path(store, &iter);
1512 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1513 gtk_tree_path_free(tpath);
1517 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1520 FileData *fd = NULL;
1522 if (sel_fd->parent) sel_fd = sel_fd->parent;
1531 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1533 if (match >= 0) break;
1536 if (fd) vflist_select_by_fd(vf, fd);
1540 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1542 GtkTreeModel *store;
1544 GtkTreeSelection *selection;
1548 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1550 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1551 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1553 valid = gtk_tree_model_get_iter_first(store, &iter);
1557 gboolean mark_val, selected;
1558 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1560 mark_val = file_data_get_mark(fd, n);
1561 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1565 case MTS_MODE_SET: selected = mark_val;
1567 case MTS_MODE_OR: selected = mark_val || selected;
1569 case MTS_MODE_AND: selected = mark_val && selected;
1571 case MTS_MODE_MINUS: selected = !mark_val && selected;
1576 gtk_tree_selection_select_iter(selection, &iter);
1578 gtk_tree_selection_unselect_iter(selection, &iter);
1580 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1584 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1586 GtkTreeModel *store;
1587 GtkTreeSelection *selection;
1592 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1594 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1595 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1599 GtkTreePath *tpath = work->data;
1603 gtk_tree_model_get_iter(store, &iter, tpath);
1604 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1606 /* the change has a very limited range and the standard notification would trigger
1607 complete re-read of the directory - try to do only minimal update instead */
1608 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1612 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1614 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1616 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1620 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1622 vf_refresh_idle(vf);
1626 /* mark functions can have various side effects - update all columns to be sure */
1627 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1628 /* mark functions can change sidecars too */
1629 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1633 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1637 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1642 *-----------------------------------------------------------------------------
1644 *-----------------------------------------------------------------------------
1647 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
1649 GtkTreeViewColumn *column;
1650 GtkCellRenderer *cell;
1653 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1654 if (!column) return;
1656 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1658 list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1663 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1664 gtk_tree_view_column_set_visible(column, thumb);
1666 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1667 if (!column) return;
1668 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1670 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1671 if (!column) return;
1672 gtk_tree_view_column_set_visible(column, !multiline);
1674 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1675 if (!column) return;
1676 gtk_tree_view_column_set_visible(column, !multiline);
1679 static gboolean vflist_is_multiline(ViewFile *vf)
1681 return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
1685 static void vflist_populate_view(ViewFile *vf, gboolean force)
1687 GtkTreeStore *store;
1690 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1696 vflist_store_clear(vf, FALSE);
1701 vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
1703 selected = vflist_selection_get_list(vf);
1705 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force);
1707 if (selected && vflist_selection_count(vf, NULL) == 0)
1709 /* all selected files disappeared */
1710 vflist_select_closest(vf, selected->data);
1713 filelist_free(selected);
1716 vf_thumb_update(vf);
1719 gboolean vflist_refresh(ViewFile *vf)
1722 gboolean ret = TRUE;
1724 old_list = vf->list;
1727 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1730 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1732 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1734 if (vf->marks_enabled)
1736 // When marks are enabled, lock FileDatas so that we don't end up re-parsing XML
1737 // each time a mark is changed.
1738 file_data_lock_list(vf->list);
1742 // FIXME: only do this when needed (aka when we just switched from
1743 // FIXME: marks-enabled to marks-disabled)
1744 file_data_unlock_list(vf->list);
1747 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1748 vf->list = fileclusterlist_remove_children_from_list(vf->cluster_list, vf->list);
1749 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1751 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1752 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1755 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1757 vflist_populate_view(vf, FALSE);
1759 DEBUG_1("%s vflist_refresh: free filelist", get_exec_time());
1761 filelist_free(old_list);
1762 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1769 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1771 #define CELL_HEIGHT_OVERRIDE 512
1773 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1777 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1778 if (spec && G_IS_PARAM_SPEC_INT(spec))
1780 GParamSpecInt *spec_int;
1782 spec_int = G_PARAM_SPEC_INT(spec);
1783 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1787 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1789 static GdkColor color;
1790 static GtkWidget *done = NULL;
1796 style = gtk_widget_get_style(widget);
1797 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1798 shift_color(&color, -1, 0);
1805 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1806 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1808 ViewFile *vf = data;
1812 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1813 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_POINTER, &fd, -1);
1814 // TODO(xsdg): optimize!
1817 FileCluster *fc = g_hash_table_lookup(vf->cluster_list->clusters, fd);
1820 if (filecluster_has_head(fc, fd))
1822 GdkColor *color_bg = g_new0(GdkColor, 1);
1823 color_bg->blue = 0x4000;
1824 color_bg->green = 0x4000;
1825 color_bg->red = 0xFFFF;
1827 g_object_set(G_OBJECT(cell),
1828 "cell-background-gdk", color_bg,
1829 "cell-background-set", TRUE, NULL);
1832 else if (filecluster_has_child(fc, fd))
1834 GdkColor *color_bg = g_new0(GdkColor, 1);
1835 color_bg->blue = 0x8000;
1836 color_bg->green = 0x8000;
1837 color_bg->red = 0xFFFF;
1839 g_object_set(G_OBJECT(cell),
1840 "cell-background-gdk", color_bg,
1841 "cell-background-set", TRUE, NULL);
1847 g_object_set(G_OBJECT(cell),
1848 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1849 "cell-background-set", set, NULL);
1852 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1854 GtkTreeViewColumn *column;
1855 GtkCellRenderer *renderer;
1857 column = gtk_tree_view_column_new();
1858 gtk_tree_view_column_set_title(column, title);
1859 gtk_tree_view_column_set_min_width(column, 4);
1863 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1864 renderer = gtk_cell_renderer_text_new();
1867 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1869 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1870 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1872 gtk_tree_view_column_set_expand(column, TRUE);
1876 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1877 renderer = gtk_cell_renderer_pixbuf_new();
1878 cell_renderer_height_override(renderer);
1879 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1880 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1883 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1884 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1885 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1887 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1890 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1892 ViewFile *vf = data;
1893 GtkTreeStore *store;
1894 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1900 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1901 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1904 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1906 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1908 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1911 /* the change has a very limited range and the standard notification would trigger
1912 complete re-read of the directory - try to do only minimal update instead */
1913 file_data_unregister_notify_func(vf_notify_cb, vf);
1914 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1915 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1917 vf_refresh_idle(vf);
1921 /* mark functions can have various side effects - update all columns to be sure */
1922 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1923 /* mark functions can change sidecars too */
1924 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1926 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1928 gtk_tree_path_free(path);
1931 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1933 GtkTreeViewColumn *column;
1934 GtkCellRenderer *renderer;
1936 renderer = gtk_cell_renderer_toggle_new();
1937 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1939 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1940 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1941 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1943 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1944 gtk_tree_view_column_set_fixed_width(column, 22);
1945 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1948 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1952 *-----------------------------------------------------------------------------
1954 *-----------------------------------------------------------------------------
1957 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1960 if (!dir_fd) return FALSE;
1961 if (vf->dir_fd == dir_fd) return TRUE;
1963 file_data_unref(vf->dir_fd);
1964 vf->dir_fd = file_data_ref(dir_fd);
1966 /* force complete reload */
1967 vflist_store_clear(vf, TRUE);
1969 filelist_free(vf->list);
1972 ret = vf_refresh(vf);
1973 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
1977 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1979 ViewFile *vf = data;
1981 file_data_unregister_notify_func(vf_notify_cb, vf);
1983 vflist_select_idle_cancel(vf);
1984 vf_refresh_idle_cancel(vf);
1987 filelist_free(vf->list);
1990 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1992 GtkTreeStore *store;
1993 GtkTreeSelection *selection;
1994 GType flist_types[FILE_COLUMN_COUNT];
1998 vf->info = g_new0(ViewFileInfoList, 1);
2000 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
2001 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
2002 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
2003 flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING;
2004 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
2005 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
2006 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
2007 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
2008 flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN;
2009 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
2010 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
2011 flist_types[i] = G_TYPE_BOOLEAN;
2013 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
2015 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2016 g_object_unref(store);
2018 g_signal_connect(G_OBJECT(vf->listview), "row-expanded",
2019 G_CALLBACK(vflist_expand_cb), vf);
2021 g_signal_connect(G_OBJECT(vf->listview), "row-collapsed",
2022 G_CALLBACK(vflist_collapse_cb), vf);
2024 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2025 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
2026 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
2028 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2029 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2033 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2035 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2036 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
2040 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2041 g_assert(column == FILE_VIEW_COLUMN_THUMB);
2044 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE);
2045 g_assert(column == FILE_VIEW_COLUMN_FORMATTED);
2048 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2049 g_assert(column == FILE_VIEW_COLUMN_SIZE);
2052 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2053 g_assert(column == FILE_VIEW_COLUMN_DATE);
2056 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2060 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2062 if (VFLIST(vf)->thumbs_enabled == enable) return;
2064 VFLIST(vf)->thumbs_enabled = enable;
2066 /* vflist_populate_view is better than vf_refresh:
2067 - no need to re-read the directory
2068 - force update because the formatted string has changed
2072 vflist_populate_view(vf, TRUE);
2073 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
2077 void vflist_marks_set(ViewFile *vf, gboolean enable)
2079 GList *columns, *work;
2081 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2086 GtkTreeViewColumn *column = work->data;
2087 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2090 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2091 gtk_tree_view_column_set_visible(column, enable);
2096 // Previously disabled, which means that vf->list is complete
2097 file_data_lock_list(vf->list);
2101 // Previously enabled, which means that vf->list is incomplete
2104 g_list_free(columns);
2107 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */