4 * Copyright (C) 2008 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"
23 #include "layout_image.h"
28 #include "ui_fileops.h"
30 #include "ui_tree_edit.h"
31 #include "uri_utils.h"
32 #include "view_file.h"
34 #include <gdk/gdkkeysyms.h> /* for keyboard values */
36 /* Index to tree store */
38 FILE_COLUMN_POINTER = 0,
42 FILE_COLUMN_MULTILINE,
47 FILE_COLUMN_MARKS_LAST = FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
52 /* Index to tree view */
54 FILE_VIEW_COLUMN_MARKS = 0,
55 FILE_VIEW_COLUMN_MARKS_LAST = FILE_VIEW_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
56 FILE_VIEW_COLUMN_THUMB,
57 FILE_VIEW_COLUMN_MULTILINE,
58 FILE_VIEW_COLUMN_NAME,
59 FILE_VIEW_COLUMN_SIZE,
60 FILE_VIEW_COLUMN_DATE,
61 FILE_VIEW_COLUMN_COUNT
66 static gint vflist_row_is_selected(ViewFile *vf, FileData *fd);
67 static gint vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data);
68 static void vflist_populate_view(ViewFile *vf);
72 *-----------------------------------------------------------------------------
74 *-----------------------------------------------------------------------------
81 } ViewFileFindRowData;
83 static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
85 ViewFileFindRowData *find = data;
87 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
98 static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter)
101 ViewFileFindRowData data = {fd, iter, 0, 0};
103 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
104 gtk_tree_model_foreach(store, vflist_find_row_cb, &data);
114 static FileData *vflist_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter)
117 GtkTreeViewColumn *column;
119 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), x, y,
120 &tpath, &column, NULL, NULL))
126 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
127 gtk_tree_model_get_iter(store, &row, tpath);
128 gtk_tree_path_free(tpath);
129 gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &fd, -1);
138 static gint vflist_find_sidecar_list_idx(GList *work, FileData *fd)
143 FileData *fd_p = work->data;
144 if (fd == fd_p) return i;
148 GList *work2 = fd_p->sidecar_files;
152 if (fd == fd_p) return i;
162 static gint vflist_sidecar_list_count(GList *work)
167 FileData *fd = work->data;
170 GList *work2 = fd->sidecar_files;
182 static gboolean vflist_store_clear_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
185 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
190 static void vflist_store_clear(ViewFile *vf)
193 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
194 gtk_tree_model_foreach(store, vflist_store_clear_cb, NULL);
195 gtk_tree_store_clear(GTK_TREE_STORE(store));
198 void vflist_color_set(ViewFile *vf, FileData *fd, gint color_set)
203 if (vflist_find_row(vf, fd, &iter) < 0) return;
204 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
205 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
208 static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
213 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
215 tpath = gtk_tree_model_get_path(store, iter);
216 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
217 gtk_tree_path_free(tpath);
221 static gint vflist_column_idx(ViewFile *vf, gint store_idx)
223 GList *columns, *work;
226 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
230 GtkTreeViewColumn *column = work->data;
231 if (store_idx == GPOINTER_TO_INT(g_object_get_data (G_OBJECT(column), "column_store_idx")))
237 g_list_free(columns);
243 *-----------------------------------------------------------------------------
245 *-----------------------------------------------------------------------------
248 static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
249 GtkSelectionData *selection_data, guint info,
250 guint time, gpointer data)
254 gchar *uri_text = NULL;
257 if (!VFLIST(vf)->click_fd) return;
259 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
261 list = vf_selection_get_list(vf);
265 list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
270 uri_text = uri_text_from_filelist(list, &total, (info == TARGET_TEXT_PLAIN));
273 DEBUG_1("%s", uri_text);
275 gtk_selection_data_set(selection_data, selection_data->target,
276 8, (guchar *)uri_text, total);
280 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
284 vflist_color_set(vf, VFLIST(vf)->click_fd, TRUE);
286 if (VFLIST(vf)->thumbs_enabled &&
287 VFLIST(vf)->click_fd && VFLIST(vf)->click_fd->thumb_pixbuf)
291 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
292 items = vf_selection_count(vf, NULL);
296 dnd_set_drag_icon(widget, context, VFLIST(vf)->click_fd->thumb_pixbuf, items);
300 static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
304 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
306 if (context->action == GDK_ACTION_MOVE)
312 static void vflist_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context,
313 int x, int y, GtkSelectionData *selection,
314 guint info, guint time, gpointer data)
318 if (info == TARGET_TEXT_PLAIN) {
319 FileData *fd = vflist_find_data_by_coord(vf, x, y, NULL);
322 /* Add keywords to file */
323 gchar *str = g_strndup(selection->data, selection->length);
324 GList *kw_list = string_to_keywords_list(str);
326 metadata_append_list(fd, KEYWORD_KEY, kw_list);
327 string_list_free(kw_list);
329 if (vf->layout && vf->layout->bar_info) {
330 bar_info_set(vf->layout->bar_info, fd);
336 void vflist_dnd_init(ViewFile *vf)
338 gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
339 dnd_file_drag_types, dnd_file_drag_types_count,
340 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
341 gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL,
342 dnd_file_drag_types, dnd_file_drag_types_count,
343 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
345 g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
346 G_CALLBACK(vflist_dnd_get), vf);
347 g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
348 G_CALLBACK(vflist_dnd_begin), vf);
349 g_signal_connect(G_OBJECT(vf->listview), "drag_end",
350 G_CALLBACK(vflist_dnd_end), vf);
351 g_signal_connect(G_OBJECT(vf->listview), "drag_data_received",
352 G_CALLBACK(vflist_drag_data_received), vf);
356 *-----------------------------------------------------------------------------
358 *-----------------------------------------------------------------------------
361 GList *vflist_pop_menu_file_list(ViewFile *vf)
363 if (!VFLIST(vf)->click_fd) return NULL;
365 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
367 return vf_selection_get_list(vf);
370 return g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
373 void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
377 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
381 list = vf_selection_get_list(vf);
382 view_window_new_from_list(list);
387 view_window_new(VFLIST(vf)->click_fd);
391 void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
396 list = vf_pop_menu_file_list(vf);
397 if (options->file_ops.enable_in_place_rename &&
398 list && !list->next && VFLIST(vf)->click_fd)
405 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
406 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
410 tpath = gtk_tree_model_get_path(store, &iter);
411 tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
412 vflist_column_idx(vf, FILE_COLUMN_NAME), VFLIST(vf)->click_fd->name,
413 vflist_row_rename_cb, vf);
414 gtk_tree_path_free(tpath);
419 file_util_rename(NULL, list, vf->listview);
422 void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
426 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
429 layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
433 vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled);
437 void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
441 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
445 void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
448 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
449 VFLIST(vf)->click_fd = NULL;
455 *-----------------------------------------------------------------------------
457 *-----------------------------------------------------------------------------
460 static gint vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
466 if (strlen(new) == 0) return FALSE;
468 old_path = g_build_filename(vf->dir_fd->path, old, NULL);
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 FileData *fd = file_data_new_simple(old_path); /* get the fd from cache */
480 file_util_rename_simple(fd, new_path, vf->listview);
489 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
497 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return;
498 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
499 tpath = gtk_tree_model_get_path(store, &iter);
500 tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
501 gtk_tree_path_free(tpath);
503 popup_menu_position_clamp(menu, x, y, 0);
506 gint vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
511 if (event->keyval != GDK_Menu) return FALSE;
513 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
519 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
520 gtk_tree_model_get_iter(store, &iter, tpath);
521 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
522 gtk_tree_path_free(tpath);
526 VFLIST(vf)->click_fd = NULL;
529 vf->popup = vf_pop_menu(vf);
530 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
535 gint vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
541 GtkTreeViewColumn *column;
543 vf->clicked_mark = 0;
545 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
546 &tpath, &column, NULL, NULL))
549 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
551 if (bevent->button == MOUSE_BUTTON_LEFT &&
552 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
555 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
556 vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
558 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
560 gtk_tree_model_get_iter(store, &iter, tpath);
561 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
563 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE);
565 gtk_tree_path_free(tpath);
568 VFLIST(vf)->click_fd = fd;
570 if (bevent->button == MOUSE_BUTTON_RIGHT)
572 vf->popup = vf_pop_menu(vf);
573 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
574 bevent->button, bevent->time);
578 if (!fd) return FALSE;
580 if (bevent->button == MOUSE_BUTTON_MIDDLE)
582 if (!vflist_row_is_selected(vf, fd))
584 vflist_color_set(vf, fd, TRUE);
590 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
591 !(bevent->state & GDK_SHIFT_MASK ) &&
592 !(bevent->state & GDK_CONTROL_MASK ) &&
593 vflist_row_is_selected(vf, fd))
595 GtkTreeSelection *selection;
597 gtk_widget_grab_focus(widget);
600 /* returning FALSE and further processing of the event is needed for
601 correct operation of the expander, to show the sidecar files.
602 It however resets the selection of multiple files. With this condition
603 it should work for both cases */
604 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
605 return (gtk_tree_selection_count_selected_rows(selection) > 1);
609 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
611 if (vf->layout) layout_image_full_screen_start(vf->layout);
618 gint vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
625 if (bevent->button == MOUSE_BUTTON_MIDDLE)
627 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
630 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
635 if ((bevent->x != 0 || bevent->y != 0) &&
636 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
637 &tpath, NULL, NULL, NULL))
641 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
642 gtk_tree_model_get_iter(store, &iter, tpath);
643 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
644 gtk_tree_path_free(tpath);
647 if (bevent->button == MOUSE_BUTTON_MIDDLE)
649 if (fd && VFLIST(vf)->click_fd == fd)
651 GtkTreeSelection *selection;
653 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
654 if (vflist_row_is_selected(vf, fd))
656 gtk_tree_selection_unselect_iter(selection, &iter);
660 gtk_tree_selection_select_iter(selection, &iter);
666 if (fd && VFLIST(vf)->click_fd == fd &&
667 !(bevent->state & GDK_SHIFT_MASK ) &&
668 !(bevent->state & GDK_CONTROL_MASK ) &&
669 vflist_row_is_selected(vf, fd))
671 GtkTreeSelection *selection;
673 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
674 gtk_tree_selection_unselect_all(selection);
675 gtk_tree_selection_select_iter(selection, &iter);
676 vflist_move_cursor(vf, &iter);
677 // return TRUE;// FIXME - expand
683 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
685 FileData *read_ahead_fd = NULL;
690 cur_fd = layout_image_get_fd(vf->layout);
691 if (sel_fd == cur_fd) return; /* no change */
693 row = g_list_index(vf->list, sel_fd);
694 // FIXME sidecar data
696 if (sel_fd && options->image.enable_read_ahead && row >= 0)
698 if (row > g_list_index(vf->list, cur_fd) &&
699 (guint) (row + 1) < vf_count(vf, NULL))
701 read_ahead_fd = vf_index_get_data(vf, row + 1);
705 read_ahead_fd = vf_index_get_data(vf, row - 1);
709 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
712 static gint vflist_select_idle_cb(gpointer data)
718 VFLIST(vf)->select_idle_id = -1;
724 if (VFLIST(vf)->select_fd)
726 vflist_select_image(vf, VFLIST(vf)->select_fd);
727 VFLIST(vf)->select_fd = NULL;
730 VFLIST(vf)->select_idle_id = -1;
734 static void vflist_select_idle_cancel(ViewFile *vf)
736 if (VFLIST(vf)->select_idle_id != -1) g_source_remove(VFLIST(vf)->select_idle_id);
737 VFLIST(vf)->select_idle_id = -1;
740 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
741 gboolean path_currently_selected, gpointer data)
746 if (!path_currently_selected &&
747 gtk_tree_model_get_iter(store, &iter, tpath))
749 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
753 VFLIST(vf)->select_fd = NULL;
757 VFLIST(vf)->select_idle_id == -1)
759 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
766 *-----------------------------------------------------------------------------
768 *-----------------------------------------------------------------------------
772 static gboolean vflist_dummy_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
773 gboolean path_currently_selected, gpointer data)
779 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
782 gchar *sidecars = NULL;
783 gchar *name_sidecars;
785 const gchar *time = text_from_time(fd->date);
786 gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
789 if (fd->sidecar_files)
791 sidecars = file_data_sc_list_to_string(fd);
792 name_sidecars = g_strdup_printf("%s%s %s", link, fd->name, sidecars);
796 name_sidecars = g_strdup_printf("%s%s", link, fd->name);
798 size = text_from_size(fd->size);
800 multiline = g_strdup_printf("%s\n%s\n%s", name_sidecars, size, time);
802 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
803 FILE_COLUMN_VERSION, fd->version,
804 FILE_COLUMN_THUMB, fd->thumb_pixbuf,
805 FILE_COLUMN_MULTILINE, multiline,
806 FILE_COLUMN_NAME, name_sidecars,
807 FILE_COLUMN_SIZE, size,
808 FILE_COLUMN_DATE, time,
809 #define STORE_SET_IS_SLOW 1
810 #if STORE_SET_IS_SLOW
811 /* this is 3x faster on a directory with 20000 files */
812 FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
813 FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
814 FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
815 FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
816 FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
817 FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
818 #if FILEDATA_MARKS_SIZE != 6
819 #error this needs to be updated
822 FILE_COLUMN_COLOR, FALSE, -1);
824 #if !STORE_SET_IS_SLOW
827 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
828 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
833 g_free(name_sidecars);
837 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected)
842 gint num_ordered = 0;
843 gint num_prepended = 0;
845 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
851 FileData *fd = work->data;
856 FileData *old_fd = NULL;
857 gint old_version = 0;
862 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
863 FILE_COLUMN_POINTER, &old_fd,
864 FILE_COLUMN_VERSION, &old_version,
874 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
876 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
878 if (match == 0) g_warning("multiple fd for the same path");
893 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
898 here should be used gtk_tree_store_append, but this function seems to be O(n)
899 and it seems to be much faster to add new entries to the beginning and reorder later
902 gtk_tree_store_prepend(store, &new, parent_iter);
905 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
906 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected);
908 if (g_list_find(selected, fd))
910 /* renamed files - the same fd appears at different position - select it again*/
911 GtkTreeSelection *selection;
912 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
913 gtk_tree_selection_select_iter(selection, &new);
920 file_data_unref(old_fd);
921 valid = gtk_tree_store_remove(store, &iter);
925 if (fd->version != old_version)
927 vflist_setup_iter(vf, store, &iter, fd);
928 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected);
931 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
942 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
943 file_data_unref(old_fd);
945 valid = gtk_tree_store_remove(store, &iter);
948 /* move the prepended entries to the correct position */
952 gint num_total = num_prepended + num_ordered;
953 gint *new_order = g_malloc(num_total * sizeof(gint));
955 for (i = 0; i < num_total; i++)
958 new_order[i] = num_prepended + i;
960 new_order[i] = num_total - 1 - i;
962 gtk_tree_store_reorder(store, parent_iter, new_order);
968 void vflist_sort_set(ViewFile *vf, SortType type, gint ascend)
971 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
976 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
977 if (!vf->list) return;
983 FileData *fd = work->data;
984 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
989 vf->sort_method = type;
990 vf->sort_ascend = ascend;
992 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
994 new_order = g_malloc(i * sizeof(gint));
1000 FileData *fd = work->data;
1001 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1006 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1007 gtk_tree_store_reorder(store, NULL, new_order);
1010 g_hash_table_destroy(fd_idx_hash);
1014 *-----------------------------------------------------------------------------
1016 *-----------------------------------------------------------------------------
1019 static gint vflist_thumb_next(ViewFile *vf);
1021 static void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1026 FileData *fd = work->data;
1029 if (fd->thumb_pixbuf) (*done)++;
1031 if (fd->sidecar_files)
1033 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1039 static gdouble vflist_thumb_progress(ViewFile *vf)
1044 vflist_thumb_progress_count(vf->list, &count, &done);
1046 DEBUG_1("thumb progress: %d of %d", done, count);
1047 return (gdouble)done / count;
1051 static void vflist_thumb_status(ViewFile *vf, gdouble val, const gchar *text)
1053 if (vf->func_thumb_status)
1055 vf->func_thumb_status(vf, val, text, vf->data_thumb_status);
1059 static void vflist_thumb_cleanup(ViewFile *vf)
1061 vflist_thumb_status(vf, 0.0, NULL);
1063 vf->thumbs_running = FALSE;
1065 thumb_loader_free(vf->thumbs_loader);
1066 vf->thumbs_loader = NULL;
1068 vf->thumbs_filedata = NULL;
1071 static void vflist_thumb_stop(ViewFile *vf)
1073 if (vf->thumbs_running) vflist_thumb_cleanup(vf);
1076 static void vflist_thumb_do(ViewFile *vf, ThumbLoader *tl, FileData *fd)
1078 GtkTreeStore *store;
1081 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1083 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1084 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1086 vflist_thumb_status(vf, vflist_thumb_progress(vf), _("Loading thumbs..."));
1089 static void vflist_thumb_error_cb(ThumbLoader *tl, gpointer data)
1091 ViewFile *vf = data;
1093 if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1095 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
1098 while (vflist_thumb_next(vf));
1101 static void vflist_thumb_done_cb(ThumbLoader *tl, gpointer data)
1103 ViewFile *vf = data;
1105 if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1107 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
1110 while (vflist_thumb_next(vf));
1113 static gint vflist_thumb_next(ViewFile *vf)
1116 FileData *fd = NULL;
1118 /* first check the visible files */
1120 if (GTK_WIDGET_REALIZED(vf->listview) &&
1121 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1123 GtkTreeModel *store;
1127 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1128 gtk_tree_model_get_iter(store, &iter, tpath);
1129 gtk_tree_path_free(tpath);
1131 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1133 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1134 if (fd->thumb_pixbuf) fd = NULL;
1136 valid = gtk_tree_model_iter_next(store, &iter);
1140 /* then find first undone */
1144 GList *work = vf->list;
1147 FileData *fd_p = work->data;
1148 if (!fd_p->thumb_pixbuf)
1152 GList *work2 = fd_p->sidecar_files;
1154 while (work2 && !fd)
1157 if (!fd_p->thumb_pixbuf) fd = fd_p;
1158 work2 = work2->next;
1168 vflist_thumb_cleanup(vf);
1172 vf->thumbs_filedata = fd;
1174 thumb_loader_free(vf->thumbs_loader);
1176 vf->thumbs_loader = thumb_loader_new(options->thumbnails.max_width, options->thumbnails.max_height);
1177 thumb_loader_set_callbacks(vf->thumbs_loader,
1178 vflist_thumb_done_cb,
1179 vflist_thumb_error_cb,
1183 if (!thumb_loader_start(vf->thumbs_loader, fd))
1185 /* set icon to unknown, continue */
1186 DEBUG_1("thumb loader start failed %s", fd->path);
1187 vflist_thumb_do(vf, vf->thumbs_loader, fd);
1195 static void vflist_thumb_update(ViewFile *vf)
1197 vflist_thumb_stop(vf);
1198 if (!VFLIST(vf)->thumbs_enabled) return;
1200 vflist_thumb_status(vf, 0.0, _("Loading thumbs..."));
1201 vf->thumbs_running = TRUE;
1203 while (vflist_thumb_next(vf));
1207 *-----------------------------------------------------------------------------
1209 *-----------------------------------------------------------------------------
1212 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1214 return g_list_nth_data(vf->list, row);
1217 gint vflist_index_by_path(ViewFile *vf, const gchar *path)
1222 if (!path) return -1;
1227 FileData *fd = work->data;
1228 if (strcmp(path, fd->path) == 0) return p;
1237 guint vflist_count(ViewFile *vf, gint64 *bytes)
1247 FileData *fd = work->data;
1255 return g_list_length(vf->list);
1258 GList *vflist_get_list(ViewFile *vf)
1266 FileData *fd = work->data;
1269 list = g_list_prepend(list, file_data_ref(fd));
1272 return g_list_reverse(list);
1276 *-----------------------------------------------------------------------------
1278 *-----------------------------------------------------------------------------
1281 static gint vflist_row_is_selected(ViewFile *vf, FileData *fd)
1283 GtkTreeModel *store;
1284 GtkTreeSelection *selection;
1289 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1290 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1292 while (!found && work)
1294 GtkTreePath *tpath = work->data;
1298 gtk_tree_model_get_iter(store, &iter, tpath);
1299 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1300 if (fd_n == fd) found = TRUE;
1303 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1309 gint vflist_index_is_selected(ViewFile *vf, gint row)
1313 fd = vf_index_get_data(vf, row);
1314 return vflist_row_is_selected(vf, fd);
1317 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1319 GtkTreeModel *store;
1320 GtkTreeSelection *selection;
1324 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1325 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1335 GtkTreePath *tpath = work->data;
1339 gtk_tree_model_get_iter(store, &iter, tpath);
1340 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1349 count = g_list_length(slist);
1350 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1356 GList *vflist_selection_get_list(ViewFile *vf)
1358 GtkTreeModel *store;
1359 GtkTreeSelection *selection;
1364 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1365 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1369 GtkTreePath *tpath = work->data;
1373 gtk_tree_model_get_iter(store, &iter, tpath);
1374 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1376 list = g_list_prepend(list, file_data_ref(fd));
1380 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1383 return g_list_reverse(list);
1386 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1388 GtkTreeModel *store;
1389 GtkTreeSelection *selection;
1394 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1395 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1399 GtkTreePath *tpath = work->data;
1403 gtk_tree_model_get_iter(store, &iter, tpath);
1404 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1406 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1410 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1413 return g_list_reverse(list);
1416 void vflist_select_all(ViewFile *vf)
1418 GtkTreeSelection *selection;
1420 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1421 gtk_tree_selection_select_all(selection);
1423 VFLIST(vf)->select_fd = NULL;
1426 void vflist_select_none(ViewFile *vf)
1428 GtkTreeSelection *selection;
1430 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1431 gtk_tree_selection_unselect_all(selection);
1434 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1439 tpath = gtk_tree_model_get_path(store, iter);
1440 result = gtk_tree_path_prev(tpath);
1442 gtk_tree_model_get_iter(store, iter, tpath);
1444 gtk_tree_path_free(tpath);
1449 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1451 if (!gtk_tree_model_get_iter_first(store, iter))
1456 GtkTreeIter next = *iter;
1458 if (gtk_tree_model_iter_next(store, &next))
1467 void vflist_select_invert(ViewFile *vf)
1470 GtkTreeSelection *selection;
1471 GtkTreeModel *store;
1474 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1475 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1477 /* Backward iteration prevents scrolling to the end of the list,
1478 * it scrolls to the first selected row instead. */
1479 valid = tree_model_get_iter_last(store, &iter);
1483 gint selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1486 gtk_tree_selection_unselect_iter(selection, &iter);
1488 gtk_tree_selection_select_iter(selection, &iter);
1490 valid = tree_model_iter_prev(store, &iter);
1494 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1498 if (vflist_find_row(vf, fd, &iter) < 0) return;
1500 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1502 if (!vflist_row_is_selected(vf, fd))
1504 GtkTreeSelection *selection;
1505 GtkTreeModel *store;
1508 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1509 gtk_tree_selection_unselect_all(selection);
1510 gtk_tree_selection_select_iter(selection, &iter);
1511 vflist_move_cursor(vf, &iter);
1513 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1514 tpath = gtk_tree_model_get_path(store, &iter);
1515 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1516 gtk_tree_path_free(tpath);
1520 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1523 FileData *fd = NULL;
1525 if (sel_fd->parent) sel_fd = sel_fd->parent;
1534 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1536 if (match >= 0) break;
1539 if (fd) vflist_select_by_fd(vf, fd);
1543 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1545 GtkTreeModel *store;
1547 GtkTreeSelection *selection;
1551 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1553 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1554 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1556 valid = gtk_tree_model_get_iter_first(store, &iter);
1560 gboolean mark_val, selected;
1561 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1563 mark_val = file_data_get_mark(fd, n);
1564 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1568 case MTS_MODE_SET: selected = mark_val;
1570 case MTS_MODE_OR: selected = mark_val | selected;
1572 case MTS_MODE_AND: selected = mark_val & selected;
1574 case MTS_MODE_MINUS: selected = !mark_val & selected;
1579 gtk_tree_selection_select_iter(selection, &iter);
1581 gtk_tree_selection_unselect_iter(selection, &iter);
1583 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1587 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1589 GtkTreeModel *store;
1590 GtkTreeSelection *selection;
1595 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1597 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1598 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1602 GtkTreePath *tpath = work->data;
1606 gtk_tree_model_get_iter(store, &iter, tpath);
1607 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1609 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1613 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1615 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1617 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1621 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1623 vf_refresh_idle(vf);
1627 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1629 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_MARKS + n, file_data_get_mark(fd, n), -1);
1633 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1638 *-----------------------------------------------------------------------------
1640 *-----------------------------------------------------------------------------
1643 static void vflist_listview_set_columns(GtkWidget *listview, gint thumb)
1645 GtkTreeViewColumn *column;
1646 GtkCellRenderer *cell;
1650 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1651 if (!column) return;
1653 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1655 list = gtk_tree_view_column_get_cell_renderers(column);
1660 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1661 gtk_tree_view_column_set_visible(column, thumb);
1663 multiline = (thumb && options->thumbnails.max_height >= 48);
1665 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_MULTILINE);
1666 if (!column) return;
1667 gtk_tree_view_column_set_visible(column, multiline);
1668 if (multiline) gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1670 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_NAME);
1671 if (!column) return;
1672 gtk_tree_view_column_set_visible(column, !multiline);
1673 if (!multiline) gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1675 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1676 if (!column) return;
1677 gtk_tree_view_column_set_visible(column, !multiline);
1679 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1680 if (!column) return;
1681 gtk_tree_view_column_set_visible(column, !multiline);
1683 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1686 static void vflist_populate_view(ViewFile *vf)
1688 GtkTreeStore *store;
1692 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1693 thumbs = VFLIST(vf)->thumbs_enabled;
1695 vflist_thumb_stop(vf);
1699 vflist_store_clear(vf);
1704 vflist_listview_set_columns(vf->listview, thumbs);
1706 selected = vflist_selection_get_list(vf);
1708 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected);
1710 if (selected && vflist_selection_count(vf, NULL) == 0)
1712 /* all selected files disappeared */
1713 vflist_select_closest(vf, selected->data);
1716 filelist_free(selected);
1719 vflist_thumb_update(vf);
1722 gint vflist_refresh(ViewFile *vf)
1727 old_list = vf->list;
1730 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1733 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1735 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1736 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1737 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1739 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1740 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1743 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1745 vflist_populate_view(vf);
1747 filelist_free(old_list);
1748 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1755 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1757 #define CELL_HEIGHT_OVERRIDE 512
1759 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1763 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1764 if (spec && G_IS_PARAM_SPEC_INT(spec))
1766 GParamSpecInt *spec_int;
1768 spec_int = G_PARAM_SPEC_INT(spec);
1769 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1773 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1775 static GdkColor color;
1776 static GtkWidget *done = NULL;
1782 style = gtk_widget_get_style(widget);
1783 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1784 shift_color(&color, -1, 0);
1791 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1792 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1794 ViewFile *vf = data;
1797 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1798 g_object_set(G_OBJECT(cell),
1799 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1800 "cell-background-set", set, NULL);
1803 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gint image, gint right_justify, gint expand)
1805 GtkTreeViewColumn *column;
1806 GtkCellRenderer *renderer;
1808 column = gtk_tree_view_column_new();
1809 gtk_tree_view_column_set_title(column, title);
1810 gtk_tree_view_column_set_min_width(column, 4);
1814 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1815 renderer = gtk_cell_renderer_text_new();
1818 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1820 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1821 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1823 gtk_tree_view_column_set_expand(column, TRUE);
1827 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1828 renderer = gtk_cell_renderer_pixbuf_new();
1829 cell_renderer_height_override(renderer);
1830 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1831 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1834 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1835 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1836 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1838 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1841 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
1843 ViewFile *vf = data;
1844 GtkTreeStore *store;
1845 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1851 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1852 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1855 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1857 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1859 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &mark, -1);
1861 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1862 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, mark);
1863 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1865 vf_refresh_idle(vf);
1867 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1869 gtk_tree_store_set(store, &iter, col_idx, mark, -1);
1870 gtk_tree_path_free(path);
1873 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1875 GtkTreeViewColumn *column;
1876 GtkCellRenderer *renderer;
1877 GtkTreeStore *store;
1880 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1882 renderer = gtk_cell_renderer_toggle_new();
1883 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1885 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1886 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1887 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1889 index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1890 gtk_tree_view_column_set_fixed_width(column, 22);
1891 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1894 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
1898 *-----------------------------------------------------------------------------
1900 *-----------------------------------------------------------------------------
1903 gint vflist_set_fd(ViewFile *vf, FileData *dir_fd)
1905 if (!dir_fd) return FALSE;
1906 if (vf->dir_fd == dir_fd) return TRUE;
1908 file_data_unref(vf->dir_fd);
1909 vf->dir_fd = file_data_ref(dir_fd);
1911 /* force complete reload */
1912 vflist_store_clear(vf);
1914 filelist_free(vf->list);
1917 return vf_refresh(vf);
1920 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1922 ViewFile *vf = data;
1924 file_data_unregister_notify_func(vf_notify_cb, vf);
1926 vflist_select_idle_cancel(vf);
1927 vf_refresh_idle_cancel(vf);
1928 vflist_thumb_stop(vf);
1930 filelist_free(vf->list);
1933 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
1935 GtkTreeStore *store;
1936 GtkTreeSelection *selection;
1938 GType flist_types[FILE_COLUMN_COUNT];
1942 vf->info = g_new0(ViewFileInfoList, 1);
1944 VFLIST(vf)->click_fd = NULL;
1945 VFLIST(vf)->select_fd = NULL;
1946 VFLIST(vf)->thumbs_enabled = FALSE;
1948 VFLIST(vf)->select_idle_id = -1;
1950 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1951 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
1952 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1953 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1954 flist_types[FILE_COLUMN_MULTILINE] = G_TYPE_STRING;
1955 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1956 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1957 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1958 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1959 flist_types[i] = G_TYPE_BOOLEAN;
1961 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
1963 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1964 g_object_unref(store);
1966 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1967 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1968 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
1970 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
1971 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
1975 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
1977 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
1978 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
1982 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
1983 g_assert(column == FILE_VIEW_COLUMN_THUMB);
1986 vflist_listview_add_column(vf, FILE_COLUMN_MULTILINE, _("Name"), FALSE, FALSE, TRUE);
1987 g_assert(column == FILE_VIEW_COLUMN_MULTILINE);
1990 vflist_listview_add_column(vf, FILE_COLUMN_NAME, _("Name"), FALSE, FALSE, TRUE);
1991 g_assert(column == FILE_VIEW_COLUMN_NAME);
1994 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
1995 g_assert(column == FILE_VIEW_COLUMN_SIZE);
1998 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
1999 g_assert(column == FILE_VIEW_COLUMN_DATE);
2002 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2006 void vflist_thumb_set(ViewFile *vf, gint enable)
2008 if (VFLIST(vf)->thumbs_enabled == enable) return;
2010 VFLIST(vf)->thumbs_enabled = enable;
2011 if (vf->layout) vf_refresh(vf);
2014 void vflist_marks_set(ViewFile *vf, gint enable)
2016 GList *columns, *work;
2018 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2023 GtkTreeViewColumn *column = work->data;
2024 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2027 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2028 gtk_tree_view_column_set_visible(column, enable);
2031 g_list_free(columns);
2035 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */