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 gchar *disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
803 name_sidecars = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
805 size = text_from_size(fd->size);
807 multiline = g_strdup_printf("%s\n%s\n%s", name_sidecars, size, time);
809 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
810 FILE_COLUMN_VERSION, fd->version,
811 FILE_COLUMN_THUMB, fd->thumb_pixbuf,
812 FILE_COLUMN_MULTILINE, multiline,
813 FILE_COLUMN_NAME, name_sidecars,
814 FILE_COLUMN_SIZE, size,
815 FILE_COLUMN_DATE, time,
816 #define STORE_SET_IS_SLOW 1
817 #if STORE_SET_IS_SLOW
818 /* this is 3x faster on a directory with 20000 files */
819 FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
820 FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
821 FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
822 FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
823 FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
824 FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
825 #if FILEDATA_MARKS_SIZE != 6
826 #error this needs to be updated
829 FILE_COLUMN_COLOR, FALSE, -1);
831 #if !STORE_SET_IS_SLOW
834 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
835 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
840 g_free(name_sidecars);
844 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected)
849 gint num_ordered = 0;
850 gint num_prepended = 0;
852 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
858 FileData *fd = work->data;
859 gboolean done = FALSE;
863 FileData *old_fd = NULL;
864 gint old_version = 0;
869 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
870 FILE_COLUMN_POINTER, &old_fd,
871 FILE_COLUMN_VERSION, &old_version,
881 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
883 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
885 if (match == 0) g_warning("multiple fd for the same path");
900 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
905 here should be used gtk_tree_store_append, but this function seems to be O(n)
906 and it seems to be much faster to add new entries to the beginning and reorder later
909 gtk_tree_store_prepend(store, &new, parent_iter);
912 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
913 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected);
915 if (g_list_find(selected, fd))
917 /* renamed files - the same fd appears at different position - select it again*/
918 GtkTreeSelection *selection;
919 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
920 gtk_tree_selection_select_iter(selection, &new);
927 file_data_unref(old_fd);
928 valid = gtk_tree_store_remove(store, &iter);
932 if (fd->version != old_version)
934 vflist_setup_iter(vf, store, &iter, fd);
935 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected);
938 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
949 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
950 file_data_unref(old_fd);
952 valid = gtk_tree_store_remove(store, &iter);
955 /* move the prepended entries to the correct position */
959 gint num_total = num_prepended + num_ordered;
960 gint *new_order = g_malloc(num_total * sizeof(gint));
962 for (i = 0; i < num_total; i++)
965 new_order[i] = num_prepended + i;
967 new_order[i] = num_total - 1 - i;
969 gtk_tree_store_reorder(store, parent_iter, new_order);
975 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
978 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
983 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
984 if (!vf->list) return;
990 FileData *fd = work->data;
991 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
996 vf->sort_method = type;
997 vf->sort_ascend = ascend;
999 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1001 new_order = g_malloc(i * sizeof(gint));
1007 FileData *fd = work->data;
1008 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1013 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1014 gtk_tree_store_reorder(store, NULL, new_order);
1017 g_hash_table_destroy(fd_idx_hash);
1021 *-----------------------------------------------------------------------------
1023 *-----------------------------------------------------------------------------
1026 static gboolean vflist_thumb_next(ViewFile *vf);
1028 static void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1033 FileData *fd = work->data;
1036 if (fd->thumb_pixbuf) (*done)++;
1038 if (fd->sidecar_files)
1040 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1046 static gdouble vflist_thumb_progress(ViewFile *vf)
1051 vflist_thumb_progress_count(vf->list, &count, &done);
1053 DEBUG_1("thumb progress: %d of %d", done, count);
1054 return (gdouble)done / count;
1058 static void vflist_thumb_status(ViewFile *vf, gdouble val, const gchar *text)
1060 if (vf->func_thumb_status)
1062 vf->func_thumb_status(vf, val, text, vf->data_thumb_status);
1066 static void vflist_thumb_cleanup(ViewFile *vf)
1068 vflist_thumb_status(vf, 0.0, NULL);
1070 vf->thumbs_running = FALSE;
1072 thumb_loader_free(vf->thumbs_loader);
1073 vf->thumbs_loader = NULL;
1075 vf->thumbs_filedata = NULL;
1078 static void vflist_thumb_stop(ViewFile *vf)
1080 if (vf->thumbs_running) vflist_thumb_cleanup(vf);
1083 static void vflist_thumb_do(ViewFile *vf, ThumbLoader *tl, FileData *fd)
1085 GtkTreeStore *store;
1088 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1090 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1091 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1093 vflist_thumb_status(vf, vflist_thumb_progress(vf), _("Loading thumbs..."));
1096 static void vflist_thumb_error_cb(ThumbLoader *tl, gpointer data)
1098 ViewFile *vf = data;
1100 if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1102 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
1105 while (vflist_thumb_next(vf));
1108 static void vflist_thumb_done_cb(ThumbLoader *tl, gpointer data)
1110 ViewFile *vf = data;
1112 if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1114 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
1117 while (vflist_thumb_next(vf));
1120 static gboolean vflist_thumb_next(ViewFile *vf)
1123 FileData *fd = NULL;
1125 /* first check the visible files */
1127 if (GTK_WIDGET_REALIZED(vf->listview) &&
1128 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1130 GtkTreeModel *store;
1132 gboolean valid = TRUE;
1134 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1135 gtk_tree_model_get_iter(store, &iter, tpath);
1136 gtk_tree_path_free(tpath);
1138 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1142 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1144 if (!nfd->thumb_pixbuf) fd = nfd;
1146 valid = gtk_tree_model_iter_next(store, &iter);
1150 /* then find first undone */
1154 GList *work = vf->list;
1157 FileData *fd_p = work->data;
1158 if (!fd_p->thumb_pixbuf)
1162 GList *work2 = fd_p->sidecar_files;
1164 while (work2 && !fd)
1167 if (!fd_p->thumb_pixbuf) fd = fd_p;
1168 work2 = work2->next;
1178 vflist_thumb_cleanup(vf);
1182 vf->thumbs_filedata = fd;
1184 thumb_loader_free(vf->thumbs_loader);
1186 vf->thumbs_loader = thumb_loader_new(options->thumbnails.max_width, options->thumbnails.max_height);
1187 thumb_loader_set_callbacks(vf->thumbs_loader,
1188 vflist_thumb_done_cb,
1189 vflist_thumb_error_cb,
1193 if (!thumb_loader_start(vf->thumbs_loader, fd))
1195 /* set icon to unknown, continue */
1196 DEBUG_1("thumb loader start failed %s", fd->path);
1197 vflist_thumb_do(vf, vf->thumbs_loader, fd);
1205 void vflist_thumb_update(ViewFile *vf)
1207 vflist_thumb_stop(vf);
1208 if (!VFLIST(vf)->thumbs_enabled) return;
1210 vflist_thumb_status(vf, 0.0, _("Loading thumbs..."));
1211 vf->thumbs_running = TRUE;
1213 if (thumb_format_changed)
1215 GList *work = vf->list;
1218 FileData *fd = work->data;
1219 if (fd->thumb_pixbuf)
1221 g_object_unref(fd->thumb_pixbuf);
1222 fd->thumb_pixbuf = NULL;
1227 thumb_format_changed = FALSE;
1230 while (vflist_thumb_next(vf));
1234 *-----------------------------------------------------------------------------
1236 *-----------------------------------------------------------------------------
1239 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1241 return g_list_nth_data(vf->list, row);
1244 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1247 GList *work, *work2;
1252 FileData *list_fd = work->data;
1253 if (list_fd == fd) return p;
1255 work2 = list_fd->sidecar_files;
1258 /* FIXME: return the same index also for sidecars
1259 it is sufficient for next/prev navigation but it should be rewritten
1260 without using indexes at all
1262 FileData *sidecar_fd = work2->data;
1263 if (sidecar_fd == fd) return p;
1264 work2 = work2->next;
1274 guint vflist_count(ViewFile *vf, gint64 *bytes)
1284 FileData *fd = work->data;
1292 return g_list_length(vf->list);
1295 GList *vflist_get_list(ViewFile *vf)
1303 FileData *fd = work->data;
1306 list = g_list_prepend(list, file_data_ref(fd));
1309 return g_list_reverse(list);
1313 *-----------------------------------------------------------------------------
1315 *-----------------------------------------------------------------------------
1318 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1320 GtkTreeModel *store;
1321 GtkTreeSelection *selection;
1324 gboolean found = FALSE;
1326 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1327 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1329 while (!found && work)
1331 GtkTreePath *tpath = work->data;
1335 gtk_tree_model_get_iter(store, &iter, tpath);
1336 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1337 if (fd_n == fd) found = TRUE;
1340 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1346 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1350 fd = vf_index_get_data(vf, row);
1351 return vflist_row_is_selected(vf, fd);
1354 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1356 GtkTreeModel *store;
1357 GtkTreeSelection *selection;
1361 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1362 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1372 GtkTreePath *tpath = work->data;
1376 gtk_tree_model_get_iter(store, &iter, tpath);
1377 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1386 count = g_list_length(slist);
1387 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1393 GList *vflist_selection_get_list(ViewFile *vf)
1395 GtkTreeModel *store;
1396 GtkTreeSelection *selection;
1401 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1402 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1406 GtkTreePath *tpath = work->data;
1410 gtk_tree_model_get_iter(store, &iter, tpath);
1411 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1413 list = g_list_prepend(list, file_data_ref(fd));
1417 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1420 return g_list_reverse(list);
1423 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1425 GtkTreeModel *store;
1426 GtkTreeSelection *selection;
1431 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1432 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1436 GtkTreePath *tpath = work->data;
1440 gtk_tree_model_get_iter(store, &iter, tpath);
1441 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1443 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1447 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1450 return g_list_reverse(list);
1453 void vflist_select_all(ViewFile *vf)
1455 GtkTreeSelection *selection;
1457 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1458 gtk_tree_selection_select_all(selection);
1460 VFLIST(vf)->select_fd = NULL;
1463 void vflist_select_none(ViewFile *vf)
1465 GtkTreeSelection *selection;
1467 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1468 gtk_tree_selection_unselect_all(selection);
1471 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1476 tpath = gtk_tree_model_get_path(store, iter);
1477 result = gtk_tree_path_prev(tpath);
1479 gtk_tree_model_get_iter(store, iter, tpath);
1481 gtk_tree_path_free(tpath);
1486 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1488 if (!gtk_tree_model_get_iter_first(store, iter))
1493 GtkTreeIter next = *iter;
1495 if (gtk_tree_model_iter_next(store, &next))
1504 void vflist_select_invert(ViewFile *vf)
1507 GtkTreeSelection *selection;
1508 GtkTreeModel *store;
1511 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1512 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1514 /* Backward iteration prevents scrolling to the end of the list,
1515 * it scrolls to the first selected row instead. */
1516 valid = tree_model_get_iter_last(store, &iter);
1520 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1523 gtk_tree_selection_unselect_iter(selection, &iter);
1525 gtk_tree_selection_select_iter(selection, &iter);
1527 valid = tree_model_iter_prev(store, &iter);
1531 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1535 if (vflist_find_row(vf, fd, &iter) < 0) return;
1537 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1539 if (!vflist_row_is_selected(vf, fd))
1541 GtkTreeSelection *selection;
1542 GtkTreeModel *store;
1545 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1546 gtk_tree_selection_unselect_all(selection);
1547 gtk_tree_selection_select_iter(selection, &iter);
1548 vflist_move_cursor(vf, &iter);
1550 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1551 tpath = gtk_tree_model_get_path(store, &iter);
1552 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1553 gtk_tree_path_free(tpath);
1557 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1560 FileData *fd = NULL;
1562 if (sel_fd->parent) sel_fd = sel_fd->parent;
1571 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1573 if (match >= 0) break;
1576 if (fd) vflist_select_by_fd(vf, fd);
1580 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1582 GtkTreeModel *store;
1584 GtkTreeSelection *selection;
1588 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1590 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1591 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1593 valid = gtk_tree_model_get_iter_first(store, &iter);
1597 gboolean mark_val, selected;
1598 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1600 mark_val = file_data_get_mark(fd, n);
1601 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1605 case MTS_MODE_SET: selected = mark_val;
1607 case MTS_MODE_OR: selected = mark_val | selected;
1609 case MTS_MODE_AND: selected = mark_val & selected;
1611 case MTS_MODE_MINUS: selected = !mark_val & selected;
1616 gtk_tree_selection_select_iter(selection, &iter);
1618 gtk_tree_selection_unselect_iter(selection, &iter);
1620 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1624 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1626 GtkTreeModel *store;
1627 GtkTreeSelection *selection;
1632 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1634 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1635 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1639 GtkTreePath *tpath = work->data;
1643 gtk_tree_model_get_iter(store, &iter, tpath);
1644 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1646 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1650 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1652 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1654 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1658 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1660 vf_refresh_idle(vf);
1664 /* mark functions can have various side effects - update all columns to be sure */
1665 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1669 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1673 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1678 *-----------------------------------------------------------------------------
1680 *-----------------------------------------------------------------------------
1683 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb)
1685 GtkTreeViewColumn *column;
1686 GtkCellRenderer *cell;
1690 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1691 if (!column) return;
1693 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1695 list = gtk_tree_view_column_get_cell_renderers(column);
1700 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1701 gtk_tree_view_column_set_visible(column, thumb);
1703 multiline = (thumb && options->thumbnails.max_height >= 48);
1705 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_MULTILINE);
1706 if (!column) return;
1707 gtk_tree_view_column_set_visible(column, multiline);
1708 if (multiline) gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1710 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_NAME);
1711 if (!column) return;
1712 gtk_tree_view_column_set_visible(column, !multiline);
1713 if (!multiline) gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1715 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1716 if (!column) return;
1717 gtk_tree_view_column_set_visible(column, !multiline);
1719 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1720 if (!column) return;
1721 gtk_tree_view_column_set_visible(column, !multiline);
1723 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1726 static void vflist_populate_view(ViewFile *vf)
1728 GtkTreeStore *store;
1729 gboolean thumbs_enabled;
1732 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1733 thumbs_enabled = VFLIST(vf)->thumbs_enabled;
1735 vflist_thumb_stop(vf);
1739 vflist_store_clear(vf);
1744 vflist_listview_set_columns(vf->listview, thumbs_enabled);
1746 selected = vflist_selection_get_list(vf);
1748 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected);
1750 if (selected && vflist_selection_count(vf, NULL) == 0)
1752 /* all selected files disappeared */
1753 vflist_select_closest(vf, selected->data);
1756 filelist_free(selected);
1759 vflist_thumb_update(vf);
1762 gboolean vflist_refresh(ViewFile *vf)
1765 gboolean ret = TRUE;
1767 old_list = vf->list;
1770 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1773 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1775 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1776 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1777 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1779 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1780 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1783 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1785 vflist_populate_view(vf);
1787 filelist_free(old_list);
1788 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1795 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1797 #define CELL_HEIGHT_OVERRIDE 512
1799 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1803 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1804 if (spec && G_IS_PARAM_SPEC_INT(spec))
1806 GParamSpecInt *spec_int;
1808 spec_int = G_PARAM_SPEC_INT(spec);
1809 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1813 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1815 static GdkColor color;
1816 static GtkWidget *done = NULL;
1822 style = gtk_widget_get_style(widget);
1823 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1824 shift_color(&color, -1, 0);
1831 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1832 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1834 ViewFile *vf = data;
1837 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1838 g_object_set(G_OBJECT(cell),
1839 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1840 "cell-background-set", set, NULL);
1843 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
1845 GtkTreeViewColumn *column;
1846 GtkCellRenderer *renderer;
1848 column = gtk_tree_view_column_new();
1849 gtk_tree_view_column_set_title(column, title);
1850 gtk_tree_view_column_set_min_width(column, 4);
1854 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1855 renderer = gtk_cell_renderer_text_new();
1858 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1860 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1861 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1863 gtk_tree_view_column_set_expand(column, TRUE);
1867 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1868 renderer = gtk_cell_renderer_pixbuf_new();
1869 cell_renderer_height_override(renderer);
1870 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1871 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1874 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1875 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1876 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1878 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1881 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1883 ViewFile *vf = data;
1884 GtkTreeStore *store;
1885 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1891 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1892 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1895 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1897 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1899 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
1901 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1902 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
1903 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1905 vf_refresh_idle(vf);
1909 /* mark functions can have various side effects - update all columns to be sure */
1910 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1912 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1914 gtk_tree_path_free(path);
1917 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1919 GtkTreeViewColumn *column;
1920 GtkCellRenderer *renderer;
1921 GtkTreeStore *store;
1924 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1926 renderer = gtk_cell_renderer_toggle_new();
1927 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1929 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1930 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1931 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1933 index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1934 gtk_tree_view_column_set_fixed_width(column, 22);
1935 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1938 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1942 *-----------------------------------------------------------------------------
1944 *-----------------------------------------------------------------------------
1947 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1949 if (!dir_fd) return FALSE;
1950 if (vf->dir_fd == dir_fd) return TRUE;
1952 file_data_unref(vf->dir_fd);
1953 vf->dir_fd = file_data_ref(dir_fd);
1955 /* force complete reload */
1956 vflist_store_clear(vf);
1958 filelist_free(vf->list);
1961 return vf_refresh(vf);
1964 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1966 ViewFile *vf = data;
1968 file_data_unregister_notify_func(vf_notify_cb, vf);
1970 vflist_select_idle_cancel(vf);
1971 vf_refresh_idle_cancel(vf);
1972 vflist_thumb_stop(vf);
1974 filelist_free(vf->list);
1977 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1979 GtkTreeStore *store;
1980 GtkTreeSelection *selection;
1981 GType flist_types[FILE_COLUMN_COUNT];
1985 vf->info = g_new0(ViewFileInfoList, 1);
1987 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1988 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1989 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1990 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1991 flist_types[FILE_COLUMN_MULTILINE] = G_TYPE_STRING;
1992 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1993 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1994 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1995 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1996 flist_types[i] = G_TYPE_BOOLEAN;
1998 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
2000 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2001 g_object_unref(store);
2003 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2004 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
2005 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
2007 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2008 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2012 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2014 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2015 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
2019 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2020 g_assert(column == FILE_VIEW_COLUMN_THUMB);
2023 vflist_listview_add_column(vf, FILE_COLUMN_MULTILINE, _("Name"), FALSE, FALSE, TRUE);
2024 g_assert(column == FILE_VIEW_COLUMN_MULTILINE);
2027 vflist_listview_add_column(vf, FILE_COLUMN_NAME, _("Name"), FALSE, FALSE, TRUE);
2028 g_assert(column == FILE_VIEW_COLUMN_NAME);
2031 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2032 g_assert(column == FILE_VIEW_COLUMN_SIZE);
2035 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2036 g_assert(column == FILE_VIEW_COLUMN_DATE);
2039 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2043 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2045 if (VFLIST(vf)->thumbs_enabled == enable) return;
2047 VFLIST(vf)->thumbs_enabled = enable;
2048 if (vf->layout) vf_refresh(vf);
2051 void vflist_marks_set(ViewFile *vf, gboolean enable)
2053 GList *columns, *work;
2055 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2060 GtkTreeViewColumn *column = work->data;
2061 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2064 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2065 gtk_tree_view_column_set_visible(column, enable);
2068 g_list_free(columns);
2072 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */