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 = 0;
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 = 0;
737 static void vflist_select_idle_cancel(ViewFile *vf)
739 if (VFLIST(vf)->select_idle_id)
741 g_source_remove(VFLIST(vf)->select_idle_id);
742 VFLIST(vf)->select_idle_id = 0;
746 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
747 gboolean path_currently_selected, gpointer data)
752 if (!path_currently_selected &&
753 gtk_tree_model_get_iter(store, &iter, tpath))
755 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
759 VFLIST(vf)->select_fd = NULL;
763 !VFLIST(vf)->select_idle_id)
765 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
772 *-----------------------------------------------------------------------------
774 *-----------------------------------------------------------------------------
778 static gboolean vflist_dummy_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
779 gboolean path_currently_selected, gpointer data)
785 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
788 gchar *sidecars = NULL;
789 gchar *name_sidecars;
791 const gchar *time = text_from_time(fd->date);
792 gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
795 if (fd->sidecar_files)
797 sidecars = file_data_sc_list_to_string(fd);
798 name_sidecars = g_strdup_printf("%s%s %s", link, fd->name, sidecars);
802 name_sidecars = g_strdup_printf("%s%s", link, fd->name);
804 size = text_from_size(fd->size);
806 multiline = g_strdup_printf("%s\n%s\n%s", name_sidecars, size, time);
808 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
809 FILE_COLUMN_VERSION, fd->version,
810 FILE_COLUMN_THUMB, fd->thumb_pixbuf,
811 FILE_COLUMN_MULTILINE, multiline,
812 FILE_COLUMN_NAME, name_sidecars,
813 FILE_COLUMN_SIZE, size,
814 FILE_COLUMN_DATE, time,
815 #define STORE_SET_IS_SLOW 1
816 #if STORE_SET_IS_SLOW
817 /* this is 3x faster on a directory with 20000 files */
818 FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
819 FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
820 FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
821 FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
822 FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
823 FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
824 #if FILEDATA_MARKS_SIZE != 6
825 #error this needs to be updated
828 FILE_COLUMN_COLOR, FALSE, -1);
830 #if !STORE_SET_IS_SLOW
833 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
834 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
839 g_free(name_sidecars);
843 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected)
848 gint num_ordered = 0;
849 gint num_prepended = 0;
851 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
857 FileData *fd = work->data;
858 gboolean done = FALSE;
862 FileData *old_fd = NULL;
863 gint old_version = 0;
868 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
869 FILE_COLUMN_POINTER, &old_fd,
870 FILE_COLUMN_VERSION, &old_version,
880 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
882 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
884 if (match == 0) g_warning("multiple fd for the same path");
899 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
904 here should be used gtk_tree_store_append, but this function seems to be O(n)
905 and it seems to be much faster to add new entries to the beginning and reorder later
908 gtk_tree_store_prepend(store, &new, parent_iter);
911 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
912 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected);
914 if (g_list_find(selected, fd))
916 /* renamed files - the same fd appears at different position - select it again*/
917 GtkTreeSelection *selection;
918 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
919 gtk_tree_selection_select_iter(selection, &new);
926 file_data_unref(old_fd);
927 valid = gtk_tree_store_remove(store, &iter);
931 if (fd->version != old_version)
933 vflist_setup_iter(vf, store, &iter, fd);
934 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected);
937 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
948 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
949 file_data_unref(old_fd);
951 valid = gtk_tree_store_remove(store, &iter);
954 /* move the prepended entries to the correct position */
958 gint num_total = num_prepended + num_ordered;
959 gint *new_order = g_malloc(num_total * sizeof(gint));
961 for (i = 0; i < num_total; i++)
964 new_order[i] = num_prepended + i;
966 new_order[i] = num_total - 1 - i;
968 gtk_tree_store_reorder(store, parent_iter, new_order);
974 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
977 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
982 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
983 if (!vf->list) return;
989 FileData *fd = work->data;
990 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
995 vf->sort_method = type;
996 vf->sort_ascend = ascend;
998 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1000 new_order = g_malloc(i * sizeof(gint));
1006 FileData *fd = work->data;
1007 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1012 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1013 gtk_tree_store_reorder(store, NULL, new_order);
1016 g_hash_table_destroy(fd_idx_hash);
1020 *-----------------------------------------------------------------------------
1022 *-----------------------------------------------------------------------------
1025 static gboolean vflist_thumb_next(ViewFile *vf);
1027 static void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1032 FileData *fd = work->data;
1035 if (fd->thumb_pixbuf) (*done)++;
1037 if (fd->sidecar_files)
1039 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1045 static gdouble vflist_thumb_progress(ViewFile *vf)
1050 vflist_thumb_progress_count(vf->list, &count, &done);
1052 DEBUG_1("thumb progress: %d of %d", done, count);
1053 return (gdouble)done / count;
1057 static void vflist_thumb_status(ViewFile *vf, gdouble val, const gchar *text)
1059 if (vf->func_thumb_status)
1061 vf->func_thumb_status(vf, val, text, vf->data_thumb_status);
1065 static void vflist_thumb_cleanup(ViewFile *vf)
1067 vflist_thumb_status(vf, 0.0, NULL);
1069 vf->thumbs_running = FALSE;
1071 thumb_loader_free(vf->thumbs_loader);
1072 vf->thumbs_loader = NULL;
1074 vf->thumbs_filedata = NULL;
1077 static void vflist_thumb_stop(ViewFile *vf)
1079 if (vf->thumbs_running) vflist_thumb_cleanup(vf);
1082 static void vflist_thumb_do(ViewFile *vf, ThumbLoader *tl, FileData *fd)
1084 GtkTreeStore *store;
1087 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1089 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1090 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1092 vflist_thumb_status(vf, vflist_thumb_progress(vf), _("Loading thumbs..."));
1095 static void vflist_thumb_error_cb(ThumbLoader *tl, gpointer data)
1097 ViewFile *vf = data;
1099 if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1101 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
1104 while (vflist_thumb_next(vf));
1107 static void vflist_thumb_done_cb(ThumbLoader *tl, gpointer data)
1109 ViewFile *vf = data;
1111 if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1113 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
1116 while (vflist_thumb_next(vf));
1119 static gboolean vflist_thumb_next(ViewFile *vf)
1122 FileData *fd = NULL;
1124 /* first check the visible files */
1126 if (GTK_WIDGET_REALIZED(vf->listview) &&
1127 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1129 GtkTreeModel *store;
1131 gboolean valid = TRUE;
1133 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1134 gtk_tree_model_get_iter(store, &iter, tpath);
1135 gtk_tree_path_free(tpath);
1137 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1141 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1143 if (!nfd->thumb_pixbuf) fd = nfd;
1145 valid = gtk_tree_model_iter_next(store, &iter);
1149 /* then find first undone */
1153 GList *work = vf->list;
1156 FileData *fd_p = work->data;
1157 if (!fd_p->thumb_pixbuf)
1161 GList *work2 = fd_p->sidecar_files;
1163 while (work2 && !fd)
1166 if (!fd_p->thumb_pixbuf) fd = fd_p;
1167 work2 = work2->next;
1177 vflist_thumb_cleanup(vf);
1181 vf->thumbs_filedata = fd;
1183 thumb_loader_free(vf->thumbs_loader);
1185 vf->thumbs_loader = thumb_loader_new(options->thumbnails.max_width, options->thumbnails.max_height);
1186 thumb_loader_set_callbacks(vf->thumbs_loader,
1187 vflist_thumb_done_cb,
1188 vflist_thumb_error_cb,
1192 if (!thumb_loader_start(vf->thumbs_loader, fd))
1194 /* set icon to unknown, continue */
1195 DEBUG_1("thumb loader start failed %s", fd->path);
1196 vflist_thumb_do(vf, vf->thumbs_loader, fd);
1204 void vflist_thumb_update(ViewFile *vf)
1206 vflist_thumb_stop(vf);
1207 if (!VFLIST(vf)->thumbs_enabled) return;
1209 vflist_thumb_status(vf, 0.0, _("Loading thumbs..."));
1210 vf->thumbs_running = TRUE;
1212 if (thumb_format_changed)
1214 GList *work = vf->list;
1217 FileData *fd = work->data;
1218 if (fd->thumb_pixbuf)
1220 g_object_unref(fd->thumb_pixbuf);
1221 fd->thumb_pixbuf = NULL;
1226 thumb_format_changed = FALSE;
1229 while (vflist_thumb_next(vf));
1233 *-----------------------------------------------------------------------------
1235 *-----------------------------------------------------------------------------
1238 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1240 return g_list_nth_data(vf->list, row);
1243 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1246 GList *work, *work2;
1251 FileData *list_fd = work->data;
1252 if (list_fd == fd) return p;
1254 work2 = list_fd->sidecar_files;
1257 /* FIXME: return the same index also for sidecars
1258 it is sufficient for next/prev navigation but it should be rewritten
1259 without using indexes at all
1261 FileData *sidecar_fd = work2->data;
1262 if (sidecar_fd == fd) return p;
1263 work2 = work2->next;
1273 guint vflist_count(ViewFile *vf, gint64 *bytes)
1283 FileData *fd = work->data;
1291 return g_list_length(vf->list);
1294 GList *vflist_get_list(ViewFile *vf)
1302 FileData *fd = work->data;
1305 list = g_list_prepend(list, file_data_ref(fd));
1308 return g_list_reverse(list);
1312 *-----------------------------------------------------------------------------
1314 *-----------------------------------------------------------------------------
1317 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1319 GtkTreeModel *store;
1320 GtkTreeSelection *selection;
1323 gboolean found = FALSE;
1325 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1326 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1328 while (!found && work)
1330 GtkTreePath *tpath = work->data;
1334 gtk_tree_model_get_iter(store, &iter, tpath);
1335 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1336 if (fd_n == fd) found = TRUE;
1339 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1345 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1349 fd = vf_index_get_data(vf, row);
1350 return vflist_row_is_selected(vf, fd);
1353 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1355 GtkTreeModel *store;
1356 GtkTreeSelection *selection;
1360 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1361 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1371 GtkTreePath *tpath = work->data;
1375 gtk_tree_model_get_iter(store, &iter, tpath);
1376 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1385 count = g_list_length(slist);
1386 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1392 GList *vflist_selection_get_list(ViewFile *vf)
1394 GtkTreeModel *store;
1395 GtkTreeSelection *selection;
1400 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1401 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1405 GtkTreePath *tpath = work->data;
1409 gtk_tree_model_get_iter(store, &iter, tpath);
1410 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1412 list = g_list_prepend(list, file_data_ref(fd));
1416 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1419 return g_list_reverse(list);
1422 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1424 GtkTreeModel *store;
1425 GtkTreeSelection *selection;
1430 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1431 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1435 GtkTreePath *tpath = work->data;
1439 gtk_tree_model_get_iter(store, &iter, tpath);
1440 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1442 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1446 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1449 return g_list_reverse(list);
1452 void vflist_select_all(ViewFile *vf)
1454 GtkTreeSelection *selection;
1456 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1457 gtk_tree_selection_select_all(selection);
1459 VFLIST(vf)->select_fd = NULL;
1462 void vflist_select_none(ViewFile *vf)
1464 GtkTreeSelection *selection;
1466 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1467 gtk_tree_selection_unselect_all(selection);
1470 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1475 tpath = gtk_tree_model_get_path(store, iter);
1476 result = gtk_tree_path_prev(tpath);
1478 gtk_tree_model_get_iter(store, iter, tpath);
1480 gtk_tree_path_free(tpath);
1485 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1487 if (!gtk_tree_model_get_iter_first(store, iter))
1492 GtkTreeIter next = *iter;
1494 if (gtk_tree_model_iter_next(store, &next))
1503 void vflist_select_invert(ViewFile *vf)
1506 GtkTreeSelection *selection;
1507 GtkTreeModel *store;
1510 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1511 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1513 /* Backward iteration prevents scrolling to the end of the list,
1514 * it scrolls to the first selected row instead. */
1515 valid = tree_model_get_iter_last(store, &iter);
1519 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1522 gtk_tree_selection_unselect_iter(selection, &iter);
1524 gtk_tree_selection_select_iter(selection, &iter);
1526 valid = tree_model_iter_prev(store, &iter);
1530 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1534 if (vflist_find_row(vf, fd, &iter) < 0) return;
1536 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1538 if (!vflist_row_is_selected(vf, fd))
1540 GtkTreeSelection *selection;
1541 GtkTreeModel *store;
1544 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1545 gtk_tree_selection_unselect_all(selection);
1546 gtk_tree_selection_select_iter(selection, &iter);
1547 vflist_move_cursor(vf, &iter);
1549 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1550 tpath = gtk_tree_model_get_path(store, &iter);
1551 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1552 gtk_tree_path_free(tpath);
1556 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1559 FileData *fd = NULL;
1561 if (sel_fd->parent) sel_fd = sel_fd->parent;
1570 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1572 if (match >= 0) break;
1575 if (fd) vflist_select_by_fd(vf, fd);
1579 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1581 GtkTreeModel *store;
1583 GtkTreeSelection *selection;
1587 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1589 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1590 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1592 valid = gtk_tree_model_get_iter_first(store, &iter);
1596 gboolean mark_val, selected;
1597 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1599 mark_val = file_data_get_mark(fd, n);
1600 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1604 case MTS_MODE_SET: selected = mark_val;
1606 case MTS_MODE_OR: selected = mark_val | selected;
1608 case MTS_MODE_AND: selected = mark_val & selected;
1610 case MTS_MODE_MINUS: selected = !mark_val & selected;
1615 gtk_tree_selection_select_iter(selection, &iter);
1617 gtk_tree_selection_unselect_iter(selection, &iter);
1619 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1623 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1625 GtkTreeModel *store;
1626 GtkTreeSelection *selection;
1631 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1633 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1634 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1638 GtkTreePath *tpath = work->data;
1642 gtk_tree_model_get_iter(store, &iter, tpath);
1643 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1645 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1649 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1651 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1653 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1657 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1659 vf_refresh_idle(vf);
1663 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1665 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_MARKS + n, file_data_get_mark(fd, n), -1);
1669 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1674 *-----------------------------------------------------------------------------
1676 *-----------------------------------------------------------------------------
1679 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb)
1681 GtkTreeViewColumn *column;
1682 GtkCellRenderer *cell;
1686 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1687 if (!column) return;
1689 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1691 list = gtk_tree_view_column_get_cell_renderers(column);
1696 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1697 gtk_tree_view_column_set_visible(column, thumb);
1699 multiline = (thumb && options->thumbnails.max_height >= 48);
1701 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_MULTILINE);
1702 if (!column) return;
1703 gtk_tree_view_column_set_visible(column, multiline);
1704 if (multiline) gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1706 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_NAME);
1707 if (!column) return;
1708 gtk_tree_view_column_set_visible(column, !multiline);
1709 if (!multiline) gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1711 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1712 if (!column) return;
1713 gtk_tree_view_column_set_visible(column, !multiline);
1715 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1716 if (!column) return;
1717 gtk_tree_view_column_set_visible(column, !multiline);
1719 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1722 static void vflist_populate_view(ViewFile *vf)
1724 GtkTreeStore *store;
1725 gboolean thumbs_enabled;
1728 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1729 thumbs_enabled = VFLIST(vf)->thumbs_enabled;
1731 vflist_thumb_stop(vf);
1735 vflist_store_clear(vf);
1740 vflist_listview_set_columns(vf->listview, thumbs_enabled);
1742 selected = vflist_selection_get_list(vf);
1744 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected);
1746 if (selected && vflist_selection_count(vf, NULL) == 0)
1748 /* all selected files disappeared */
1749 vflist_select_closest(vf, selected->data);
1752 filelist_free(selected);
1755 vflist_thumb_update(vf);
1758 gboolean vflist_refresh(ViewFile *vf)
1761 gboolean ret = TRUE;
1763 old_list = vf->list;
1766 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1769 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1771 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1772 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1773 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1775 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1776 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1779 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1781 vflist_populate_view(vf);
1783 filelist_free(old_list);
1784 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1791 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1793 #define CELL_HEIGHT_OVERRIDE 512
1795 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1799 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1800 if (spec && G_IS_PARAM_SPEC_INT(spec))
1802 GParamSpecInt *spec_int;
1804 spec_int = G_PARAM_SPEC_INT(spec);
1805 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1809 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1811 static GdkColor color;
1812 static GtkWidget *done = NULL;
1818 style = gtk_widget_get_style(widget);
1819 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1820 shift_color(&color, -1, 0);
1827 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1828 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1830 ViewFile *vf = data;
1833 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1834 g_object_set(G_OBJECT(cell),
1835 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1836 "cell-background-set", set, NULL);
1839 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1841 GtkTreeViewColumn *column;
1842 GtkCellRenderer *renderer;
1844 column = gtk_tree_view_column_new();
1845 gtk_tree_view_column_set_title(column, title);
1846 gtk_tree_view_column_set_min_width(column, 4);
1850 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1851 renderer = gtk_cell_renderer_text_new();
1854 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1856 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1857 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1859 gtk_tree_view_column_set_expand(column, TRUE);
1863 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1864 renderer = gtk_cell_renderer_pixbuf_new();
1865 cell_renderer_height_override(renderer);
1866 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1867 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1870 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1871 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1872 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1874 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1877 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1879 ViewFile *vf = data;
1880 GtkTreeStore *store;
1881 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1887 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1888 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1891 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1893 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1895 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1897 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1898 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1899 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1901 vf_refresh_idle(vf);
1903 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1905 gtk_tree_store_set(store, &iter, col_idx, marked, -1);
1906 gtk_tree_path_free(path);
1909 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1911 GtkTreeViewColumn *column;
1912 GtkCellRenderer *renderer;
1913 GtkTreeStore *store;
1916 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1918 renderer = gtk_cell_renderer_toggle_new();
1919 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1921 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1922 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1923 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1925 index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1926 gtk_tree_view_column_set_fixed_width(column, 22);
1927 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1930 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1934 *-----------------------------------------------------------------------------
1936 *-----------------------------------------------------------------------------
1939 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1941 if (!dir_fd) return FALSE;
1942 if (vf->dir_fd == dir_fd) return TRUE;
1944 file_data_unref(vf->dir_fd);
1945 vf->dir_fd = file_data_ref(dir_fd);
1947 /* force complete reload */
1948 vflist_store_clear(vf);
1950 filelist_free(vf->list);
1953 return vf_refresh(vf);
1956 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1958 ViewFile *vf = data;
1960 file_data_unregister_notify_func(vf_notify_cb, vf);
1962 vflist_select_idle_cancel(vf);
1963 vf_refresh_idle_cancel(vf);
1964 vflist_thumb_stop(vf);
1966 filelist_free(vf->list);
1969 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1971 GtkTreeStore *store;
1972 GtkTreeSelection *selection;
1973 GType flist_types[FILE_COLUMN_COUNT];
1977 vf->info = g_new0(ViewFileInfoList, 1);
1979 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1980 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1981 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1982 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1983 flist_types[FILE_COLUMN_MULTILINE] = G_TYPE_STRING;
1984 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1985 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1986 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1987 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1988 flist_types[i] = G_TYPE_BOOLEAN;
1990 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
1992 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1993 g_object_unref(store);
1995 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1996 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1997 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
1999 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2000 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2004 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2006 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2007 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
2011 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2012 g_assert(column == FILE_VIEW_COLUMN_THUMB);
2015 vflist_listview_add_column(vf, FILE_COLUMN_MULTILINE, _("Name"), FALSE, FALSE, TRUE);
2016 g_assert(column == FILE_VIEW_COLUMN_MULTILINE);
2019 vflist_listview_add_column(vf, FILE_COLUMN_NAME, _("Name"), FALSE, FALSE, TRUE);
2020 g_assert(column == FILE_VIEW_COLUMN_NAME);
2023 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2024 g_assert(column == FILE_VIEW_COLUMN_SIZE);
2027 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2028 g_assert(column == FILE_VIEW_COLUMN_DATE);
2031 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2035 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2037 if (VFLIST(vf)->thumbs_enabled == enable) return;
2039 VFLIST(vf)->thumbs_enabled = enable;
2040 if (vf->layout) vf_refresh(vf);
2043 void vflist_marks_set(ViewFile *vf, gboolean enable)
2045 GList *columns, *work;
2047 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2052 GtkTreeViewColumn *column = work->data;
2053 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2056 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2057 gtk_tree_view_column_set_visible(column, enable);
2060 g_list_free(columns);
2064 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */