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"
16 #include "cache_maint.h"
23 #include "layout_image.h"
27 #include "ui_bookmark.h"
28 #include "ui_fileops.h"
30 #include "ui_tree_edit.h"
33 #include <gdk/gdkkeysyms.h> /* for keyboard values */
35 #define VFLIST_INFO_POINTER(_vf_) ((ViewFileInfoList *)(_vf_->info))
36 #define VFLIST_INFO(_vf_, _part_) (VFLIST_INFO_POINTER(_vf_)->_part_)
39 FILE_COLUMN_POINTER = 0,
47 FILE_COLUMN_MARKS_LAST = FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
52 static gint vflist_row_is_selected(ViewFile *vf, FileData *fd);
53 static gint vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data);
54 static void vflist_populate_view(ViewFile *vf);
57 *-----------------------------------------------------------------------------
59 *-----------------------------------------------------------------------------
62 static void vflist_send_update(ViewFile *vf)
64 if (vf->func_status) vf->func_status(vf, vf->data_status);
68 *-----------------------------------------------------------------------------
70 *-----------------------------------------------------------------------------
77 } ViewFileFindRowData;
79 static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
81 ViewFileFindRowData *find = data;
83 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
94 static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter)
97 ViewFileFindRowData data = {fd, iter, 0, 0};
99 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
100 gtk_tree_model_foreach(store, vflist_find_row_cb, &data);
112 static gint vflist_find_sidecar_list_idx(GList *work, FileData *fd)
117 FileData *fd_p = work->data;
118 if (fd == fd_p) return i;
122 GList *work2 = fd_p->sidecar_files;
126 if (fd == fd_p) return i;
137 static gint vflist_sidecar_list_count(GList *work)
142 FileData *fd = work->data;
145 GList *work2 = fd->sidecar_files;
157 static void vflist_color_set(ViewFile *vf, FileData *fd, gint color_set)
162 if (vflist_find_row(vf, fd, &iter) < 0) return;
163 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
164 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
167 static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
172 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
174 tpath = gtk_tree_model_get_path(store, iter);
175 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
176 gtk_tree_path_free(tpath);
180 static gint vflist_column_idx(ViewFile *vf, gint store_idx)
182 GList *columns, *work;
185 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
189 GtkTreeViewColumn *column = work->data;
190 if (store_idx == GPOINTER_TO_INT(g_object_get_data (G_OBJECT(column), "column_store_idx")))
196 g_list_free(columns);
202 *-----------------------------------------------------------------------------
204 *-----------------------------------------------------------------------------
207 static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
208 GtkSelectionData *selection_data, guint info,
209 guint time, gpointer data)
213 gchar *uri_text = NULL;
216 if (!VFLIST_INFO(vf, click_fd)) return;
218 if (vflist_row_is_selected(vf, VFLIST_INFO(vf, click_fd)))
220 list = vflist_selection_get_list(vf);
224 list = g_list_append(NULL, file_data_ref(VFLIST_INFO(vf, click_fd)));
229 uri_text = uri_text_from_filelist(list, &total, (info == TARGET_TEXT_PLAIN));
234 gtk_selection_data_set(selection_data, selection_data->target,
235 8, (guchar *)uri_text, total);
239 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
243 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), TRUE);
245 if (VFLIST_INFO(vf, thumbs_enabled) &&
246 VFLIST_INFO(vf, click_fd) && VFLIST_INFO(vf, click_fd)->pixbuf)
250 if (vflist_row_is_selected(vf, VFLIST_INFO(vf, click_fd)))
251 items = vflist_selection_count(vf, NULL);
255 dnd_set_drag_icon(widget, context, VFLIST_INFO(vf, click_fd)->pixbuf, items);
259 static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
263 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
265 if (context->action == GDK_ACTION_MOVE)
271 void vflist_dnd_init(ViewFile *vf)
273 gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
274 dnd_file_drag_types, dnd_file_drag_types_count,
275 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
276 g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
277 G_CALLBACK(vflist_dnd_get), vf);
278 g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
279 G_CALLBACK(vflist_dnd_begin), vf);
280 g_signal_connect(G_OBJECT(vf->listview), "drag_end",
281 G_CALLBACK(vflist_dnd_end), vf);
285 *-----------------------------------------------------------------------------
287 *-----------------------------------------------------------------------------
290 static GList *vflist_pop_menu_file_list(ViewFile *vf)
292 if (!VFLIST_INFO(vf, click_fd)) return NULL;
294 if (vflist_row_is_selected(vf, VFLIST_INFO(vf, click_fd)))
296 return vflist_selection_get_list(vf);
299 return g_list_append(NULL, file_data_ref(VFLIST_INFO(vf, click_fd)));
302 static void vflist_pop_menu_edit_cb(GtkWidget *widget, gpointer data)
308 vf = submenu_item_get_data(widget);
309 n = GPOINTER_TO_INT(data);
313 list = vflist_pop_menu_file_list(vf);
314 start_editor_from_filelist(n, list);
318 static void vflist_pop_menu_info_cb(GtkWidget *widget, gpointer data)
322 info_window_new(NULL, vflist_pop_menu_file_list(vf), NULL);
325 static void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
329 if (vflist_row_is_selected(vf, VFLIST_INFO(vf, click_fd)))
333 list = vflist_selection_get_list(vf);
334 view_window_new_from_list(list);
339 view_window_new(VFLIST_INFO(vf, click_fd));
343 static void vflist_pop_menu_copy_cb(GtkWidget *widget, gpointer data)
347 file_util_copy(NULL, vflist_pop_menu_file_list(vf), NULL, vf->listview);
350 static void vflist_pop_menu_copy_path_cb(GtkWidget *widget, gpointer data)
354 file_util_copy_path_list_to_clipboard(vflist_pop_menu_file_list(vf));
357 static void vflist_pop_menu_move_cb(GtkWidget *widget, gpointer data)
361 file_util_move(NULL, vflist_pop_menu_file_list(vf), NULL, vf->listview);
364 static void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
369 list = vflist_pop_menu_file_list(vf);
370 if (options->file_ops.enable_in_place_rename &&
371 list && !list->next && VFLIST_INFO(vf, click_fd))
378 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
379 if (vflist_find_row(vf, VFLIST_INFO(vf, click_fd), &iter) >= 0)
383 tpath = gtk_tree_model_get_path(store, &iter);
384 tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
385 vflist_column_idx(vf, FILE_COLUMN_NAME), VFLIST_INFO(vf, click_fd)->name,
386 vflist_row_rename_cb, vf);
387 gtk_tree_path_free(tpath);
392 file_util_rename(NULL, list, vf->listview);
395 static void vflist_pop_menu_delete_cb(GtkWidget *widget, gpointer data)
399 file_util_delete(NULL, vflist_pop_menu_file_list(vf), vf->listview);
402 static void vflist_pop_menu_sort_cb(GtkWidget *widget, gpointer data)
407 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) return;
409 vf = submenu_item_get_data(widget);
412 type = (SortType)GPOINTER_TO_INT(data);
416 layout_sort_set(vf->layout, type, vf->sort_ascend);
420 vflist_sort_set(vf, type, vf->sort_ascend);
424 static void vflist_pop_menu_sort_ascend_cb(GtkWidget *widget, gpointer data)
430 layout_sort_set(vf->layout, vf->sort_method, !vf->sort_ascend);
434 vflist_sort_set(vf, vf->sort_method, !vf->sort_ascend);
438 static void vflist_pop_menu_icons_cb(GtkWidget *widget, gpointer data)
442 if (vf->layout) layout_views_set(vf->layout, vf->layout->dir_view_type, TRUE);
445 static void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
449 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
452 layout_thumb_set(vf->layout, !VFLIST_INFO(vf, thumbs_enabled));
456 vflist_thumb_set(vf, !VFLIST_INFO(vf, thumbs_enabled));
460 static void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
464 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
468 static void vflist_pop_menu_sel_mark_cb(GtkWidget *widget, gpointer data)
471 vflist_mark_to_selection(vf, vf->active_mark, MTS_MODE_SET);
474 static void vflist_pop_menu_sel_mark_and_cb(GtkWidget *widget, gpointer data)
477 vflist_mark_to_selection(vf, vf->active_mark, MTS_MODE_AND);
480 static void vflist_pop_menu_sel_mark_or_cb(GtkWidget *widget, gpointer data)
483 vflist_mark_to_selection(vf, vf->active_mark, MTS_MODE_OR);
486 static void vflist_pop_menu_sel_mark_minus_cb(GtkWidget *widget, gpointer data)
489 vflist_mark_to_selection(vf, vf->active_mark, MTS_MODE_MINUS);
492 static void vflist_pop_menu_set_mark_sel_cb(GtkWidget *widget, gpointer data)
495 vflist_selection_to_mark(vf, vf->active_mark, STM_MODE_SET);
498 static void vflist_pop_menu_res_mark_sel_cb(GtkWidget *widget, gpointer data)
501 vflist_selection_to_mark(vf, vf->active_mark, STM_MODE_RESET);
504 static void vflist_pop_menu_toggle_mark_sel_cb(GtkWidget *widget, gpointer data)
507 vflist_selection_to_mark(vf, vf->active_mark, STM_MODE_TOGGLE);
511 static void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
514 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
515 VFLIST_INFO(vf, click_fd) = NULL;
520 static GtkWidget *vflist_pop_menu(ViewFile *vf, FileData *fd, gint col_idx)
527 vflist_color_set(vf, fd, TRUE);
528 active = (fd != NULL);
530 menu = popup_menu_short_lived();
531 g_signal_connect(G_OBJECT(menu), "destroy",
532 G_CALLBACK(vflist_popup_destroy_cb), vf);
534 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
536 gint mark = col_idx - FILE_COLUMN_MARKS;
537 gchar *str_set_mark = g_strdup_printf(_("_Set mark %d"), mark + 1);
538 gchar *str_res_mark = g_strdup_printf(_("_Reset mark %d"), mark + 1);
539 gchar *str_toggle_mark = g_strdup_printf(_("_Toggle mark %d"), mark + 1);
540 gchar *str_sel_mark = g_strdup_printf(_("_Select mark %d"), mark + 1);
541 gchar *str_sel_mark_or = g_strdup_printf(_("_Add mark %d"), mark + 1);
542 gchar *str_sel_mark_and = g_strdup_printf(_("_Intersection with mark %d"), mark + 1);
543 gchar *str_sel_mark_minus = g_strdup_printf(_("_Unselect mark %d"), mark + 1);
546 vf->active_mark = mark;
547 menu_item_add_sensitive(menu, str_set_mark, active,
548 G_CALLBACK(vflist_pop_menu_set_mark_sel_cb), vf);
550 menu_item_add_sensitive(menu, str_res_mark, active,
551 G_CALLBACK(vflist_pop_menu_res_mark_sel_cb), vf);
553 menu_item_add_sensitive(menu, str_toggle_mark, active,
554 G_CALLBACK(vflist_pop_menu_toggle_mark_sel_cb), vf);
556 menu_item_add_divider(menu);
558 menu_item_add_sensitive(menu, str_sel_mark, active,
559 G_CALLBACK(vflist_pop_menu_sel_mark_cb), vf);
560 menu_item_add_sensitive(menu, str_sel_mark_or, active,
561 G_CALLBACK(vflist_pop_menu_sel_mark_or_cb), vf);
562 menu_item_add_sensitive(menu, str_sel_mark_and, active,
563 G_CALLBACK(vflist_pop_menu_sel_mark_and_cb), vf);
564 menu_item_add_sensitive(menu, str_sel_mark_minus, active,
565 G_CALLBACK(vflist_pop_menu_sel_mark_minus_cb), vf);
567 menu_item_add_divider(menu);
569 g_free(str_set_mark);
570 g_free(str_res_mark);
571 g_free(str_toggle_mark);
572 g_free(str_sel_mark);
573 g_free(str_sel_mark_and);
574 g_free(str_sel_mark_or);
575 g_free(str_sel_mark_minus);
578 submenu_add_edit(menu, &item, G_CALLBACK(vflist_pop_menu_edit_cb), vf);
579 gtk_widget_set_sensitive(item, active);
581 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
582 G_CALLBACK(vflist_pop_menu_info_cb), vf);
583 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
584 G_CALLBACK(vflist_pop_menu_view_cb), vf);
586 menu_item_add_divider(menu);
587 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
588 G_CALLBACK(vflist_pop_menu_copy_cb), vf);
589 menu_item_add_sensitive(menu, _("_Move..."), active,
590 G_CALLBACK(vflist_pop_menu_move_cb), vf);
591 menu_item_add_sensitive(menu, _("_Rename..."), active,
592 G_CALLBACK(vflist_pop_menu_rename_cb), vf);
593 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
594 G_CALLBACK(vflist_pop_menu_delete_cb), vf);
595 if (options->show_copy_path)
596 menu_item_add_sensitive(menu, _("_Copy path"), active,
597 G_CALLBACK(vflist_pop_menu_copy_path_cb), vf);
599 menu_item_add_divider(menu);
601 submenu = submenu_add_sort(NULL, G_CALLBACK(vflist_pop_menu_sort_cb), vf,
602 FALSE, FALSE, TRUE, vf->sort_method);
603 menu_item_add_divider(submenu);
604 menu_item_add_check(submenu, _("Ascending"), vf->sort_ascend,
605 G_CALLBACK(vflist_pop_menu_sort_ascend_cb), vf);
607 item = menu_item_add(menu, _("_Sort"), NULL, NULL);
608 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
610 menu_item_add_check(menu, _("View as _icons"), FALSE,
611 G_CALLBACK(vflist_pop_menu_icons_cb), vf);
612 menu_item_add_check(menu, _("Show _thumbnails"), VFLIST_INFO(vf, thumbs_enabled),
613 G_CALLBACK(vflist_pop_menu_thumbs_cb), vf);
614 menu_item_add_stock(menu, _("Re_fresh"), GTK_STOCK_REFRESH, G_CALLBACK(vflist_pop_menu_refresh_cb), vf);
620 *-----------------------------------------------------------------------------
622 *-----------------------------------------------------------------------------
625 static gint vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
631 if (strlen(new) == 0) return FALSE;
633 old_path = concat_dir_and_file(vf->path, old);
634 new_path = concat_dir_and_file(vf->path, new);
636 if (strchr(new, '/') != NULL)
638 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
639 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
642 else if (isfile(new_path))
644 gchar *text = g_strdup_printf(_("A file with name %s already exists."), new);
645 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
650 gint row = vflist_index_by_path(vf, old_path);
653 GList *work = g_list_nth(vf->list, row);
654 FileData *fd = work->data;
656 if (!file_data_add_change_info(fd, FILEDATA_CHANGE_RENAME, old_path, new_path) || !rename_file_ext(fd))
658 gchar *text = g_strdup_printf(_("Unable to rename file:\n%s\nto:\n%s"), old, new);
659 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
671 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
679 if (vflist_find_row(vf, VFLIST_INFO(vf, click_fd), &iter) < 0) return;
680 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
681 tpath = gtk_tree_model_get_path(store, &iter);
682 tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
683 gtk_tree_path_free(tpath);
685 popup_menu_position_clamp(menu, x, y, 0);
688 gint vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
693 if (event->keyval != GDK_Menu) return FALSE;
695 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
701 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
702 gtk_tree_model_get_iter(store, &iter, tpath);
703 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST_INFO(vf, click_fd), -1);
704 gtk_tree_path_free(tpath);
708 VFLIST_INFO(vf, click_fd) = NULL;
711 vf->popup = vflist_pop_menu(vf, VFLIST_INFO(vf, click_fd), 0);
712 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
717 gint vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
723 GtkTreeViewColumn *column;
726 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
727 &tpath, &column, NULL, NULL))
730 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
732 if (bevent->button == MOUSE_BUTTON_LEFT &&
733 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
736 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
738 gtk_tree_model_get_iter(store, &iter, tpath);
739 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
741 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE);
743 gtk_tree_path_free(tpath);
746 VFLIST_INFO(vf, click_fd) = fd;
748 if (bevent->button == MOUSE_BUTTON_RIGHT)
750 vf->popup = vflist_pop_menu(vf, VFLIST_INFO(vf, click_fd), col_idx);
751 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
752 bevent->button, bevent->time);
756 if (!fd) return FALSE;
758 if (bevent->button == MOUSE_BUTTON_MIDDLE)
760 if (!vflist_row_is_selected(vf, fd))
762 vflist_color_set(vf, fd, TRUE);
768 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
769 !(bevent->state & GDK_SHIFT_MASK ) &&
770 !(bevent->state & GDK_CONTROL_MASK ) &&
771 vflist_row_is_selected(vf, fd))
773 GtkTreeSelection *selection;
775 gtk_widget_grab_focus(widget);
778 /* returning FALSE and further processing of the event is needed for
779 correct operation of the expander, to show the sidecar files.
780 It however resets the selection of multiple files. With this condition
781 it should work for both cases */
782 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
783 return (gtk_tree_selection_count_selected_rows(selection) > 1);
787 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
789 if (vf->layout) layout_image_full_screen_start(vf->layout);
796 gint vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
803 if (bevent->button == MOUSE_BUTTON_MIDDLE)
805 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
808 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
813 if ((bevent->x != 0 || bevent->y != 0) &&
814 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
815 &tpath, NULL, NULL, NULL))
819 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
820 gtk_tree_model_get_iter(store, &iter, tpath);
821 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
822 gtk_tree_path_free(tpath);
825 if (bevent->button == MOUSE_BUTTON_MIDDLE)
827 if (fd && VFLIST_INFO(vf, click_fd) == fd)
829 GtkTreeSelection *selection;
831 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
832 if (vflist_row_is_selected(vf, fd))
834 gtk_tree_selection_unselect_iter(selection, &iter);
838 gtk_tree_selection_select_iter(selection, &iter);
844 if (fd && VFLIST_INFO(vf, click_fd) == fd &&
845 !(bevent->state & GDK_SHIFT_MASK ) &&
846 !(bevent->state & GDK_CONTROL_MASK ) &&
847 vflist_row_is_selected(vf, fd))
849 GtkTreeSelection *selection;
851 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
852 gtk_tree_selection_unselect_all(selection);
853 gtk_tree_selection_select_iter(selection, &iter);
854 vflist_move_cursor(vf, &iter);
855 // return TRUE;// FIXME - expand
861 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
863 FileData *read_ahead_fd = NULL;
868 cur_fd = layout_image_get_fd(vf->layout);
869 if (sel_fd == cur_fd) return; /* no change */
871 row = g_list_index(vf->list, sel_fd);
872 // FIXME sidecar data
874 if (sel_fd && options->image.enable_read_ahead && row >= 0)
876 if (row > g_list_index(vf->list, cur_fd) &&
877 row + 1 < vflist_count(vf, NULL))
879 read_ahead_fd = vflist_index_get_data(vf, row + 1);
883 read_ahead_fd = vflist_index_get_data(vf, row - 1);
887 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
890 static gint vflist_select_idle_cb(gpointer data)
896 VFLIST_INFO(vf, select_idle_id) = -1;
900 vflist_send_update(vf);
902 if (VFLIST_INFO(vf, select_fd))
904 vflist_select_image(vf, VFLIST_INFO(vf, select_fd));
905 VFLIST_INFO(vf, select_fd) = NULL;
908 VFLIST_INFO(vf, select_idle_id) = -1;
912 static void vflist_select_idle_cancel(ViewFile *vf)
914 if (VFLIST_INFO(vf, select_idle_id) != -1) g_source_remove(VFLIST_INFO(vf, select_idle_id));
915 VFLIST_INFO(vf, select_idle_id) = -1;
918 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
919 gboolean path_currently_selected, gpointer data)
924 if (!path_currently_selected &&
925 gtk_tree_model_get_iter(store, &iter, tpath))
927 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST_INFO(vf, select_fd), -1);
931 VFLIST_INFO(vf, select_fd) = NULL;
935 VFLIST_INFO(vf, select_idle_id) == -1)
937 VFLIST_INFO(vf, select_idle_id) = g_idle_add(vflist_select_idle_cb, vf);
944 *-----------------------------------------------------------------------------
946 *-----------------------------------------------------------------------------
950 static gboolean vflist_dummy_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
951 gboolean path_currently_selected, gpointer data)
958 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
962 gchar *sidecars = NULL;
964 if (fd->sidecar_files)
965 sidecars = sidecar_file_data_list_to_string(fd);
966 size = text_from_size(fd->size);
968 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
969 FILE_COLUMN_THUMB, (VFLIST_INFO(vf, thumbs_enabled)) ? fd->pixbuf : NULL,
970 FILE_COLUMN_NAME, fd->name,
971 FILE_COLUMN_SIDECARS, sidecars,
972 FILE_COLUMN_SIZE, size,
973 FILE_COLUMN_DATE, text_from_time(fd->date),
974 FILE_COLUMN_COLOR, FALSE, -1);
975 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
976 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, fd->marks[i], -1);
983 static void vflist_setup_iter_with_sidecars(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
989 vflist_setup_iter(vf, store, iter, fd);
992 /* this is almost the same code as in vflist_populate_view
993 maybe it should be made more generic and used in both places */
996 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &s_iter, iter);
998 work = fd->sidecar_files;
1002 FileData *sfd = work->data;
1007 FileData *old_sfd = NULL;
1011 gtk_tree_model_get(GTK_TREE_MODEL(store), &s_iter, FILE_COLUMN_POINTER, &old_sfd, -1);
1019 match = filelist_sort_compare_filedata_full(sfd, old_sfd, SORT_NAME, TRUE);
1034 gtk_tree_store_insert_before(store, &new, iter, &s_iter);
1038 gtk_tree_store_append(store, &new, iter);
1041 vflist_setup_iter(vf, store, &new, sfd);
1047 valid = gtk_tree_store_remove(store, &s_iter);
1051 vflist_setup_iter(vf, store, &s_iter, sfd);
1053 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &s_iter);
1063 valid = gtk_tree_store_remove(store, &s_iter);
1067 void vflist_sort_set(ViewFile *vf, SortType type, gint ascend)
1070 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
1072 GtkTreeStore *store;
1075 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1076 if (!vf->list) return;
1082 FileData *fd = work->data;
1083 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1088 vf->sort_method = type;
1089 vf->sort_ascend = ascend;
1091 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1093 new_order = g_malloc(i * sizeof(gint));
1099 FileData *fd = work->data;
1100 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1105 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1106 gtk_tree_store_reorder(store, NULL, new_order);
1109 g_hash_table_destroy(fd_idx_hash);
1113 *-----------------------------------------------------------------------------
1115 *-----------------------------------------------------------------------------
1118 static gint vflist_thumb_next(ViewFile *vf);
1120 static void vflist_thumb_status(ViewFile *vf, gdouble val, const gchar *text)
1122 if (vf->func_thumb_status)
1124 vf->func_thumb_status(vf, val, text, vf->data_thumb_status);
1128 static void vflist_thumb_cleanup(ViewFile *vf)
1130 vflist_thumb_status(vf, 0.0, NULL);
1132 vf->thumbs_count = 0;
1133 vf->thumbs_running = FALSE;
1135 thumb_loader_free(vf->thumbs_loader);
1136 vf->thumbs_loader = NULL;
1138 vf->thumbs_filedata = NULL;
1141 static void vflist_thumb_stop(ViewFile *vf)
1143 if (vf->thumbs_running) vflist_thumb_cleanup(vf);
1146 static void vflist_thumb_do(ViewFile *vf, ThumbLoader *tl, FileData *fd)
1148 GtkTreeStore *store;
1151 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1153 if (fd->pixbuf) g_object_unref(fd->pixbuf);
1154 fd->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
1156 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1157 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->pixbuf, -1);
1159 vflist_thumb_status(vf, (gdouble)(vf->thumbs_count) / vflist_sidecar_list_count(vf->list), _("Loading thumbs..."));
1162 static void vflist_thumb_error_cb(ThumbLoader *tl, gpointer data)
1164 ViewFile *vf = data;
1166 if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1168 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
1171 while (vflist_thumb_next(vf));
1174 static void vflist_thumb_done_cb(ThumbLoader *tl, gpointer data)
1176 ViewFile *vf = data;
1178 if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1180 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
1183 while (vflist_thumb_next(vf));
1186 static gint vflist_thumb_next(ViewFile *vf)
1189 FileData *fd = NULL;
1191 /* first check the visible files */
1193 if (GTK_WIDGET_REALIZED(vf->listview) &&
1194 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1196 GtkTreeModel *store;
1200 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1201 gtk_tree_model_get_iter(store, &iter, tpath);
1202 gtk_tree_path_free(tpath);
1204 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1206 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1207 if (fd->pixbuf) fd = NULL;
1209 valid = gtk_tree_model_iter_next(store, &iter);
1213 /* then find first undone */
1217 GList *work = vf->list;
1220 FileData *fd_p = work->data;
1225 GList *work2 = fd_p->sidecar_files;
1227 while (work2 && !fd)
1230 if (!fd_p->pixbuf) fd = fd_p;
1231 work2 = work2->next;
1241 vflist_thumb_cleanup(vf);
1247 vf->thumbs_filedata = fd;
1249 thumb_loader_free(vf->thumbs_loader);
1251 vf->thumbs_loader = thumb_loader_new(options->thumbnails.max_width, options->thumbnails.max_height);
1252 thumb_loader_set_callbacks(vf->thumbs_loader,
1253 vflist_thumb_done_cb,
1254 vflist_thumb_error_cb,
1258 if (!thumb_loader_start(vf->thumbs_loader, fd->path))
1260 /* set icon to unknown, continue */
1261 DEBUG_1("thumb loader start failed %s", vf->thumbs_loader->path);
1262 vflist_thumb_do(vf, vf->thumbs_loader, fd);
1270 static void vflist_thumb_update(ViewFile *vf)
1272 vflist_thumb_stop(vf);
1273 if (!VFLIST_INFO(vf, thumbs_enabled)) return;
1275 vflist_thumb_status(vf, 0.0, _("Loading thumbs..."));
1276 vf->thumbs_running = TRUE;
1278 while (vflist_thumb_next(vf));
1282 *-----------------------------------------------------------------------------
1284 *-----------------------------------------------------------------------------
1287 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1289 return g_list_nth_data(vf->list, row);
1292 gchar *vflist_index_get_path(ViewFile *vf, gint row)
1296 fd = g_list_nth_data(vf->list, row);
1298 return (fd ? fd->path : NULL);
1301 static gint vflist_row_by_path(ViewFile *vf, const gchar *path, FileData **fd)
1306 if (!path) return -1;
1311 FileData *fd_n = work->data;
1312 if (strcmp(path, fd_n->path) == 0)
1325 gint vflist_index_by_path(ViewFile *vf, const gchar *path)
1327 return vflist_row_by_path(vf, path, NULL);
1330 gint vflist_count(ViewFile *vf, gint64 *bytes)
1340 FileData *fd = work->data;
1348 return g_list_length(vf->list);
1351 GList *vflist_get_list(ViewFile *vf)
1359 FileData *fd = work->data;
1362 list = g_list_prepend(list, file_data_ref(fd));
1365 return g_list_reverse(list);
1369 *-----------------------------------------------------------------------------
1371 *-----------------------------------------------------------------------------
1374 static gint vflist_row_is_selected(ViewFile *vf, FileData *fd)
1376 GtkTreeModel *store;
1377 GtkTreeSelection *selection;
1382 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1383 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1385 while (!found && work)
1387 GtkTreePath *tpath = work->data;
1391 gtk_tree_model_get_iter(store, &iter, tpath);
1392 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1393 if (fd_n == fd) found = TRUE;
1396 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1402 gint vflist_index_is_selected(ViewFile *vf, gint row)
1406 fd = vflist_index_get_data(vf, row);
1407 return vflist_row_is_selected(vf, fd);
1410 gint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1412 GtkTreeModel *store;
1413 GtkTreeSelection *selection;
1417 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1418 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1428 GtkTreePath *tpath = work->data;
1432 gtk_tree_model_get_iter(store, &iter, tpath);
1433 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1442 count = g_list_length(slist);
1443 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1449 GList *vflist_selection_get_list(ViewFile *vf)
1451 GtkTreeModel *store;
1452 GtkTreeSelection *selection;
1457 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1458 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1462 GtkTreePath *tpath = work->data;
1466 gtk_tree_model_get_iter(store, &iter, tpath);
1467 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1469 list = g_list_prepend(list, file_data_ref(fd));
1473 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1476 return g_list_reverse(list);
1479 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1481 GtkTreeModel *store;
1482 GtkTreeSelection *selection;
1487 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1488 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1492 GtkTreePath *tpath = work->data;
1496 gtk_tree_model_get_iter(store, &iter, tpath);
1497 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1499 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1503 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1506 return g_list_reverse(list);
1509 void vflist_select_all(ViewFile *vf)
1511 GtkTreeSelection *selection;
1513 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1514 gtk_tree_selection_select_all(selection);
1516 VFLIST_INFO(vf, select_fd) = NULL;
1519 void vflist_select_none(ViewFile *vf)
1521 GtkTreeSelection *selection;
1523 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1524 gtk_tree_selection_unselect_all(selection);
1527 void vflist_select_by_path(ViewFile *vf, const gchar *path)
1531 if (vflist_row_by_path(vf, path, &fd) < 0) return;
1533 vflist_select_by_fd(vf, fd);
1536 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1540 if (vflist_find_row(vf, fd, &iter) < 0) return;
1542 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1544 if (!vflist_row_is_selected(vf, fd))
1546 GtkTreeSelection *selection;
1547 GtkTreeModel *store;
1550 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1551 gtk_tree_selection_unselect_all(selection);
1552 gtk_tree_selection_select_iter(selection, &iter);
1553 vflist_move_cursor(vf, &iter);
1555 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1556 tpath = gtk_tree_model_get_path(store, &iter);
1557 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1558 gtk_tree_path_free(tpath);
1562 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1564 GtkTreeModel *store;
1566 GtkTreeSelection *selection;
1569 g_assert(mark >= 0 && mark < FILEDATA_MARKS_SIZE);
1571 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1572 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1574 valid = gtk_tree_model_get_iter_first(store, &iter);
1578 gboolean mark_val, selected;
1579 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1581 mark_val = fd->marks[mark];
1582 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1586 case MTS_MODE_SET: selected = mark_val;
1588 case MTS_MODE_OR: selected = mark_val | selected;
1590 case MTS_MODE_AND: selected = mark_val & selected;
1592 case MTS_MODE_MINUS: selected = !mark_val & selected;
1597 gtk_tree_selection_select_iter(selection, &iter);
1599 gtk_tree_selection_unselect_iter(selection, &iter);
1601 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1605 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1607 GtkTreeModel *store;
1608 GtkTreeSelection *selection;
1612 g_assert(mark >= 0 && mark < FILEDATA_MARKS_SIZE);
1614 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1615 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1619 GtkTreePath *tpath = work->data;
1623 gtk_tree_model_get_iter(store, &iter, tpath);
1624 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1628 case STM_MODE_SET: fd->marks[mark] = 1;
1630 case STM_MODE_RESET: fd->marks[mark] = 0;
1632 case STM_MODE_TOGGLE: fd->marks[mark] = !fd->marks[mark];
1636 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_MARKS + mark, fd->marks[mark], -1);
1640 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1645 *-----------------------------------------------------------------------------
1647 *-----------------------------------------------------------------------------
1650 static void vflist_listview_set_height(GtkWidget *listview, gint thumb)
1652 GtkTreeViewColumn *column;
1653 GtkCellRenderer *cell;
1656 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_COLUMN_THUMB - 1);
1657 if (!column) return;
1659 gtk_tree_view_column_set_fixed_width(column, ((thumb) ? options->thumbnails.max_width : 4) + 10);
1661 list = gtk_tree_view_column_get_cell_renderers(column);
1666 g_object_set(G_OBJECT(cell), "height", (thumb) ? options->thumbnails.max_height : -1, NULL);
1667 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1670 static void vflist_populate_view(ViewFile *vf)
1672 GtkTreeStore *store;
1676 GtkTreeRowReference *visible_row = NULL;
1680 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1681 thumbs = VFLIST_INFO(vf, thumbs_enabled);
1683 vflist_thumb_stop(vf);
1687 gtk_tree_store_clear(store);
1688 vflist_send_update(vf);
1692 if (GTK_WIDGET_REALIZED(vf->listview) &&
1693 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1695 visible_row = gtk_tree_row_reference_new(GTK_TREE_MODEL(store), tpath);
1696 gtk_tree_path_free(tpath);
1699 vflist_listview_set_height(vf->listview, thumbs);
1701 valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
1707 FileData *fd = work->data;
1712 FileData *old_fd = NULL;
1716 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1724 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
1726 match = -1; /* probably should not happen*/
1741 gtk_tree_store_insert_before(store, &new, NULL, &iter);
1745 gtk_tree_store_append(store, &new, NULL);
1747 vflist_setup_iter_with_sidecars(vf, store, &new, fd);
1753 valid = gtk_tree_store_remove(store, &iter);
1757 vflist_setup_iter_with_sidecars(vf, store, &iter, fd);
1759 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1769 valid = gtk_tree_store_remove(store, &iter);
1774 if (gtk_tree_row_reference_valid(visible_row))
1776 tpath = gtk_tree_row_reference_get_path(visible_row);
1777 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(vf->listview), tpath, NULL, TRUE, 0.0, 0.0);
1778 gtk_tree_path_free(tpath);
1780 gtk_tree_row_reference_free(visible_row);
1783 vflist_send_update(vf);
1784 vflist_thumb_update(vf);
1787 gint vflist_refresh(ViewFile *vf)
1792 old_list = vf->list;
1797 ret = filelist_read(vf->path, &vf->list, NULL);
1800 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1801 vflist_populate_view(vf);
1803 filelist_free(old_list);
1808 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1810 #define CELL_HEIGHT_OVERRIDE 512
1812 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1816 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1817 if (spec && G_IS_PARAM_SPEC_INT(spec))
1819 GParamSpecInt *spec_int;
1821 spec_int = G_PARAM_SPEC_INT(spec);
1822 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1826 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1828 static GdkColor color;
1829 static GtkWidget *done = NULL;
1835 style = gtk_widget_get_style(widget);
1836 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1837 shift_color(&color, -1, 0);
1844 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1845 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1847 ViewFile *vf = data;
1850 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1851 g_object_set(G_OBJECT(cell),
1852 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1853 "cell-background-set", set, NULL);
1856 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gint image, gint right_justify, gint expand)
1858 GtkTreeViewColumn *column;
1859 GtkCellRenderer *renderer;
1861 column = gtk_tree_view_column_new();
1862 gtk_tree_view_column_set_title(column, title);
1863 gtk_tree_view_column_set_min_width(column, 4);
1867 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1868 renderer = gtk_cell_renderer_text_new();
1871 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1873 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1874 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1876 gtk_tree_view_column_set_expand(column, TRUE);
1880 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1881 renderer = gtk_cell_renderer_pixbuf_new();
1882 cell_renderer_height_override(renderer);
1883 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1884 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1887 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1888 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1889 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1891 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1894 static void vflist_listview_mark_toggled(GtkCellRendererToggle *cell, gchar *path_str, GtkTreeStore *store)
1896 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1902 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1905 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1907 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1909 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &mark, -1);
1911 fd->marks[col_idx - FILE_COLUMN_MARKS] = mark;
1913 gtk_tree_store_set(store, &iter, col_idx, mark, -1);
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, 16);
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), store);
1942 *-----------------------------------------------------------------------------
1944 *-----------------------------------------------------------------------------
1947 gint vflist_set_path(ViewFile *vf, const gchar *path)
1949 GtkTreeStore *store;
1951 if (!path) return FALSE;
1952 if (vf->path && strcmp(path, vf->path) == 0) return TRUE;
1955 vf->path = g_strdup(path);
1957 /* force complete reload */
1958 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1959 gtk_tree_store_clear(store);
1961 filelist_free(vf->list);
1964 return vflist_refresh(vf);
1967 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1969 ViewFile *vf = data;
1971 vflist_select_idle_cancel(vf);
1972 vflist_thumb_stop(vf);
1974 filelist_free(vf->list);
1977 ViewFile *vflist_new(ViewFile *vf, const gchar *path)
1979 GtkTreeStore *store;
1980 GtkTreeSelection *selection;
1982 GType flist_types[FILE_COLUMN_COUNT];
1985 vf->info = g_new0(ViewFileInfoList, 1);
1987 VFLIST_INFO(vf, click_fd) = NULL;
1988 VFLIST_INFO(vf, select_fd) = NULL;
1989 VFLIST_INFO(vf, thumbs_enabled) = FALSE;
1991 VFLIST_INFO(vf, select_idle_id) = -1;
1993 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1994 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1995 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1996 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
1997 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1998 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1999 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
2000 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
2001 flist_types[i] = G_TYPE_BOOLEAN;
2003 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
2005 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2006 g_object_unref(store);
2008 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2009 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
2010 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
2012 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2013 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2015 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2017 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2018 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2020 vflist_listview_add_column(vf, FILE_COLUMN_NAME, _("Name"), FALSE, FALSE, FALSE);
2021 vflist_listview_add_column(vf, FILE_COLUMN_SIDECARS, _("SC"), FALSE, FALSE, FALSE);
2023 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2024 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2029 void vflist_thumb_set(ViewFile *vf, gint enable)
2031 if (VFLIST_INFO(vf, thumbs_enabled) == enable) return;
2033 VFLIST_INFO(vf, thumbs_enabled) = enable;
2034 if (vf->layout) vflist_refresh(vf);
2037 void vflist_marks_set(ViewFile *vf, gint enable)
2039 GList *columns, *work;
2041 if (vf->marks_enabled == enable) return;
2043 vf->marks_enabled = enable;
2045 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2050 GtkTreeViewColumn *column = work->data;
2051 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2054 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2055 gtk_tree_view_column_set_visible(column, enable);
2058 g_list_free(columns);
2059 //vflist_refresh(vf);
2063 *-----------------------------------------------------------------------------
2064 * maintenance (for rename, move, remove)
2065 *-----------------------------------------------------------------------------
2068 static gint vflist_maint_find_closest(ViewFile *vf, gint row, gint count, GList *ignore_list)
2078 gint f = vflist_index_by_path(vf, work->data);
2079 if (f >= 0) list = g_list_prepend(list, GINT_TO_POINTER(f));
2089 gpointer p = work->data;
2091 if (row == GPOINTER_TO_INT(p))
2096 if (rev == GPOINTER_TO_INT(p))
2101 if (!c) list = g_list_remove(list, p);
2109 if (row > count - 1)
2122 gint vflist_maint_renamed(ViewFile *vf, FileData *fd)
2128 if (g_list_index(vf->list, fd) < 0) return FALSE;
2130 source_base = remove_level_from_path(fd->change->source);
2131 dest_base = remove_level_from_path(fd->change->dest);
2134 if (strcmp(source_base, dest_base) == 0)
2136 GtkTreeStore *store;
2138 GtkTreeIter position;
2142 old_row = g_list_index(vf->list, fd);
2144 vf->list = g_list_remove(vf->list, fd);
2146 vf->list = filelist_insert_sort(vf->list, fd, vf->sort_method, vf->sort_ascend);
2147 n = g_list_index(vf->list, fd);
2149 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2150 if (vflist_find_row(vf, fd, &iter) >= 0 &&
2151 gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &position, NULL, n))
2155 gtk_tree_store_move_before(store, &iter, &position);
2159 gtk_tree_store_move_after(store, &iter, &position);
2162 gtk_tree_store_set(store, &iter, FILE_COLUMN_NAME, fd->name, -1);
2168 ret = vflist_maint_removed(vf, fd, NULL);
2171 g_free(source_base);
2177 gint vflist_maint_removed(ViewFile *vf, FileData *fd, GList *ignore_list)
2184 row = g_list_index(vf->list, fd);
2185 if (row < 0) return FALSE;
2187 if (vflist_index_is_selected(vf, row) &&
2188 layout_image_get_collection(vf->layout, NULL) == NULL)
2192 n = vflist_count(vf, NULL);
2195 new_row = vflist_maint_find_closest(vf, row, n, ignore_list);
2196 DEBUG_1("row = %d, closest is %d", row, new_row);
2209 vflist_select_none(vf);
2212 fd = vflist_index_get_data(vf, new_row);
2213 if (vflist_find_row(vf, fd, &iter) >= 0)
2215 GtkTreeSelection *selection;
2217 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2218 gtk_tree_selection_select_iter(selection, &iter);
2219 vflist_move_cursor(vf, &iter);
2224 fd = vflist_index_get_data(vf, row);
2225 if (vflist_find_row(vf, fd, &iter) >= 0)
2227 GtkTreeStore *store;
2228 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2229 gtk_tree_store_remove(store, &iter);
2231 list = g_list_nth(vf->list, row);
2234 /* thumbnail loader check */
2235 if (fd == vf->thumbs_filedata) vf->thumbs_filedata = NULL;
2236 if (vf->thumbs_count > 0) vf->thumbs_count--;
2238 vf->list = g_list_remove(vf->list, fd);
2239 file_data_unref(fd);
2241 vflist_send_update(vf);
2246 gint vflist_maint_moved(ViewFile *vf, FileData *fd, GList *ignore_list)
2251 if (!fd->change->source || !vf->path) return FALSE;
2253 buf = remove_level_from_path(fd->change->source);
2255 if (strcmp(buf, vf->path) == 0)
2257 ret = vflist_maint_removed(vf, fd, ignore_list);