4 * Copyright (C) 2008 - 2009 The Geeqie Team
8 * This software is released under the GNU General Public License (GNU GPL).
9 * Please read the included file COPYING for more information.
10 * This software comes with no warranty of any kind, use at your own risk!
14 #include "view_file_list.h"
17 #include "cache_maint.h"
22 #include "layout_image.h"
27 #include "ui_fileops.h"
29 #include "ui_tree_edit.h"
30 #include "uri_utils.h"
31 #include "view_file.h"
33 #include <gdk/gdkkeysyms.h> /* for keyboard values */
35 /* Index to tree store */
37 FILE_COLUMN_POINTER = 0,
41 FILE_COLUMN_MULTILINE,
46 FILE_COLUMN_MARKS_LAST = FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
51 /* Index to tree view */
53 FILE_VIEW_COLUMN_MARKS = 0,
54 FILE_VIEW_COLUMN_MARKS_LAST = FILE_VIEW_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
55 FILE_VIEW_COLUMN_THUMB,
56 FILE_VIEW_COLUMN_MULTILINE,
57 FILE_VIEW_COLUMN_NAME,
58 FILE_VIEW_COLUMN_SIZE,
59 FILE_VIEW_COLUMN_DATE,
60 FILE_VIEW_COLUMN_COUNT
65 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd);
66 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data);
67 static void vflist_populate_view(ViewFile *vf);
71 *-----------------------------------------------------------------------------
73 *-----------------------------------------------------------------------------
80 } ViewFileFindRowData;
82 static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
84 ViewFileFindRowData *find = data;
86 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
97 static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter)
100 ViewFileFindRowData data = {fd, iter, FALSE, 0};
102 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
103 gtk_tree_model_foreach(store, vflist_find_row_cb, &data);
113 static FileData *vflist_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter)
116 GtkTreeViewColumn *column;
118 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), x, y,
119 &tpath, &column, NULL, NULL))
125 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
126 gtk_tree_model_get_iter(store, &row, tpath);
127 gtk_tree_path_free(tpath);
128 gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &fd, -1);
137 static gint vflist_find_sidecar_list_idx(GList *work, FileData *fd)
142 FileData *fd_p = work->data;
143 if (fd == fd_p) return i;
147 GList *work2 = fd_p->sidecar_files;
151 if (fd == fd_p) return i;
161 static gint vflist_sidecar_list_count(GList *work)
166 FileData *fd = work->data;
169 GList *work2 = fd->sidecar_files;
181 static gboolean vflist_store_clear_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
184 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
189 static void vflist_store_clear(ViewFile *vf)
192 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
193 gtk_tree_model_foreach(store, vflist_store_clear_cb, NULL);
194 gtk_tree_store_clear(GTK_TREE_STORE(store));
197 void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set)
202 if (vflist_find_row(vf, fd, &iter) < 0) return;
203 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
204 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
207 static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
212 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
214 tpath = gtk_tree_model_get_path(store, iter);
215 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
216 gtk_tree_path_free(tpath);
220 static gint vflist_column_idx(ViewFile *vf, gint store_idx)
222 GList *columns, *work;
225 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
229 GtkTreeViewColumn *column = work->data;
230 if (store_idx == GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx")))
236 g_list_free(columns);
242 *-----------------------------------------------------------------------------
244 *-----------------------------------------------------------------------------
247 static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
248 GtkSelectionData *selection_data, guint info,
249 guint time, gpointer data)
253 gchar *uri_text = NULL;
256 if (!VFLIST(vf)->click_fd) return;
258 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
260 list = vf_selection_get_list(vf);
264 list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
269 uri_text = uri_text_from_filelist(list, &total, (info == TARGET_TEXT_PLAIN));
272 DEBUG_1("%s", uri_text);
274 gtk_selection_data_set(selection_data, selection_data->target,
275 8, (guchar *)uri_text, total);
279 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
283 vflist_color_set(vf, VFLIST(vf)->click_fd, TRUE);
285 if (VFLIST(vf)->thumbs_enabled &&
286 VFLIST(vf)->click_fd && VFLIST(vf)->click_fd->thumb_pixbuf)
290 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
291 items = vf_selection_count(vf, NULL);
295 dnd_set_drag_icon(widget, context, VFLIST(vf)->click_fd->thumb_pixbuf, items);
299 static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
303 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
305 if (context->action == GDK_ACTION_MOVE)
311 static void vflist_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context,
312 int x, int y, GtkSelectionData *selection,
313 guint info, guint time, gpointer data)
317 if (info == TARGET_TEXT_PLAIN) {
318 FileData *fd = vflist_find_data_by_coord(vf, x, y, NULL);
321 /* Add keywords to file */
322 gchar *str = g_strndup((gchar *)selection->data, selection->length);
323 GList *kw_list = string_to_keywords_list(str);
325 metadata_append_list(fd, KEYWORD_KEY, kw_list);
326 string_list_free(kw_list);
329 file notification should handle this automatically
330 if (vf->layout && vf->layout->bar_info) {
331 bar_set_fd(vf->layout->bar_info, fd);
338 void vflist_dnd_init(ViewFile *vf)
340 gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
341 dnd_file_drag_types, dnd_file_drag_types_count,
342 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
343 gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL,
344 dnd_file_drag_types, dnd_file_drag_types_count,
345 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
347 g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
348 G_CALLBACK(vflist_dnd_get), vf);
349 g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
350 G_CALLBACK(vflist_dnd_begin), vf);
351 g_signal_connect(G_OBJECT(vf->listview), "drag_end",
352 G_CALLBACK(vflist_dnd_end), vf);
353 g_signal_connect(G_OBJECT(vf->listview), "drag_data_received",
354 G_CALLBACK(vflist_drag_data_received), vf);
358 *-----------------------------------------------------------------------------
360 *-----------------------------------------------------------------------------
363 GList *vflist_pop_menu_file_list(ViewFile *vf)
365 if (!VFLIST(vf)->click_fd) return NULL;
367 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
369 return vf_selection_get_list(vf);
372 return g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
375 void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
379 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
383 list = vf_selection_get_list(vf);
384 view_window_new_from_list(list);
389 view_window_new(VFLIST(vf)->click_fd);
393 void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
398 list = vf_pop_menu_file_list(vf);
399 if (options->file_ops.enable_in_place_rename &&
400 list && !list->next && VFLIST(vf)->click_fd)
407 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
408 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
412 tpath = gtk_tree_model_get_path(store, &iter);
413 tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
414 vflist_column_idx(vf, FILE_COLUMN_NAME), VFLIST(vf)->click_fd->name,
415 vflist_row_rename_cb, vf);
416 gtk_tree_path_free(tpath);
421 file_util_rename(NULL, list, vf->listview);
424 void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
428 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
431 layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
435 vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled);
439 void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
443 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
447 void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
450 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
451 VFLIST(vf)->click_fd = NULL;
457 *-----------------------------------------------------------------------------
459 *-----------------------------------------------------------------------------
462 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
467 if (!new || !new[0]) return FALSE;
469 new_path = g_build_filename(vf->dir_fd->path, new, NULL);
471 if (strchr(new, G_DIR_SEPARATOR) != NULL)
473 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
474 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
479 gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL);
480 FileData *fd = file_data_new_simple(old_path); /* get the fd from cache */
481 file_util_rename_simple(fd, new_path, vf->listview);
491 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
499 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return;
500 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
501 tpath = gtk_tree_model_get_path(store, &iter);
502 tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
503 gtk_tree_path_free(tpath);
505 popup_menu_position_clamp(menu, x, y, 0);
508 gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
513 if (event->keyval != GDK_Menu) return FALSE;
515 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
521 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
522 gtk_tree_model_get_iter(store, &iter, tpath);
523 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
524 gtk_tree_path_free(tpath);
528 VFLIST(vf)->click_fd = NULL;
531 vf->popup = vf_pop_menu(vf);
532 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
537 gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
543 GtkTreeViewColumn *column;
545 vf->clicked_mark = 0;
547 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
548 &tpath, &column, NULL, NULL))
551 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
553 if (bevent->button == MOUSE_BUTTON_LEFT &&
554 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
557 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
558 vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
560 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
562 gtk_tree_model_get_iter(store, &iter, tpath);
563 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
565 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE);
567 gtk_tree_path_free(tpath);
570 VFLIST(vf)->click_fd = fd;
572 if (bevent->button == MOUSE_BUTTON_RIGHT)
574 vf->popup = vf_pop_menu(vf);
575 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
576 bevent->button, bevent->time);
580 if (!fd) return FALSE;
582 if (bevent->button == MOUSE_BUTTON_MIDDLE)
584 if (!vflist_row_is_selected(vf, fd))
586 vflist_color_set(vf, fd, TRUE);
592 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
593 !(bevent->state & GDK_SHIFT_MASK ) &&
594 !(bevent->state & GDK_CONTROL_MASK ) &&
595 vflist_row_is_selected(vf, fd))
597 GtkTreeSelection *selection;
599 gtk_widget_grab_focus(widget);
602 /* returning FALSE and further processing of the event is needed for
603 correct operation of the expander, to show the sidecar files.
604 It however resets the selection of multiple files. With this condition
605 it should work for both cases */
606 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
607 return (gtk_tree_selection_count_selected_rows(selection) > 1);
611 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
613 if (vf->layout) layout_image_full_screen_start(vf->layout);
620 gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
627 if (bevent->button == MOUSE_BUTTON_MIDDLE)
629 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
632 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
637 if ((bevent->x != 0 || bevent->y != 0) &&
638 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
639 &tpath, NULL, NULL, NULL))
643 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
644 gtk_tree_model_get_iter(store, &iter, tpath);
645 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
646 gtk_tree_path_free(tpath);
649 if (bevent->button == MOUSE_BUTTON_MIDDLE)
651 if (fd && VFLIST(vf)->click_fd == fd)
653 GtkTreeSelection *selection;
655 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
656 if (vflist_row_is_selected(vf, fd))
658 gtk_tree_selection_unselect_iter(selection, &iter);
662 gtk_tree_selection_select_iter(selection, &iter);
668 if (fd && VFLIST(vf)->click_fd == fd &&
669 !(bevent->state & GDK_SHIFT_MASK ) &&
670 !(bevent->state & GDK_CONTROL_MASK ) &&
671 vflist_row_is_selected(vf, fd))
673 GtkTreeSelection *selection;
675 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
676 gtk_tree_selection_unselect_all(selection);
677 gtk_tree_selection_select_iter(selection, &iter);
678 vflist_move_cursor(vf, &iter);
679 // return TRUE;// FIXME - expand
685 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
687 FileData *read_ahead_fd = NULL;
693 cur_fd = layout_image_get_fd(vf->layout);
694 if (sel_fd == cur_fd) return; /* no change */
696 row = g_list_index(vf->list, sel_fd);
697 // FIXME sidecar data
699 if (sel_fd && options->image.enable_read_ahead && row >= 0)
701 if (row > g_list_index(vf->list, cur_fd) &&
702 (guint) (row + 1) < vf_count(vf, NULL))
704 read_ahead_fd = vf_index_get_data(vf, row + 1);
708 read_ahead_fd = vf_index_get_data(vf, row - 1);
712 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
715 static gboolean vflist_select_idle_cb(gpointer data)
721 VFLIST(vf)->select_idle_id = -1;
727 if (VFLIST(vf)->select_fd)
729 vflist_select_image(vf, VFLIST(vf)->select_fd);
730 VFLIST(vf)->select_fd = NULL;
733 VFLIST(vf)->select_idle_id = -1;
737 static void vflist_select_idle_cancel(ViewFile *vf)
739 if (VFLIST(vf)->select_idle_id != -1) g_source_remove(VFLIST(vf)->select_idle_id);
740 VFLIST(vf)->select_idle_id = -1;
743 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
744 gboolean path_currently_selected, gpointer data)
749 if (!path_currently_selected &&
750 gtk_tree_model_get_iter(store, &iter, tpath))
752 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
756 VFLIST(vf)->select_fd = NULL;
760 VFLIST(vf)->select_idle_id == -1)
762 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
769 *-----------------------------------------------------------------------------
771 *-----------------------------------------------------------------------------
775 static gboolean vflist_dummy_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
776 gboolean path_currently_selected, gpointer data)
782 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
785 gchar *sidecars = NULL;
786 gchar *name_sidecars;
788 const gchar *time = text_from_time(fd->date);
789 gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
792 if (fd->sidecar_files)
794 sidecars = file_data_sc_list_to_string(fd);
795 name_sidecars = g_strdup_printf("%s%s %s", link, fd->name, sidecars);
799 name_sidecars = g_strdup_printf("%s%s", link, fd->name);
801 size = text_from_size(fd->size);
803 multiline = g_strdup_printf("%s\n%s\n%s", name_sidecars, size, time);
805 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
806 FILE_COLUMN_VERSION, fd->version,
807 FILE_COLUMN_THUMB, fd->thumb_pixbuf,
808 FILE_COLUMN_MULTILINE, multiline,
809 FILE_COLUMN_NAME, name_sidecars,
810 FILE_COLUMN_SIZE, size,
811 FILE_COLUMN_DATE, time,
812 #define STORE_SET_IS_SLOW 1
813 #if STORE_SET_IS_SLOW
814 /* this is 3x faster on a directory with 20000 files */
815 FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
816 FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
817 FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
818 FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
819 FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
820 FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
821 #if FILEDATA_MARKS_SIZE != 6
822 #error this needs to be updated
825 FILE_COLUMN_COLOR, FALSE, -1);
827 #if !STORE_SET_IS_SLOW
830 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
831 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
836 g_free(name_sidecars);
840 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected)
845 gint num_ordered = 0;
846 gint num_prepended = 0;
848 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
854 FileData *fd = work->data;
855 gboolean done = FALSE;
859 FileData *old_fd = NULL;
860 gint old_version = 0;
865 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
866 FILE_COLUMN_POINTER, &old_fd,
867 FILE_COLUMN_VERSION, &old_version,
877 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
879 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
881 if (match == 0) g_warning("multiple fd for the same path");
896 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
901 here should be used gtk_tree_store_append, but this function seems to be O(n)
902 and it seems to be much faster to add new entries to the beginning and reorder later
905 gtk_tree_store_prepend(store, &new, parent_iter);
908 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
909 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected);
911 if (g_list_find(selected, fd))
913 /* renamed files - the same fd appears at different position - select it again*/
914 GtkTreeSelection *selection;
915 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
916 gtk_tree_selection_select_iter(selection, &new);
923 file_data_unref(old_fd);
924 valid = gtk_tree_store_remove(store, &iter);
928 if (fd->version != old_version)
930 vflist_setup_iter(vf, store, &iter, fd);
931 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected);
934 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
945 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
946 file_data_unref(old_fd);
948 valid = gtk_tree_store_remove(store, &iter);
951 /* move the prepended entries to the correct position */
955 gint num_total = num_prepended + num_ordered;
956 gint *new_order = g_malloc(num_total * sizeof(gint));
958 for (i = 0; i < num_total; i++)
961 new_order[i] = num_prepended + i;
963 new_order[i] = num_total - 1 - i;
965 gtk_tree_store_reorder(store, parent_iter, new_order);
971 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
974 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
979 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
980 if (!vf->list) return;
986 FileData *fd = work->data;
987 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
992 vf->sort_method = type;
993 vf->sort_ascend = ascend;
995 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
997 new_order = g_malloc(i * sizeof(gint));
1003 FileData *fd = work->data;
1004 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1009 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1010 gtk_tree_store_reorder(store, NULL, new_order);
1013 g_hash_table_destroy(fd_idx_hash);
1017 *-----------------------------------------------------------------------------
1019 *-----------------------------------------------------------------------------
1022 static gboolean vflist_thumb_next(ViewFile *vf);
1024 static void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1029 FileData *fd = work->data;
1032 if (fd->thumb_pixbuf) (*done)++;
1034 if (fd->sidecar_files)
1036 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1042 static gdouble vflist_thumb_progress(ViewFile *vf)
1047 vflist_thumb_progress_count(vf->list, &count, &done);
1049 DEBUG_1("thumb progress: %d of %d", done, count);
1050 return (gdouble)done / count;
1054 static void vflist_thumb_status(ViewFile *vf, gdouble val, const gchar *text)
1056 if (vf->func_thumb_status)
1058 vf->func_thumb_status(vf, val, text, vf->data_thumb_status);
1062 static void vflist_thumb_cleanup(ViewFile *vf)
1064 vflist_thumb_status(vf, 0.0, NULL);
1066 vf->thumbs_running = FALSE;
1068 thumb_loader_free(vf->thumbs_loader);
1069 vf->thumbs_loader = NULL;
1071 vf->thumbs_filedata = NULL;
1074 static void vflist_thumb_stop(ViewFile *vf)
1076 if (vf->thumbs_running) vflist_thumb_cleanup(vf);
1079 static void vflist_thumb_do(ViewFile *vf, ThumbLoader *tl, FileData *fd)
1081 GtkTreeStore *store;
1084 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1086 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1087 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1089 vflist_thumb_status(vf, vflist_thumb_progress(vf), _("Loading thumbs..."));
1092 static void vflist_thumb_error_cb(ThumbLoader *tl, gpointer data)
1094 ViewFile *vf = data;
1096 if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1098 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
1101 while (vflist_thumb_next(vf));
1104 static void vflist_thumb_done_cb(ThumbLoader *tl, gpointer data)
1106 ViewFile *vf = data;
1108 if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1110 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
1113 while (vflist_thumb_next(vf));
1116 static gboolean vflist_thumb_next(ViewFile *vf)
1119 FileData *fd = NULL;
1121 /* first check the visible files */
1123 if (GTK_WIDGET_REALIZED(vf->listview) &&
1124 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1126 GtkTreeModel *store;
1128 gboolean valid = TRUE;
1130 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1131 gtk_tree_model_get_iter(store, &iter, tpath);
1132 gtk_tree_path_free(tpath);
1134 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1136 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1137 if (fd->thumb_pixbuf) fd = NULL;
1139 valid = gtk_tree_model_iter_next(store, &iter);
1143 /* then find first undone */
1147 GList *work = vf->list;
1150 FileData *fd_p = work->data;
1151 if (!fd_p->thumb_pixbuf)
1155 GList *work2 = fd_p->sidecar_files;
1157 while (work2 && !fd)
1160 if (!fd_p->thumb_pixbuf) fd = fd_p;
1161 work2 = work2->next;
1171 vflist_thumb_cleanup(vf);
1175 vf->thumbs_filedata = fd;
1177 thumb_loader_free(vf->thumbs_loader);
1179 vf->thumbs_loader = thumb_loader_new(options->thumbnails.max_width, options->thumbnails.max_height);
1180 thumb_loader_set_callbacks(vf->thumbs_loader,
1181 vflist_thumb_done_cb,
1182 vflist_thumb_error_cb,
1186 if (!thumb_loader_start(vf->thumbs_loader, fd))
1188 /* set icon to unknown, continue */
1189 DEBUG_1("thumb loader start failed %s", fd->path);
1190 vflist_thumb_do(vf, vf->thumbs_loader, fd);
1198 static void vflist_thumb_update(ViewFile *vf)
1200 vflist_thumb_stop(vf);
1201 if (!VFLIST(vf)->thumbs_enabled) return;
1203 vflist_thumb_status(vf, 0.0, _("Loading thumbs..."));
1204 vf->thumbs_running = TRUE;
1206 while (vflist_thumb_next(vf));
1210 *-----------------------------------------------------------------------------
1212 *-----------------------------------------------------------------------------
1215 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1217 return g_list_nth_data(vf->list, row);
1220 gint vflist_index_by_path(ViewFile *vf, const gchar *path)
1225 if (!path) return -1;
1230 FileData *fd = work->data;
1231 if (strcmp(path, fd->path) == 0) return p;
1240 guint vflist_count(ViewFile *vf, gint64 *bytes)
1250 FileData *fd = work->data;
1258 return g_list_length(vf->list);
1261 GList *vflist_get_list(ViewFile *vf)
1269 FileData *fd = work->data;
1272 list = g_list_prepend(list, file_data_ref(fd));
1275 return g_list_reverse(list);
1279 *-----------------------------------------------------------------------------
1281 *-----------------------------------------------------------------------------
1284 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1286 GtkTreeModel *store;
1287 GtkTreeSelection *selection;
1290 gboolean found = FALSE;
1292 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1293 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1295 while (!found && work)
1297 GtkTreePath *tpath = work->data;
1301 gtk_tree_model_get_iter(store, &iter, tpath);
1302 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1303 if (fd_n == fd) found = TRUE;
1306 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1312 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1316 fd = vf_index_get_data(vf, row);
1317 return vflist_row_is_selected(vf, fd);
1320 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1322 GtkTreeModel *store;
1323 GtkTreeSelection *selection;
1327 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1328 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1338 GtkTreePath *tpath = work->data;
1342 gtk_tree_model_get_iter(store, &iter, tpath);
1343 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1352 count = g_list_length(slist);
1353 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1359 GList *vflist_selection_get_list(ViewFile *vf)
1361 GtkTreeModel *store;
1362 GtkTreeSelection *selection;
1367 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1368 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1372 GtkTreePath *tpath = work->data;
1376 gtk_tree_model_get_iter(store, &iter, tpath);
1377 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1379 list = g_list_prepend(list, file_data_ref(fd));
1383 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1386 return g_list_reverse(list);
1389 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1391 GtkTreeModel *store;
1392 GtkTreeSelection *selection;
1397 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1398 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1402 GtkTreePath *tpath = work->data;
1406 gtk_tree_model_get_iter(store, &iter, tpath);
1407 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1409 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1413 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1416 return g_list_reverse(list);
1419 void vflist_select_all(ViewFile *vf)
1421 GtkTreeSelection *selection;
1423 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1424 gtk_tree_selection_select_all(selection);
1426 VFLIST(vf)->select_fd = NULL;
1429 void vflist_select_none(ViewFile *vf)
1431 GtkTreeSelection *selection;
1433 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1434 gtk_tree_selection_unselect_all(selection);
1437 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1442 tpath = gtk_tree_model_get_path(store, iter);
1443 result = gtk_tree_path_prev(tpath);
1445 gtk_tree_model_get_iter(store, iter, tpath);
1447 gtk_tree_path_free(tpath);
1452 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1454 if (!gtk_tree_model_get_iter_first(store, iter))
1459 GtkTreeIter next = *iter;
1461 if (gtk_tree_model_iter_next(store, &next))
1470 void vflist_select_invert(ViewFile *vf)
1473 GtkTreeSelection *selection;
1474 GtkTreeModel *store;
1477 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1478 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1480 /* Backward iteration prevents scrolling to the end of the list,
1481 * it scrolls to the first selected row instead. */
1482 valid = tree_model_get_iter_last(store, &iter);
1486 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1489 gtk_tree_selection_unselect_iter(selection, &iter);
1491 gtk_tree_selection_select_iter(selection, &iter);
1493 valid = tree_model_iter_prev(store, &iter);
1497 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1501 if (vflist_find_row(vf, fd, &iter) < 0) return;
1503 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1505 if (!vflist_row_is_selected(vf, fd))
1507 GtkTreeSelection *selection;
1508 GtkTreeModel *store;
1511 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1512 gtk_tree_selection_unselect_all(selection);
1513 gtk_tree_selection_select_iter(selection, &iter);
1514 vflist_move_cursor(vf, &iter);
1516 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1517 tpath = gtk_tree_model_get_path(store, &iter);
1518 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1519 gtk_tree_path_free(tpath);
1523 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1526 FileData *fd = NULL;
1528 if (sel_fd->parent) sel_fd = sel_fd->parent;
1537 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1539 if (match >= 0) break;
1542 if (fd) vflist_select_by_fd(vf, fd);
1546 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1548 GtkTreeModel *store;
1550 GtkTreeSelection *selection;
1554 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1556 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1557 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1559 valid = gtk_tree_model_get_iter_first(store, &iter);
1563 gboolean mark_val, selected;
1564 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1566 mark_val = file_data_get_mark(fd, n);
1567 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1571 case MTS_MODE_SET: selected = mark_val;
1573 case MTS_MODE_OR: selected = mark_val | selected;
1575 case MTS_MODE_AND: selected = mark_val & selected;
1577 case MTS_MODE_MINUS: selected = !mark_val & selected;
1582 gtk_tree_selection_select_iter(selection, &iter);
1584 gtk_tree_selection_unselect_iter(selection, &iter);
1586 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1590 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1592 GtkTreeModel *store;
1593 GtkTreeSelection *selection;
1598 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1600 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1601 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1605 GtkTreePath *tpath = work->data;
1609 gtk_tree_model_get_iter(store, &iter, tpath);
1610 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1612 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1616 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1618 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1620 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1624 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1626 vf_refresh_idle(vf);
1630 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1632 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_MARKS + n, file_data_get_mark(fd, n), -1);
1636 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1641 *-----------------------------------------------------------------------------
1643 *-----------------------------------------------------------------------------
1646 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb)
1648 GtkTreeViewColumn *column;
1649 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_tree_view_column_get_cell_renderers(column);
1663 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1664 gtk_tree_view_column_set_visible(column, thumb);
1666 multiline = (thumb && options->thumbnails.max_height >= 48);
1668 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_MULTILINE);
1669 if (!column) return;
1670 gtk_tree_view_column_set_visible(column, multiline);
1671 if (multiline) gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1673 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_NAME);
1674 if (!column) return;
1675 gtk_tree_view_column_set_visible(column, !multiline);
1676 if (!multiline) gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1678 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1679 if (!column) return;
1680 gtk_tree_view_column_set_visible(column, !multiline);
1682 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1683 if (!column) return;
1684 gtk_tree_view_column_set_visible(column, !multiline);
1686 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1689 static void vflist_populate_view(ViewFile *vf)
1691 GtkTreeStore *store;
1692 gboolean thumbs_enabled;
1695 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1696 thumbs_enabled = VFLIST(vf)->thumbs_enabled;
1698 vflist_thumb_stop(vf);
1702 vflist_store_clear(vf);
1707 vflist_listview_set_columns(vf->listview, thumbs_enabled);
1709 selected = vflist_selection_get_list(vf);
1711 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected);
1713 if (selected && vflist_selection_count(vf, NULL) == 0)
1715 /* all selected files disappeared */
1716 vflist_select_closest(vf, selected->data);
1719 filelist_free(selected);
1722 vflist_thumb_update(vf);
1725 gboolean vflist_refresh(ViewFile *vf)
1728 gboolean ret = TRUE;
1730 old_list = vf->list;
1733 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1736 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1738 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1739 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1740 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1742 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1743 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1746 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1748 vflist_populate_view(vf);
1750 filelist_free(old_list);
1751 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1758 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1760 #define CELL_HEIGHT_OVERRIDE 512
1762 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1766 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1767 if (spec && G_IS_PARAM_SPEC_INT(spec))
1769 GParamSpecInt *spec_int;
1771 spec_int = G_PARAM_SPEC_INT(spec);
1772 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1776 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1778 static GdkColor color;
1779 static GtkWidget *done = NULL;
1785 style = gtk_widget_get_style(widget);
1786 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1787 shift_color(&color, -1, 0);
1794 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1795 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1797 ViewFile *vf = data;
1800 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1801 g_object_set(G_OBJECT(cell),
1802 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1803 "cell-background-set", set, NULL);
1806 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1808 GtkTreeViewColumn *column;
1809 GtkCellRenderer *renderer;
1811 column = gtk_tree_view_column_new();
1812 gtk_tree_view_column_set_title(column, title);
1813 gtk_tree_view_column_set_min_width(column, 4);
1817 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1818 renderer = gtk_cell_renderer_text_new();
1821 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1823 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1824 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1826 gtk_tree_view_column_set_expand(column, TRUE);
1830 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1831 renderer = gtk_cell_renderer_pixbuf_new();
1832 cell_renderer_height_override(renderer);
1833 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1834 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1837 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1838 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1839 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1841 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1844 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1846 ViewFile *vf = data;
1847 GtkTreeStore *store;
1848 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1854 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1855 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1858 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1860 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1862 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1864 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1865 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1866 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1868 vf_refresh_idle(vf);
1870 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1872 gtk_tree_store_set(store, &iter, col_idx, marked, -1);
1873 gtk_tree_path_free(path);
1876 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1878 GtkTreeViewColumn *column;
1879 GtkCellRenderer *renderer;
1880 GtkTreeStore *store;
1883 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1885 renderer = gtk_cell_renderer_toggle_new();
1886 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1888 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1889 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1890 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1892 index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1893 gtk_tree_view_column_set_fixed_width(column, 22);
1894 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1897 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1901 *-----------------------------------------------------------------------------
1903 *-----------------------------------------------------------------------------
1906 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1908 if (!dir_fd) return FALSE;
1909 if (vf->dir_fd == dir_fd) return TRUE;
1911 file_data_unref(vf->dir_fd);
1912 vf->dir_fd = file_data_ref(dir_fd);
1914 /* force complete reload */
1915 vflist_store_clear(vf);
1917 filelist_free(vf->list);
1920 return vf_refresh(vf);
1923 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1925 ViewFile *vf = data;
1927 file_data_unregister_notify_func(vf_notify_cb, vf);
1929 vflist_select_idle_cancel(vf);
1930 vf_refresh_idle_cancel(vf);
1931 vflist_thumb_stop(vf);
1933 filelist_free(vf->list);
1936 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1938 GtkTreeStore *store;
1939 GtkTreeSelection *selection;
1940 GType flist_types[FILE_COLUMN_COUNT];
1944 vf->info = g_new0(ViewFileInfoList, 1);
1946 VFLIST(vf)->select_idle_id = -1;
1948 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1949 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1950 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1951 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1952 flist_types[FILE_COLUMN_MULTILINE] = G_TYPE_STRING;
1953 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1954 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1955 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1956 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1957 flist_types[i] = G_TYPE_BOOLEAN;
1959 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
1961 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1962 g_object_unref(store);
1964 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1965 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1966 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
1968 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
1969 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
1973 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
1975 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
1976 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
1980 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
1981 g_assert(column == FILE_VIEW_COLUMN_THUMB);
1984 vflist_listview_add_column(vf, FILE_COLUMN_MULTILINE, _("Name"), FALSE, FALSE, TRUE);
1985 g_assert(column == FILE_VIEW_COLUMN_MULTILINE);
1988 vflist_listview_add_column(vf, FILE_COLUMN_NAME, _("Name"), FALSE, FALSE, TRUE);
1989 g_assert(column == FILE_VIEW_COLUMN_NAME);
1992 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
1993 g_assert(column == FILE_VIEW_COLUMN_SIZE);
1996 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
1997 g_assert(column == FILE_VIEW_COLUMN_DATE);
2000 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2004 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2006 if (VFLIST(vf)->thumbs_enabled == enable) return;
2008 VFLIST(vf)->thumbs_enabled = enable;
2009 if (vf->layout) vf_refresh(vf);
2012 void vflist_marks_set(ViewFile *vf, gboolean enable)
2014 GList *columns, *work;
2016 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2021 GtkTreeViewColumn *column = work->data;
2022 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2025 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2026 gtk_tree_view_column_set_visible(column, enable);
2029 g_list_free(columns);
2033 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */