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 = file_data_sc_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_fd(ViewFile *vf, FileData *fd)
1531 if (vflist_find_row(vf, fd, &iter) < 0) return;
1533 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1535 if (!vflist_row_is_selected(vf, fd))
1537 GtkTreeSelection *selection;
1538 GtkTreeModel *store;
1541 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1542 gtk_tree_selection_unselect_all(selection);
1543 gtk_tree_selection_select_iter(selection, &iter);
1544 vflist_move_cursor(vf, &iter);
1546 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1547 tpath = gtk_tree_model_get_path(store, &iter);
1548 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1549 gtk_tree_path_free(tpath);
1553 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1555 GtkTreeModel *store;
1557 GtkTreeSelection *selection;
1560 g_assert(mark >= 0 && mark < FILEDATA_MARKS_SIZE);
1562 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1563 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1565 valid = gtk_tree_model_get_iter_first(store, &iter);
1569 gboolean mark_val, selected;
1570 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1572 mark_val = fd->marks[mark];
1573 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1577 case MTS_MODE_SET: selected = mark_val;
1579 case MTS_MODE_OR: selected = mark_val | selected;
1581 case MTS_MODE_AND: selected = mark_val & selected;
1583 case MTS_MODE_MINUS: selected = !mark_val & selected;
1588 gtk_tree_selection_select_iter(selection, &iter);
1590 gtk_tree_selection_unselect_iter(selection, &iter);
1592 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1596 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1598 GtkTreeModel *store;
1599 GtkTreeSelection *selection;
1603 g_assert(mark >= 0 && mark < FILEDATA_MARKS_SIZE);
1605 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1606 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1610 GtkTreePath *tpath = work->data;
1614 gtk_tree_model_get_iter(store, &iter, tpath);
1615 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1619 case STM_MODE_SET: fd->marks[mark] = 1;
1621 case STM_MODE_RESET: fd->marks[mark] = 0;
1623 case STM_MODE_TOGGLE: fd->marks[mark] = !fd->marks[mark];
1627 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_MARKS + mark, fd->marks[mark], -1);
1631 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1636 *-----------------------------------------------------------------------------
1638 *-----------------------------------------------------------------------------
1641 static void vflist_listview_set_height(GtkWidget *listview, gint thumb)
1643 GtkTreeViewColumn *column;
1644 GtkCellRenderer *cell;
1647 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_COLUMN_THUMB - 1);
1648 if (!column) return;
1650 gtk_tree_view_column_set_fixed_width(column, ((thumb) ? options->thumbnails.max_width : 4) + 10);
1652 list = gtk_tree_view_column_get_cell_renderers(column);
1657 g_object_set(G_OBJECT(cell), "height", (thumb) ? options->thumbnails.max_height : -1, NULL);
1658 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1661 static void vflist_populate_view(ViewFile *vf)
1663 GtkTreeStore *store;
1667 GtkTreeRowReference *visible_row = NULL;
1671 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1672 thumbs = VFLIST_INFO(vf, thumbs_enabled);
1674 vflist_thumb_stop(vf);
1678 gtk_tree_store_clear(store);
1679 vflist_send_update(vf);
1683 if (GTK_WIDGET_REALIZED(vf->listview) &&
1684 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1686 visible_row = gtk_tree_row_reference_new(GTK_TREE_MODEL(store), tpath);
1687 gtk_tree_path_free(tpath);
1690 vflist_listview_set_height(vf->listview, thumbs);
1692 valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
1698 FileData *fd = work->data;
1703 FileData *old_fd = NULL;
1707 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1715 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
1717 match = -1; /* probably should not happen*/
1732 gtk_tree_store_insert_before(store, &new, NULL, &iter);
1736 gtk_tree_store_append(store, &new, NULL);
1738 vflist_setup_iter_with_sidecars(vf, store, &new, fd);
1744 valid = gtk_tree_store_remove(store, &iter);
1748 vflist_setup_iter_with_sidecars(vf, store, &iter, fd);
1750 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1760 valid = gtk_tree_store_remove(store, &iter);
1765 if (gtk_tree_row_reference_valid(visible_row))
1767 tpath = gtk_tree_row_reference_get_path(visible_row);
1768 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(vf->listview), tpath, NULL, TRUE, 0.0, 0.0);
1769 gtk_tree_path_free(tpath);
1771 gtk_tree_row_reference_free(visible_row);
1774 vflist_send_update(vf);
1775 vflist_thumb_update(vf);
1778 gint vflist_refresh(ViewFile *vf)
1783 old_list = vf->list;
1788 ret = filelist_read(vf->path, &vf->list, NULL);
1791 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1792 vflist_populate_view(vf);
1794 filelist_free(old_list);
1799 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1801 #define CELL_HEIGHT_OVERRIDE 512
1803 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1807 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1808 if (spec && G_IS_PARAM_SPEC_INT(spec))
1810 GParamSpecInt *spec_int;
1812 spec_int = G_PARAM_SPEC_INT(spec);
1813 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1817 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1819 static GdkColor color;
1820 static GtkWidget *done = NULL;
1826 style = gtk_widget_get_style(widget);
1827 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1828 shift_color(&color, -1, 0);
1835 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1836 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1838 ViewFile *vf = data;
1841 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1842 g_object_set(G_OBJECT(cell),
1843 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1844 "cell-background-set", set, NULL);
1847 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gint image, gint right_justify, gint expand)
1849 GtkTreeViewColumn *column;
1850 GtkCellRenderer *renderer;
1852 column = gtk_tree_view_column_new();
1853 gtk_tree_view_column_set_title(column, title);
1854 gtk_tree_view_column_set_min_width(column, 4);
1858 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1859 renderer = gtk_cell_renderer_text_new();
1862 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1864 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1865 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1867 gtk_tree_view_column_set_expand(column, TRUE);
1871 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1872 renderer = gtk_cell_renderer_pixbuf_new();
1873 cell_renderer_height_override(renderer);
1874 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1875 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1878 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1879 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1880 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1882 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1885 static void vflist_listview_mark_toggled(GtkCellRendererToggle *cell, gchar *path_str, GtkTreeStore *store)
1887 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1893 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1896 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1898 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1900 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &mark, -1);
1902 fd->marks[col_idx - FILE_COLUMN_MARKS] = mark;
1904 gtk_tree_store_set(store, &iter, col_idx, mark, -1);
1905 gtk_tree_path_free(path);
1908 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1910 GtkTreeViewColumn *column;
1911 GtkCellRenderer *renderer;
1912 GtkTreeStore *store;
1915 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1917 renderer = gtk_cell_renderer_toggle_new();
1918 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1920 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1921 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1922 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1924 index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1925 gtk_tree_view_column_set_fixed_width(column, 16);
1926 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1929 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled), store);
1933 *-----------------------------------------------------------------------------
1935 *-----------------------------------------------------------------------------
1938 gint vflist_set_path(ViewFile *vf, const gchar *path)
1940 GtkTreeStore *store;
1942 if (!path) return FALSE;
1943 if (vf->path && strcmp(path, vf->path) == 0) return TRUE;
1946 vf->path = g_strdup(path);
1948 /* force complete reload */
1949 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1950 gtk_tree_store_clear(store);
1952 filelist_free(vf->list);
1955 return vflist_refresh(vf);
1958 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1960 ViewFile *vf = data;
1962 vflist_select_idle_cancel(vf);
1963 vflist_thumb_stop(vf);
1965 filelist_free(vf->list);
1968 ViewFile *vflist_new(ViewFile *vf, const gchar *path)
1970 GtkTreeStore *store;
1971 GtkTreeSelection *selection;
1973 GType flist_types[FILE_COLUMN_COUNT];
1976 vf->info = g_new0(ViewFileInfoList, 1);
1978 VFLIST_INFO(vf, click_fd) = NULL;
1979 VFLIST_INFO(vf, select_fd) = NULL;
1980 VFLIST_INFO(vf, thumbs_enabled) = FALSE;
1982 VFLIST_INFO(vf, select_idle_id) = -1;
1984 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1985 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1986 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1987 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
1988 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1989 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1990 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1991 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1992 flist_types[i] = G_TYPE_BOOLEAN;
1994 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
1996 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1997 g_object_unref(store);
1999 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2000 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
2001 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
2003 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2004 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2006 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2008 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2009 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2011 vflist_listview_add_column(vf, FILE_COLUMN_NAME, _("Name"), FALSE, FALSE, FALSE);
2012 vflist_listview_add_column(vf, FILE_COLUMN_SIDECARS, _("SC"), FALSE, FALSE, FALSE);
2014 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2015 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2020 void vflist_thumb_set(ViewFile *vf, gint enable)
2022 if (VFLIST_INFO(vf, thumbs_enabled) == enable) return;
2024 VFLIST_INFO(vf, thumbs_enabled) = enable;
2025 if (vf->layout) vflist_refresh(vf);
2028 void vflist_marks_set(ViewFile *vf, gint enable)
2030 GList *columns, *work;
2032 if (vf->marks_enabled == enable) return;
2034 vf->marks_enabled = enable;
2036 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2041 GtkTreeViewColumn *column = work->data;
2042 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2045 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2046 gtk_tree_view_column_set_visible(column, enable);
2049 g_list_free(columns);
2050 //vflist_refresh(vf);
2054 *-----------------------------------------------------------------------------
2055 * maintenance (for rename, move, remove)
2056 *-----------------------------------------------------------------------------
2059 static gint vflist_maint_find_closest(ViewFile *vf, gint row, gint count, GList *ignore_list)
2069 gint f = vflist_index_by_path(vf, work->data);
2070 if (f >= 0) list = g_list_prepend(list, GINT_TO_POINTER(f));
2080 gpointer p = work->data;
2082 if (row == GPOINTER_TO_INT(p))
2087 if (rev == GPOINTER_TO_INT(p))
2092 if (!c) list = g_list_remove(list, p);
2100 if (row > count - 1)
2113 gint vflist_maint_renamed(ViewFile *vf, FileData *fd)
2119 if (g_list_index(vf->list, fd) < 0) return FALSE;
2121 source_base = remove_level_from_path(fd->change->source);
2122 dest_base = remove_level_from_path(fd->change->dest);
2125 if (strcmp(source_base, dest_base) == 0)
2127 GtkTreeStore *store;
2129 GtkTreeIter position;
2133 old_row = g_list_index(vf->list, fd);
2135 vf->list = g_list_remove(vf->list, fd);
2137 vf->list = filelist_insert_sort(vf->list, fd, vf->sort_method, vf->sort_ascend);
2138 n = g_list_index(vf->list, fd);
2140 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2141 if (vflist_find_row(vf, fd, &iter) >= 0 &&
2142 gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &position, NULL, n))
2146 gtk_tree_store_move_before(store, &iter, &position);
2150 gtk_tree_store_move_after(store, &iter, &position);
2153 gtk_tree_store_set(store, &iter, FILE_COLUMN_NAME, fd->name, -1);
2159 ret = vflist_maint_removed(vf, fd, NULL);
2162 g_free(source_base);
2168 gint vflist_maint_removed(ViewFile *vf, FileData *fd, GList *ignore_list)
2175 row = g_list_index(vf->list, fd);
2176 if (row < 0) return FALSE;
2178 if (vflist_index_is_selected(vf, row) &&
2179 layout_image_get_collection(vf->layout, NULL) == NULL)
2183 n = vflist_count(vf, NULL);
2186 new_row = vflist_maint_find_closest(vf, row, n, ignore_list);
2187 DEBUG_1("row = %d, closest is %d", row, new_row);
2200 vflist_select_none(vf);
2203 fd = vflist_index_get_data(vf, new_row);
2204 if (vflist_find_row(vf, fd, &iter) >= 0)
2206 GtkTreeSelection *selection;
2208 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2209 gtk_tree_selection_select_iter(selection, &iter);
2210 vflist_move_cursor(vf, &iter);
2215 fd = vflist_index_get_data(vf, row);
2216 if (vflist_find_row(vf, fd, &iter) >= 0)
2218 GtkTreeStore *store;
2219 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2220 gtk_tree_store_remove(store, &iter);
2222 list = g_list_nth(vf->list, row);
2225 /* thumbnail loader check */
2226 if (fd == vf->thumbs_filedata) vf->thumbs_filedata = NULL;
2227 if (vf->thumbs_count > 0) vf->thumbs_count--;
2229 vf->list = g_list_remove(vf->list, fd);
2230 file_data_unref(fd);
2232 vflist_send_update(vf);
2237 gint vflist_maint_moved(ViewFile *vf, FileData *fd, GList *ignore_list)
2242 if (!fd->change->source || !vf->path) return FALSE;
2244 buf = remove_level_from_path(fd->change->source);
2246 if (strcmp(buf, vf->path) == 0)
2248 ret = vflist_maint_removed(vf, fd, ignore_list);