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 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1532 tpath = gtk_tree_model_get_path(store, iter);
1533 result = gtk_tree_path_prev(tpath);
1535 gtk_tree_model_get_iter(store, iter, tpath);
1537 gtk_tree_path_free(tpath);
1542 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1544 if (!gtk_tree_model_get_iter_first(store, iter))
1549 GtkTreeIter next = *iter;
1551 if (gtk_tree_model_iter_next(store, &next))
1560 void vflist_select_invert(ViewFile *vf)
1563 GtkTreeSelection *selection;
1564 GtkTreeModel *store;
1567 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1568 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1570 /* Backward iteration prevents scrolling to the end of the list,
1571 * it scrolls to the first selected row instead. */
1572 valid = tree_model_get_iter_last(store, &iter);
1576 gint selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1579 gtk_tree_selection_unselect_iter(selection, &iter);
1581 gtk_tree_selection_select_iter(selection, &iter);
1583 valid = tree_model_iter_prev(store, &iter);
1587 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1591 if (vflist_find_row(vf, fd, &iter) < 0) return;
1593 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1595 if (!vflist_row_is_selected(vf, fd))
1597 GtkTreeSelection *selection;
1598 GtkTreeModel *store;
1601 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1602 gtk_tree_selection_unselect_all(selection);
1603 gtk_tree_selection_select_iter(selection, &iter);
1604 vflist_move_cursor(vf, &iter);
1606 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1607 tpath = gtk_tree_model_get_path(store, &iter);
1608 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1609 gtk_tree_path_free(tpath);
1613 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1615 GtkTreeModel *store;
1617 GtkTreeSelection *selection;
1620 g_assert(mark >= 0 && mark < FILEDATA_MARKS_SIZE);
1622 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1623 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1625 valid = gtk_tree_model_get_iter_first(store, &iter);
1629 gboolean mark_val, selected;
1630 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1632 mark_val = fd->marks[mark];
1633 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1637 case MTS_MODE_SET: selected = mark_val;
1639 case MTS_MODE_OR: selected = mark_val | selected;
1641 case MTS_MODE_AND: selected = mark_val & selected;
1643 case MTS_MODE_MINUS: selected = !mark_val & selected;
1648 gtk_tree_selection_select_iter(selection, &iter);
1650 gtk_tree_selection_unselect_iter(selection, &iter);
1652 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1656 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1658 GtkTreeModel *store;
1659 GtkTreeSelection *selection;
1663 g_assert(mark >= 0 && mark < FILEDATA_MARKS_SIZE);
1665 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1666 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1670 GtkTreePath *tpath = work->data;
1674 gtk_tree_model_get_iter(store, &iter, tpath);
1675 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1679 case STM_MODE_SET: fd->marks[mark] = 1;
1681 case STM_MODE_RESET: fd->marks[mark] = 0;
1683 case STM_MODE_TOGGLE: fd->marks[mark] = !fd->marks[mark];
1687 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_MARKS + mark, fd->marks[mark], -1);
1691 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1696 *-----------------------------------------------------------------------------
1698 *-----------------------------------------------------------------------------
1701 static void vflist_listview_set_height(GtkWidget *listview, gint thumb)
1703 GtkTreeViewColumn *column;
1704 GtkCellRenderer *cell;
1707 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_COLUMN_THUMB - 1);
1708 if (!column) return;
1710 gtk_tree_view_column_set_fixed_width(column, ((thumb) ? options->thumbnails.max_width : 4) + 10);
1712 list = gtk_tree_view_column_get_cell_renderers(column);
1717 g_object_set(G_OBJECT(cell), "height", (thumb) ? options->thumbnails.max_height : -1, NULL);
1718 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1721 static void vflist_populate_view(ViewFile *vf)
1723 GtkTreeStore *store;
1727 GtkTreeRowReference *visible_row = NULL;
1731 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1732 thumbs = VFLIST_INFO(vf, thumbs_enabled);
1734 vflist_thumb_stop(vf);
1738 gtk_tree_store_clear(store);
1739 vflist_send_update(vf);
1743 if (GTK_WIDGET_REALIZED(vf->listview) &&
1744 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1746 visible_row = gtk_tree_row_reference_new(GTK_TREE_MODEL(store), tpath);
1747 gtk_tree_path_free(tpath);
1750 vflist_listview_set_height(vf->listview, thumbs);
1752 valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
1758 FileData *fd = work->data;
1763 FileData *old_fd = NULL;
1767 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1775 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
1777 match = -1; /* probably should not happen*/
1792 gtk_tree_store_insert_before(store, &new, NULL, &iter);
1796 gtk_tree_store_append(store, &new, NULL);
1798 vflist_setup_iter_with_sidecars(vf, store, &new, fd);
1804 valid = gtk_tree_store_remove(store, &iter);
1808 vflist_setup_iter_with_sidecars(vf, store, &iter, fd);
1810 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1820 valid = gtk_tree_store_remove(store, &iter);
1825 if (gtk_tree_row_reference_valid(visible_row))
1827 tpath = gtk_tree_row_reference_get_path(visible_row);
1828 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(vf->listview), tpath, NULL, TRUE, 0.0, 0.0);
1829 gtk_tree_path_free(tpath);
1831 gtk_tree_row_reference_free(visible_row);
1834 vflist_send_update(vf);
1835 vflist_thumb_update(vf);
1838 gint vflist_refresh(ViewFile *vf)
1843 old_list = vf->list;
1848 ret = filelist_read(vf->path, &vf->list, NULL);
1851 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1852 vflist_populate_view(vf);
1854 filelist_free(old_list);
1859 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1861 #define CELL_HEIGHT_OVERRIDE 512
1863 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1867 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1868 if (spec && G_IS_PARAM_SPEC_INT(spec))
1870 GParamSpecInt *spec_int;
1872 spec_int = G_PARAM_SPEC_INT(spec);
1873 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1877 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1879 static GdkColor color;
1880 static GtkWidget *done = NULL;
1886 style = gtk_widget_get_style(widget);
1887 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1888 shift_color(&color, -1, 0);
1895 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1896 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1898 ViewFile *vf = data;
1901 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1902 g_object_set(G_OBJECT(cell),
1903 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1904 "cell-background-set", set, NULL);
1907 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gint image, gint right_justify, gint expand)
1909 GtkTreeViewColumn *column;
1910 GtkCellRenderer *renderer;
1912 column = gtk_tree_view_column_new();
1913 gtk_tree_view_column_set_title(column, title);
1914 gtk_tree_view_column_set_min_width(column, 4);
1918 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1919 renderer = gtk_cell_renderer_text_new();
1922 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1924 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1925 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1927 gtk_tree_view_column_set_expand(column, TRUE);
1931 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1932 renderer = gtk_cell_renderer_pixbuf_new();
1933 cell_renderer_height_override(renderer);
1934 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1935 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1938 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1939 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1940 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1942 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1945 static void vflist_listview_mark_toggled(GtkCellRendererToggle *cell, gchar *path_str, GtkTreeStore *store)
1947 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1953 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1956 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1958 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1960 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &mark, -1);
1962 fd->marks[col_idx - FILE_COLUMN_MARKS] = mark;
1964 gtk_tree_store_set(store, &iter, col_idx, mark, -1);
1965 gtk_tree_path_free(path);
1968 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1970 GtkTreeViewColumn *column;
1971 GtkCellRenderer *renderer;
1972 GtkTreeStore *store;
1975 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1977 renderer = gtk_cell_renderer_toggle_new();
1978 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1980 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1981 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1982 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1984 index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1985 gtk_tree_view_column_set_fixed_width(column, 16);
1986 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1989 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled), store);
1993 *-----------------------------------------------------------------------------
1995 *-----------------------------------------------------------------------------
1998 gint vflist_set_path(ViewFile *vf, const gchar *path)
2000 GtkTreeStore *store;
2002 if (!path) return FALSE;
2003 if (vf->path && strcmp(path, vf->path) == 0) return TRUE;
2006 vf->path = g_strdup(path);
2008 /* force complete reload */
2009 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2010 gtk_tree_store_clear(store);
2012 filelist_free(vf->list);
2015 return vflist_refresh(vf);
2018 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
2020 ViewFile *vf = data;
2022 vflist_select_idle_cancel(vf);
2023 vflist_thumb_stop(vf);
2025 filelist_free(vf->list);
2028 ViewFile *vflist_new(ViewFile *vf, const gchar *path)
2030 GtkTreeStore *store;
2031 GtkTreeSelection *selection;
2033 GType flist_types[FILE_COLUMN_COUNT];
2036 vf->info = g_new0(ViewFileInfoList, 1);
2038 VFLIST_INFO(vf, click_fd) = NULL;
2039 VFLIST_INFO(vf, select_fd) = NULL;
2040 VFLIST_INFO(vf, thumbs_enabled) = FALSE;
2042 VFLIST_INFO(vf, select_idle_id) = -1;
2044 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
2045 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
2046 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
2047 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
2048 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
2049 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
2050 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
2051 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
2052 flist_types[i] = G_TYPE_BOOLEAN;
2054 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
2056 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2057 g_object_unref(store);
2059 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2060 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
2061 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
2063 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2064 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2066 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2068 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2069 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2071 vflist_listview_add_column(vf, FILE_COLUMN_NAME, _("Name"), FALSE, FALSE, FALSE);
2072 vflist_listview_add_column(vf, FILE_COLUMN_SIDECARS, _("SC"), FALSE, FALSE, FALSE);
2074 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2075 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2080 void vflist_thumb_set(ViewFile *vf, gint enable)
2082 if (VFLIST_INFO(vf, thumbs_enabled) == enable) return;
2084 VFLIST_INFO(vf, thumbs_enabled) = enable;
2085 if (vf->layout) vflist_refresh(vf);
2088 void vflist_marks_set(ViewFile *vf, gint enable)
2090 GList *columns, *work;
2092 if (vf->marks_enabled == enable) return;
2094 vf->marks_enabled = enable;
2096 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2101 GtkTreeViewColumn *column = work->data;
2102 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2105 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2106 gtk_tree_view_column_set_visible(column, enable);
2109 g_list_free(columns);
2110 //vflist_refresh(vf);
2114 *-----------------------------------------------------------------------------
2115 * maintenance (for rename, move, remove)
2116 *-----------------------------------------------------------------------------
2119 static gint vflist_maint_find_closest(ViewFile *vf, gint row, gint count, GList *ignore_list)
2129 gint f = vflist_index_by_path(vf, work->data);
2130 if (f >= 0) list = g_list_prepend(list, GINT_TO_POINTER(f));
2140 gpointer p = work->data;
2142 if (row == GPOINTER_TO_INT(p))
2147 if (rev == GPOINTER_TO_INT(p))
2152 if (!c) list = g_list_remove(list, p);
2160 if (row > count - 1)
2173 gint vflist_maint_renamed(ViewFile *vf, FileData *fd)
2179 if (g_list_index(vf->list, fd) < 0) return FALSE;
2181 source_base = remove_level_from_path(fd->change->source);
2182 dest_base = remove_level_from_path(fd->change->dest);
2185 if (strcmp(source_base, dest_base) == 0)
2187 GtkTreeStore *store;
2189 GtkTreeIter position;
2193 old_row = g_list_index(vf->list, fd);
2195 vf->list = g_list_remove(vf->list, fd);
2197 vf->list = filelist_insert_sort(vf->list, fd, vf->sort_method, vf->sort_ascend);
2198 n = g_list_index(vf->list, fd);
2200 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2201 if (vflist_find_row(vf, fd, &iter) >= 0 &&
2202 gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &position, NULL, n))
2206 gtk_tree_store_move_before(store, &iter, &position);
2210 gtk_tree_store_move_after(store, &iter, &position);
2213 gtk_tree_store_set(store, &iter, FILE_COLUMN_NAME, fd->name, -1);
2219 ret = vflist_maint_removed(vf, fd, NULL);
2222 g_free(source_base);
2228 gint vflist_maint_removed(ViewFile *vf, FileData *fd, GList *ignore_list)
2235 row = g_list_index(vf->list, fd);
2236 if (row < 0) return FALSE;
2238 if (vflist_index_is_selected(vf, row) &&
2239 layout_image_get_collection(vf->layout, NULL) == NULL)
2243 n = vflist_count(vf, NULL);
2246 new_row = vflist_maint_find_closest(vf, row, n, ignore_list);
2247 DEBUG_1("row = %d, closest is %d", row, new_row);
2260 vflist_select_none(vf);
2263 fd = vflist_index_get_data(vf, new_row);
2264 if (vflist_find_row(vf, fd, &iter) >= 0)
2266 GtkTreeSelection *selection;
2268 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2269 gtk_tree_selection_select_iter(selection, &iter);
2270 vflist_move_cursor(vf, &iter);
2275 fd = vflist_index_get_data(vf, row);
2276 if (vflist_find_row(vf, fd, &iter) >= 0)
2278 GtkTreeStore *store;
2279 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2280 gtk_tree_store_remove(store, &iter);
2282 list = g_list_nth(vf->list, row);
2285 /* thumbnail loader check */
2286 if (fd == vf->thumbs_filedata) vf->thumbs_filedata = NULL;
2287 if (vf->thumbs_count > 0) vf->thumbs_count--;
2289 vf->list = g_list_remove(vf->list, fd);
2290 file_data_unref(fd);
2292 vflist_send_update(vf);
2297 gint vflist_maint_moved(ViewFile *vf, FileData *fd, GList *ignore_list)
2302 if (!fd->change->source || !vf->path) return FALSE;
2304 buf = remove_level_from_path(fd->change->source);
2306 if (strcmp(buf, vf->path) == 0)
2308 ret = vflist_maint_removed(vf, fd, ignore_list);