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_fd(ViewFile *vf, FileData *fd)
1223 GList *work, *work2;
1228 FileData *list_fd = work->data;
1229 if (list_fd == fd) return p;
1231 work2 = list_fd->sidecar_files;
1234 /* FIXME: return the same index also for sidecars
1235 it is sufficient for next/prev navigation but it should be rewritten
1236 without using indexes at all
1238 FileData *sidecar_fd = work2->data;
1239 if (sidecar_fd == fd) return p;
1240 work2 = work2->next;
1250 guint vflist_count(ViewFile *vf, gint64 *bytes)
1260 FileData *fd = work->data;
1268 return g_list_length(vf->list);
1271 GList *vflist_get_list(ViewFile *vf)
1279 FileData *fd = work->data;
1282 list = g_list_prepend(list, file_data_ref(fd));
1285 return g_list_reverse(list);
1289 *-----------------------------------------------------------------------------
1291 *-----------------------------------------------------------------------------
1294 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1296 GtkTreeModel *store;
1297 GtkTreeSelection *selection;
1300 gboolean found = FALSE;
1302 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1303 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1305 while (!found && work)
1307 GtkTreePath *tpath = work->data;
1311 gtk_tree_model_get_iter(store, &iter, tpath);
1312 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1313 if (fd_n == fd) found = TRUE;
1316 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1322 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1326 fd = vf_index_get_data(vf, row);
1327 return vflist_row_is_selected(vf, fd);
1330 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1332 GtkTreeModel *store;
1333 GtkTreeSelection *selection;
1337 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1338 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1348 GtkTreePath *tpath = work->data;
1352 gtk_tree_model_get_iter(store, &iter, tpath);
1353 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1362 count = g_list_length(slist);
1363 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1369 GList *vflist_selection_get_list(ViewFile *vf)
1371 GtkTreeModel *store;
1372 GtkTreeSelection *selection;
1377 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1378 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1382 GtkTreePath *tpath = work->data;
1386 gtk_tree_model_get_iter(store, &iter, tpath);
1387 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1389 list = g_list_prepend(list, file_data_ref(fd));
1393 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1396 return g_list_reverse(list);
1399 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1401 GtkTreeModel *store;
1402 GtkTreeSelection *selection;
1407 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1408 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1412 GtkTreePath *tpath = work->data;
1416 gtk_tree_model_get_iter(store, &iter, tpath);
1417 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1419 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1423 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1426 return g_list_reverse(list);
1429 void vflist_select_all(ViewFile *vf)
1431 GtkTreeSelection *selection;
1433 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1434 gtk_tree_selection_select_all(selection);
1436 VFLIST(vf)->select_fd = NULL;
1439 void vflist_select_none(ViewFile *vf)
1441 GtkTreeSelection *selection;
1443 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1444 gtk_tree_selection_unselect_all(selection);
1447 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1452 tpath = gtk_tree_model_get_path(store, iter);
1453 result = gtk_tree_path_prev(tpath);
1455 gtk_tree_model_get_iter(store, iter, tpath);
1457 gtk_tree_path_free(tpath);
1462 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1464 if (!gtk_tree_model_get_iter_first(store, iter))
1469 GtkTreeIter next = *iter;
1471 if (gtk_tree_model_iter_next(store, &next))
1480 void vflist_select_invert(ViewFile *vf)
1483 GtkTreeSelection *selection;
1484 GtkTreeModel *store;
1487 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1488 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1490 /* Backward iteration prevents scrolling to the end of the list,
1491 * it scrolls to the first selected row instead. */
1492 valid = tree_model_get_iter_last(store, &iter);
1496 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1499 gtk_tree_selection_unselect_iter(selection, &iter);
1501 gtk_tree_selection_select_iter(selection, &iter);
1503 valid = tree_model_iter_prev(store, &iter);
1507 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1511 if (vflist_find_row(vf, fd, &iter) < 0) return;
1513 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1515 if (!vflist_row_is_selected(vf, fd))
1517 GtkTreeSelection *selection;
1518 GtkTreeModel *store;
1521 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1522 gtk_tree_selection_unselect_all(selection);
1523 gtk_tree_selection_select_iter(selection, &iter);
1524 vflist_move_cursor(vf, &iter);
1526 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1527 tpath = gtk_tree_model_get_path(store, &iter);
1528 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1529 gtk_tree_path_free(tpath);
1533 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1536 FileData *fd = NULL;
1538 if (sel_fd->parent) sel_fd = sel_fd->parent;
1547 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1549 if (match >= 0) break;
1552 if (fd) vflist_select_by_fd(vf, fd);
1556 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1558 GtkTreeModel *store;
1560 GtkTreeSelection *selection;
1564 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1566 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1567 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1569 valid = gtk_tree_model_get_iter_first(store, &iter);
1573 gboolean mark_val, selected;
1574 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1576 mark_val = file_data_get_mark(fd, n);
1577 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1581 case MTS_MODE_SET: selected = mark_val;
1583 case MTS_MODE_OR: selected = mark_val | selected;
1585 case MTS_MODE_AND: selected = mark_val & selected;
1587 case MTS_MODE_MINUS: selected = !mark_val & selected;
1592 gtk_tree_selection_select_iter(selection, &iter);
1594 gtk_tree_selection_unselect_iter(selection, &iter);
1596 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1600 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1602 GtkTreeModel *store;
1603 GtkTreeSelection *selection;
1608 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1610 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1611 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1615 GtkTreePath *tpath = work->data;
1619 gtk_tree_model_get_iter(store, &iter, tpath);
1620 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1622 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1626 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1628 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1630 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1634 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1636 vf_refresh_idle(vf);
1640 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1642 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_MARKS + n, file_data_get_mark(fd, n), -1);
1646 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1651 *-----------------------------------------------------------------------------
1653 *-----------------------------------------------------------------------------
1656 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb)
1658 GtkTreeViewColumn *column;
1659 GtkCellRenderer *cell;
1663 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1664 if (!column) return;
1666 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1668 list = gtk_tree_view_column_get_cell_renderers(column);
1673 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1674 gtk_tree_view_column_set_visible(column, thumb);
1676 multiline = (thumb && options->thumbnails.max_height >= 48);
1678 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_MULTILINE);
1679 if (!column) return;
1680 gtk_tree_view_column_set_visible(column, multiline);
1681 if (multiline) gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1683 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_NAME);
1684 if (!column) return;
1685 gtk_tree_view_column_set_visible(column, !multiline);
1686 if (!multiline) gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1688 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1689 if (!column) return;
1690 gtk_tree_view_column_set_visible(column, !multiline);
1692 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1693 if (!column) return;
1694 gtk_tree_view_column_set_visible(column, !multiline);
1696 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1699 static void vflist_populate_view(ViewFile *vf)
1701 GtkTreeStore *store;
1702 gboolean thumbs_enabled;
1705 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1706 thumbs_enabled = VFLIST(vf)->thumbs_enabled;
1708 vflist_thumb_stop(vf);
1712 vflist_store_clear(vf);
1717 vflist_listview_set_columns(vf->listview, thumbs_enabled);
1719 selected = vflist_selection_get_list(vf);
1721 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected);
1723 if (selected && vflist_selection_count(vf, NULL) == 0)
1725 /* all selected files disappeared */
1726 vflist_select_closest(vf, selected->data);
1729 filelist_free(selected);
1732 vflist_thumb_update(vf);
1735 gboolean vflist_refresh(ViewFile *vf)
1738 gboolean ret = TRUE;
1740 old_list = vf->list;
1743 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1746 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1748 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1749 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1750 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1752 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1753 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1756 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1758 vflist_populate_view(vf);
1760 filelist_free(old_list);
1761 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1768 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1770 #define CELL_HEIGHT_OVERRIDE 512
1772 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1776 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1777 if (spec && G_IS_PARAM_SPEC_INT(spec))
1779 GParamSpecInt *spec_int;
1781 spec_int = G_PARAM_SPEC_INT(spec);
1782 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1786 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1788 static GdkColor color;
1789 static GtkWidget *done = NULL;
1795 style = gtk_widget_get_style(widget);
1796 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1797 shift_color(&color, -1, 0);
1804 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1805 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1807 ViewFile *vf = data;
1810 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1811 g_object_set(G_OBJECT(cell),
1812 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1813 "cell-background-set", set, NULL);
1816 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1818 GtkTreeViewColumn *column;
1819 GtkCellRenderer *renderer;
1821 column = gtk_tree_view_column_new();
1822 gtk_tree_view_column_set_title(column, title);
1823 gtk_tree_view_column_set_min_width(column, 4);
1827 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1828 renderer = gtk_cell_renderer_text_new();
1831 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1833 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1834 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1836 gtk_tree_view_column_set_expand(column, TRUE);
1840 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1841 renderer = gtk_cell_renderer_pixbuf_new();
1842 cell_renderer_height_override(renderer);
1843 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1844 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1847 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1848 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1849 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1851 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1854 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1856 ViewFile *vf = data;
1857 GtkTreeStore *store;
1858 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1864 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1865 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1868 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1870 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1872 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1874 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1875 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1876 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1878 vf_refresh_idle(vf);
1880 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1882 gtk_tree_store_set(store, &iter, col_idx, marked, -1);
1883 gtk_tree_path_free(path);
1886 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1888 GtkTreeViewColumn *column;
1889 GtkCellRenderer *renderer;
1890 GtkTreeStore *store;
1893 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1895 renderer = gtk_cell_renderer_toggle_new();
1896 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1898 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1899 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1900 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1902 index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1903 gtk_tree_view_column_set_fixed_width(column, 22);
1904 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1907 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1911 *-----------------------------------------------------------------------------
1913 *-----------------------------------------------------------------------------
1916 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1918 if (!dir_fd) return FALSE;
1919 if (vf->dir_fd == dir_fd) return TRUE;
1921 file_data_unref(vf->dir_fd);
1922 vf->dir_fd = file_data_ref(dir_fd);
1924 /* force complete reload */
1925 vflist_store_clear(vf);
1927 filelist_free(vf->list);
1930 return vf_refresh(vf);
1933 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1935 ViewFile *vf = data;
1937 file_data_unregister_notify_func(vf_notify_cb, vf);
1939 vflist_select_idle_cancel(vf);
1940 vf_refresh_idle_cancel(vf);
1941 vflist_thumb_stop(vf);
1943 filelist_free(vf->list);
1946 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1948 GtkTreeStore *store;
1949 GtkTreeSelection *selection;
1950 GType flist_types[FILE_COLUMN_COUNT];
1954 vf->info = g_new0(ViewFileInfoList, 1);
1956 VFLIST(vf)->select_idle_id = -1;
1958 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1959 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1960 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1961 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1962 flist_types[FILE_COLUMN_MULTILINE] = G_TYPE_STRING;
1963 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1964 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1965 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1966 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1967 flist_types[i] = G_TYPE_BOOLEAN;
1969 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
1971 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1972 g_object_unref(store);
1974 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1975 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1976 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
1978 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
1979 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
1983 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
1985 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
1986 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
1990 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
1991 g_assert(column == FILE_VIEW_COLUMN_THUMB);
1994 vflist_listview_add_column(vf, FILE_COLUMN_MULTILINE, _("Name"), FALSE, FALSE, TRUE);
1995 g_assert(column == FILE_VIEW_COLUMN_MULTILINE);
1998 vflist_listview_add_column(vf, FILE_COLUMN_NAME, _("Name"), FALSE, FALSE, TRUE);
1999 g_assert(column == FILE_VIEW_COLUMN_NAME);
2002 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2003 g_assert(column == FILE_VIEW_COLUMN_SIZE);
2006 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2007 g_assert(column == FILE_VIEW_COLUMN_DATE);
2010 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2014 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2016 if (VFLIST(vf)->thumbs_enabled == enable) return;
2018 VFLIST(vf)->thumbs_enabled = enable;
2019 if (vf->layout) vf_refresh(vf);
2022 void vflist_marks_set(ViewFile *vf, gboolean enable)
2024 GList *columns, *work;
2026 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2031 GtkTreeViewColumn *column = work->data;
2032 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2035 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2036 gtk_tree_view_column_set_visible(column, enable);
2039 g_list_free(columns);
2043 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */