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)
1138 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1140 if (!nfd->thumb_pixbuf) fd = nfd;
1142 valid = gtk_tree_model_iter_next(store, &iter);
1146 /* then find first undone */
1150 GList *work = vf->list;
1153 FileData *fd_p = work->data;
1154 if (!fd_p->thumb_pixbuf)
1158 GList *work2 = fd_p->sidecar_files;
1160 while (work2 && !fd)
1163 if (!fd_p->thumb_pixbuf) fd = fd_p;
1164 work2 = work2->next;
1174 vflist_thumb_cleanup(vf);
1178 vf->thumbs_filedata = fd;
1180 thumb_loader_free(vf->thumbs_loader);
1182 vf->thumbs_loader = thumb_loader_new(options->thumbnails.max_width, options->thumbnails.max_height);
1183 thumb_loader_set_callbacks(vf->thumbs_loader,
1184 vflist_thumb_done_cb,
1185 vflist_thumb_error_cb,
1189 if (!thumb_loader_start(vf->thumbs_loader, fd))
1191 /* set icon to unknown, continue */
1192 DEBUG_1("thumb loader start failed %s", fd->path);
1193 vflist_thumb_do(vf, vf->thumbs_loader, fd);
1201 void vflist_thumb_update(ViewFile *vf)
1203 vflist_thumb_stop(vf);
1204 if (!VFLIST(vf)->thumbs_enabled) return;
1206 vflist_thumb_status(vf, 0.0, _("Loading thumbs..."));
1207 vf->thumbs_running = TRUE;
1209 if (thumb_format_changed)
1211 GList *work = vf->list;
1214 FileData *fd = work->data;
1215 if (fd->thumb_pixbuf)
1217 g_object_unref(fd->thumb_pixbuf);
1218 fd->thumb_pixbuf = NULL;
1223 thumb_format_changed = FALSE;
1226 while (vflist_thumb_next(vf));
1230 *-----------------------------------------------------------------------------
1232 *-----------------------------------------------------------------------------
1235 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1237 return g_list_nth_data(vf->list, row);
1240 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1243 GList *work, *work2;
1248 FileData *list_fd = work->data;
1249 if (list_fd == fd) return p;
1251 work2 = list_fd->sidecar_files;
1254 /* FIXME: return the same index also for sidecars
1255 it is sufficient for next/prev navigation but it should be rewritten
1256 without using indexes at all
1258 FileData *sidecar_fd = work2->data;
1259 if (sidecar_fd == fd) return p;
1260 work2 = work2->next;
1270 guint vflist_count(ViewFile *vf, gint64 *bytes)
1280 FileData *fd = work->data;
1288 return g_list_length(vf->list);
1291 GList *vflist_get_list(ViewFile *vf)
1299 FileData *fd = work->data;
1302 list = g_list_prepend(list, file_data_ref(fd));
1305 return g_list_reverse(list);
1309 *-----------------------------------------------------------------------------
1311 *-----------------------------------------------------------------------------
1314 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1316 GtkTreeModel *store;
1317 GtkTreeSelection *selection;
1320 gboolean found = FALSE;
1322 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1323 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1325 while (!found && work)
1327 GtkTreePath *tpath = work->data;
1331 gtk_tree_model_get_iter(store, &iter, tpath);
1332 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1333 if (fd_n == fd) found = TRUE;
1336 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1342 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1346 fd = vf_index_get_data(vf, row);
1347 return vflist_row_is_selected(vf, fd);
1350 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1352 GtkTreeModel *store;
1353 GtkTreeSelection *selection;
1357 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1358 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1368 GtkTreePath *tpath = work->data;
1372 gtk_tree_model_get_iter(store, &iter, tpath);
1373 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1382 count = g_list_length(slist);
1383 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1389 GList *vflist_selection_get_list(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, file_data_ref(fd));
1413 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1416 return g_list_reverse(list);
1419 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1421 GtkTreeModel *store;
1422 GtkTreeSelection *selection;
1427 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1428 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1432 GtkTreePath *tpath = work->data;
1436 gtk_tree_model_get_iter(store, &iter, tpath);
1437 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1439 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1443 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1446 return g_list_reverse(list);
1449 void vflist_select_all(ViewFile *vf)
1451 GtkTreeSelection *selection;
1453 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1454 gtk_tree_selection_select_all(selection);
1456 VFLIST(vf)->select_fd = NULL;
1459 void vflist_select_none(ViewFile *vf)
1461 GtkTreeSelection *selection;
1463 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1464 gtk_tree_selection_unselect_all(selection);
1467 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1472 tpath = gtk_tree_model_get_path(store, iter);
1473 result = gtk_tree_path_prev(tpath);
1475 gtk_tree_model_get_iter(store, iter, tpath);
1477 gtk_tree_path_free(tpath);
1482 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1484 if (!gtk_tree_model_get_iter_first(store, iter))
1489 GtkTreeIter next = *iter;
1491 if (gtk_tree_model_iter_next(store, &next))
1500 void vflist_select_invert(ViewFile *vf)
1503 GtkTreeSelection *selection;
1504 GtkTreeModel *store;
1507 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1508 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1510 /* Backward iteration prevents scrolling to the end of the list,
1511 * it scrolls to the first selected row instead. */
1512 valid = tree_model_get_iter_last(store, &iter);
1516 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1519 gtk_tree_selection_unselect_iter(selection, &iter);
1521 gtk_tree_selection_select_iter(selection, &iter);
1523 valid = tree_model_iter_prev(store, &iter);
1527 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1531 if (vflist_find_row(vf, fd, &iter) < 0) return;
1533 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1535 if (!vflist_row_is_selected(vf, fd))
1537 GtkTreeSelection *selection;
1538 GtkTreeModel *store;
1541 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1542 gtk_tree_selection_unselect_all(selection);
1543 gtk_tree_selection_select_iter(selection, &iter);
1544 vflist_move_cursor(vf, &iter);
1546 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1547 tpath = gtk_tree_model_get_path(store, &iter);
1548 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1549 gtk_tree_path_free(tpath);
1553 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1556 FileData *fd = NULL;
1558 if (sel_fd->parent) sel_fd = sel_fd->parent;
1567 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1569 if (match >= 0) break;
1572 if (fd) vflist_select_by_fd(vf, fd);
1576 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1578 GtkTreeModel *store;
1580 GtkTreeSelection *selection;
1584 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1586 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1587 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1589 valid = gtk_tree_model_get_iter_first(store, &iter);
1593 gboolean mark_val, selected;
1594 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1596 mark_val = file_data_get_mark(fd, n);
1597 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1601 case MTS_MODE_SET: selected = mark_val;
1603 case MTS_MODE_OR: selected = mark_val | selected;
1605 case MTS_MODE_AND: selected = mark_val & selected;
1607 case MTS_MODE_MINUS: selected = !mark_val & selected;
1612 gtk_tree_selection_select_iter(selection, &iter);
1614 gtk_tree_selection_unselect_iter(selection, &iter);
1616 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1620 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1622 GtkTreeModel *store;
1623 GtkTreeSelection *selection;
1628 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1630 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1631 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1635 GtkTreePath *tpath = work->data;
1639 gtk_tree_model_get_iter(store, &iter, tpath);
1640 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1642 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1646 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1648 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1650 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1654 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1656 vf_refresh_idle(vf);
1660 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1662 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_MARKS + n, file_data_get_mark(fd, n), -1);
1666 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1671 *-----------------------------------------------------------------------------
1673 *-----------------------------------------------------------------------------
1676 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb)
1678 GtkTreeViewColumn *column;
1679 GtkCellRenderer *cell;
1683 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1684 if (!column) return;
1686 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1688 list = gtk_tree_view_column_get_cell_renderers(column);
1693 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1694 gtk_tree_view_column_set_visible(column, thumb);
1696 multiline = (thumb && options->thumbnails.max_height >= 48);
1698 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_MULTILINE);
1699 if (!column) return;
1700 gtk_tree_view_column_set_visible(column, multiline);
1701 if (multiline) gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1703 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_NAME);
1704 if (!column) return;
1705 gtk_tree_view_column_set_visible(column, !multiline);
1706 if (!multiline) gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1708 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1709 if (!column) return;
1710 gtk_tree_view_column_set_visible(column, !multiline);
1712 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1713 if (!column) return;
1714 gtk_tree_view_column_set_visible(column, !multiline);
1716 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1719 static void vflist_populate_view(ViewFile *vf)
1721 GtkTreeStore *store;
1722 gboolean thumbs_enabled;
1725 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1726 thumbs_enabled = VFLIST(vf)->thumbs_enabled;
1728 vflist_thumb_stop(vf);
1732 vflist_store_clear(vf);
1737 vflist_listview_set_columns(vf->listview, thumbs_enabled);
1739 selected = vflist_selection_get_list(vf);
1741 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected);
1743 if (selected && vflist_selection_count(vf, NULL) == 0)
1745 /* all selected files disappeared */
1746 vflist_select_closest(vf, selected->data);
1749 filelist_free(selected);
1752 vflist_thumb_update(vf);
1755 gboolean vflist_refresh(ViewFile *vf)
1758 gboolean ret = TRUE;
1760 old_list = vf->list;
1763 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1766 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1768 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1769 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1770 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1772 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1773 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1776 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1778 vflist_populate_view(vf);
1780 filelist_free(old_list);
1781 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1788 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1790 #define CELL_HEIGHT_OVERRIDE 512
1792 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1796 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1797 if (spec && G_IS_PARAM_SPEC_INT(spec))
1799 GParamSpecInt *spec_int;
1801 spec_int = G_PARAM_SPEC_INT(spec);
1802 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1806 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1808 static GdkColor color;
1809 static GtkWidget *done = NULL;
1815 style = gtk_widget_get_style(widget);
1816 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1817 shift_color(&color, -1, 0);
1824 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1825 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1827 ViewFile *vf = data;
1830 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1831 g_object_set(G_OBJECT(cell),
1832 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1833 "cell-background-set", set, NULL);
1836 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1838 GtkTreeViewColumn *column;
1839 GtkCellRenderer *renderer;
1841 column = gtk_tree_view_column_new();
1842 gtk_tree_view_column_set_title(column, title);
1843 gtk_tree_view_column_set_min_width(column, 4);
1847 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1848 renderer = gtk_cell_renderer_text_new();
1851 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1853 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1854 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1856 gtk_tree_view_column_set_expand(column, TRUE);
1860 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1861 renderer = gtk_cell_renderer_pixbuf_new();
1862 cell_renderer_height_override(renderer);
1863 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1864 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1867 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1868 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1869 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1871 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1874 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1876 ViewFile *vf = data;
1877 GtkTreeStore *store;
1878 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1884 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1885 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1888 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1890 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1892 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1894 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1895 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1896 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1898 vf_refresh_idle(vf);
1900 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1902 gtk_tree_store_set(store, &iter, col_idx, marked, -1);
1903 gtk_tree_path_free(path);
1906 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1908 GtkTreeViewColumn *column;
1909 GtkCellRenderer *renderer;
1910 GtkTreeStore *store;
1913 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1915 renderer = gtk_cell_renderer_toggle_new();
1916 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1918 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1919 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1920 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1922 index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1923 gtk_tree_view_column_set_fixed_width(column, 22);
1924 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1927 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1931 *-----------------------------------------------------------------------------
1933 *-----------------------------------------------------------------------------
1936 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1938 if (!dir_fd) return FALSE;
1939 if (vf->dir_fd == dir_fd) return TRUE;
1941 file_data_unref(vf->dir_fd);
1942 vf->dir_fd = file_data_ref(dir_fd);
1944 /* force complete reload */
1945 vflist_store_clear(vf);
1947 filelist_free(vf->list);
1950 return vf_refresh(vf);
1953 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1955 ViewFile *vf = data;
1957 file_data_unregister_notify_func(vf_notify_cb, vf);
1959 vflist_select_idle_cancel(vf);
1960 vf_refresh_idle_cancel(vf);
1961 vflist_thumb_stop(vf);
1963 filelist_free(vf->list);
1966 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1968 GtkTreeStore *store;
1969 GtkTreeSelection *selection;
1970 GType flist_types[FILE_COLUMN_COUNT];
1974 vf->info = g_new0(ViewFileInfoList, 1);
1976 VFLIST(vf)->select_idle_id = -1;
1978 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1979 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1980 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1981 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1982 flist_types[FILE_COLUMN_MULTILINE] = G_TYPE_STRING;
1983 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1984 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1985 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1986 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1987 flist_types[i] = G_TYPE_BOOLEAN;
1989 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
1991 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1992 g_object_unref(store);
1994 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1995 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1996 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
1998 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
1999 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2003 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2005 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2006 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
2010 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2011 g_assert(column == FILE_VIEW_COLUMN_THUMB);
2014 vflist_listview_add_column(vf, FILE_COLUMN_MULTILINE, _("Name"), FALSE, FALSE, TRUE);
2015 g_assert(column == FILE_VIEW_COLUMN_MULTILINE);
2018 vflist_listview_add_column(vf, FILE_COLUMN_NAME, _("Name"), FALSE, FALSE, TRUE);
2019 g_assert(column == FILE_VIEW_COLUMN_NAME);
2022 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2023 g_assert(column == FILE_VIEW_COLUMN_SIZE);
2026 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2027 g_assert(column == FILE_VIEW_COLUMN_DATE);
2030 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2034 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2036 if (VFLIST(vf)->thumbs_enabled == enable) return;
2038 VFLIST(vf)->thumbs_enabled = enable;
2039 if (vf->layout) vf_refresh(vf);
2042 void vflist_marks_set(ViewFile *vf, gboolean enable)
2044 GList *columns, *work;
2046 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2051 GtkTreeViewColumn *column = work->data;
2052 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2055 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2056 gtk_tree_view_column_set_visible(column, enable);
2059 g_list_free(columns);
2063 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */