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 static gint vflist_row_by_path(ViewFile *vf, const gchar *path, FileData **fd)
1297 if (!path) return -1;
1302 FileData *fd_n = work->data;
1303 if (strcmp(path, fd_n->path) == 0)
1316 gint vflist_index_by_path(ViewFile *vf, const gchar *path)
1318 return vflist_row_by_path(vf, path, NULL);
1321 gint vflist_count(ViewFile *vf, gint64 *bytes)
1331 FileData *fd = work->data;
1339 return g_list_length(vf->list);
1342 GList *vflist_get_list(ViewFile *vf)
1350 FileData *fd = work->data;
1353 list = g_list_prepend(list, file_data_ref(fd));
1356 return g_list_reverse(list);
1360 *-----------------------------------------------------------------------------
1362 *-----------------------------------------------------------------------------
1365 static gint vflist_row_is_selected(ViewFile *vf, FileData *fd)
1367 GtkTreeModel *store;
1368 GtkTreeSelection *selection;
1373 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1374 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1376 while (!found && work)
1378 GtkTreePath *tpath = work->data;
1382 gtk_tree_model_get_iter(store, &iter, tpath);
1383 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1384 if (fd_n == fd) found = TRUE;
1387 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1393 gint vflist_index_is_selected(ViewFile *vf, gint row)
1397 fd = vflist_index_get_data(vf, row);
1398 return vflist_row_is_selected(vf, fd);
1401 gint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1403 GtkTreeModel *store;
1404 GtkTreeSelection *selection;
1408 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1409 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1419 GtkTreePath *tpath = work->data;
1423 gtk_tree_model_get_iter(store, &iter, tpath);
1424 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1433 count = g_list_length(slist);
1434 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1440 GList *vflist_selection_get_list(ViewFile *vf)
1442 GtkTreeModel *store;
1443 GtkTreeSelection *selection;
1448 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1449 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1453 GtkTreePath *tpath = work->data;
1457 gtk_tree_model_get_iter(store, &iter, tpath);
1458 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1460 list = g_list_prepend(list, file_data_ref(fd));
1464 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1467 return g_list_reverse(list);
1470 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1472 GtkTreeModel *store;
1473 GtkTreeSelection *selection;
1478 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1479 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1483 GtkTreePath *tpath = work->data;
1487 gtk_tree_model_get_iter(store, &iter, tpath);
1488 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1490 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1494 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1497 return g_list_reverse(list);
1500 void vflist_select_all(ViewFile *vf)
1502 GtkTreeSelection *selection;
1504 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1505 gtk_tree_selection_select_all(selection);
1507 VFLIST_INFO(vf, select_fd) = NULL;
1510 void vflist_select_none(ViewFile *vf)
1512 GtkTreeSelection *selection;
1514 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1515 gtk_tree_selection_unselect_all(selection);
1518 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1523 tpath = gtk_tree_model_get_path(store, iter);
1524 result = gtk_tree_path_prev(tpath);
1526 gtk_tree_model_get_iter(store, iter, tpath);
1528 gtk_tree_path_free(tpath);
1533 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1535 if (!gtk_tree_model_get_iter_first(store, iter))
1540 GtkTreeIter next = *iter;
1542 if (gtk_tree_model_iter_next(store, &next))
1551 void vflist_select_invert(ViewFile *vf)
1554 GtkTreeSelection *selection;
1555 GtkTreeModel *store;
1558 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1559 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1561 /* Backward iteration prevents scrolling to the end of the list,
1562 * it scrolls to the first selected row instead. */
1563 valid = tree_model_get_iter_last(store, &iter);
1567 gint selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1570 gtk_tree_selection_unselect_iter(selection, &iter);
1572 gtk_tree_selection_select_iter(selection, &iter);
1574 valid = tree_model_iter_prev(store, &iter);
1578 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1582 if (vflist_find_row(vf, fd, &iter) < 0) return;
1584 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1586 if (!vflist_row_is_selected(vf, fd))
1588 GtkTreeSelection *selection;
1589 GtkTreeModel *store;
1592 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1593 gtk_tree_selection_unselect_all(selection);
1594 gtk_tree_selection_select_iter(selection, &iter);
1595 vflist_move_cursor(vf, &iter);
1597 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1598 tpath = gtk_tree_model_get_path(store, &iter);
1599 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1600 gtk_tree_path_free(tpath);
1604 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1606 GtkTreeModel *store;
1608 GtkTreeSelection *selection;
1611 g_assert(mark >= 0 && mark < FILEDATA_MARKS_SIZE);
1613 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1614 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1616 valid = gtk_tree_model_get_iter_first(store, &iter);
1620 gboolean mark_val, selected;
1621 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1623 mark_val = fd->marks[mark];
1624 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1628 case MTS_MODE_SET: selected = mark_val;
1630 case MTS_MODE_OR: selected = mark_val | selected;
1632 case MTS_MODE_AND: selected = mark_val & selected;
1634 case MTS_MODE_MINUS: selected = !mark_val & selected;
1639 gtk_tree_selection_select_iter(selection, &iter);
1641 gtk_tree_selection_unselect_iter(selection, &iter);
1643 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1647 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1649 GtkTreeModel *store;
1650 GtkTreeSelection *selection;
1654 g_assert(mark >= 0 && mark < FILEDATA_MARKS_SIZE);
1656 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1657 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1661 GtkTreePath *tpath = work->data;
1665 gtk_tree_model_get_iter(store, &iter, tpath);
1666 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1670 case STM_MODE_SET: fd->marks[mark] = 1;
1672 case STM_MODE_RESET: fd->marks[mark] = 0;
1674 case STM_MODE_TOGGLE: fd->marks[mark] = !fd->marks[mark];
1678 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_MARKS + mark, fd->marks[mark], -1);
1682 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1687 *-----------------------------------------------------------------------------
1689 *-----------------------------------------------------------------------------
1692 static void vflist_listview_set_height(GtkWidget *listview, gint thumb)
1694 GtkTreeViewColumn *column;
1695 GtkCellRenderer *cell;
1698 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_COLUMN_THUMB - 1);
1699 if (!column) return;
1701 gtk_tree_view_column_set_fixed_width(column, ((thumb) ? options->thumbnails.max_width : 4) + 10);
1703 list = gtk_tree_view_column_get_cell_renderers(column);
1708 g_object_set(G_OBJECT(cell), "height", (thumb) ? options->thumbnails.max_height : -1, NULL);
1709 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1712 static void vflist_populate_view(ViewFile *vf)
1714 GtkTreeStore *store;
1718 GtkTreeRowReference *visible_row = NULL;
1722 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1723 thumbs = VFLIST_INFO(vf, thumbs_enabled);
1725 vflist_thumb_stop(vf);
1729 gtk_tree_store_clear(store);
1730 vflist_send_update(vf);
1734 if (GTK_WIDGET_REALIZED(vf->listview) &&
1735 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1737 visible_row = gtk_tree_row_reference_new(GTK_TREE_MODEL(store), tpath);
1738 gtk_tree_path_free(tpath);
1741 vflist_listview_set_height(vf->listview, thumbs);
1743 valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
1749 FileData *fd = work->data;
1754 FileData *old_fd = NULL;
1758 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1766 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
1768 match = -1; /* probably should not happen*/
1783 gtk_tree_store_insert_before(store, &new, NULL, &iter);
1787 gtk_tree_store_append(store, &new, NULL);
1789 vflist_setup_iter_with_sidecars(vf, store, &new, fd);
1795 valid = gtk_tree_store_remove(store, &iter);
1799 vflist_setup_iter_with_sidecars(vf, store, &iter, fd);
1801 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1811 valid = gtk_tree_store_remove(store, &iter);
1816 if (gtk_tree_row_reference_valid(visible_row))
1818 tpath = gtk_tree_row_reference_get_path(visible_row);
1819 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(vf->listview), tpath, NULL, TRUE, 0.0, 0.0);
1820 gtk_tree_path_free(tpath);
1822 gtk_tree_row_reference_free(visible_row);
1825 vflist_send_update(vf);
1826 vflist_thumb_update(vf);
1829 gint vflist_refresh(ViewFile *vf)
1834 old_list = vf->list;
1839 ret = filelist_read(vf->path, &vf->list, NULL);
1842 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1843 vflist_populate_view(vf);
1845 filelist_free(old_list);
1850 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1852 #define CELL_HEIGHT_OVERRIDE 512
1854 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1858 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1859 if (spec && G_IS_PARAM_SPEC_INT(spec))
1861 GParamSpecInt *spec_int;
1863 spec_int = G_PARAM_SPEC_INT(spec);
1864 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1868 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1870 static GdkColor color;
1871 static GtkWidget *done = NULL;
1877 style = gtk_widget_get_style(widget);
1878 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1879 shift_color(&color, -1, 0);
1886 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1887 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1889 ViewFile *vf = data;
1892 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1893 g_object_set(G_OBJECT(cell),
1894 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1895 "cell-background-set", set, NULL);
1898 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gint image, gint right_justify, gint expand)
1900 GtkTreeViewColumn *column;
1901 GtkCellRenderer *renderer;
1903 column = gtk_tree_view_column_new();
1904 gtk_tree_view_column_set_title(column, title);
1905 gtk_tree_view_column_set_min_width(column, 4);
1909 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1910 renderer = gtk_cell_renderer_text_new();
1913 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1915 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1916 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1918 gtk_tree_view_column_set_expand(column, TRUE);
1922 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1923 renderer = gtk_cell_renderer_pixbuf_new();
1924 cell_renderer_height_override(renderer);
1925 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1926 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1929 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1930 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1931 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1933 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1936 static void vflist_listview_mark_toggled(GtkCellRendererToggle *cell, gchar *path_str, GtkTreeStore *store)
1938 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1944 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1947 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1949 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1951 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &mark, -1);
1953 fd->marks[col_idx - FILE_COLUMN_MARKS] = mark;
1955 gtk_tree_store_set(store, &iter, col_idx, mark, -1);
1956 gtk_tree_path_free(path);
1959 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1961 GtkTreeViewColumn *column;
1962 GtkCellRenderer *renderer;
1963 GtkTreeStore *store;
1966 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1968 renderer = gtk_cell_renderer_toggle_new();
1969 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1971 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1972 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1973 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1975 index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1976 gtk_tree_view_column_set_fixed_width(column, 16);
1977 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1980 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled), store);
1984 *-----------------------------------------------------------------------------
1986 *-----------------------------------------------------------------------------
1989 gint vflist_set_path(ViewFile *vf, const gchar *path)
1991 GtkTreeStore *store;
1993 if (!path) return FALSE;
1994 if (vf->path && strcmp(path, vf->path) == 0) return TRUE;
1997 vf->path = g_strdup(path);
1999 /* force complete reload */
2000 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2001 gtk_tree_store_clear(store);
2003 filelist_free(vf->list);
2006 return vflist_refresh(vf);
2009 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
2011 ViewFile *vf = data;
2013 vflist_select_idle_cancel(vf);
2014 vflist_thumb_stop(vf);
2016 filelist_free(vf->list);
2019 ViewFile *vflist_new(ViewFile *vf, const gchar *path)
2021 GtkTreeStore *store;
2022 GtkTreeSelection *selection;
2024 GType flist_types[FILE_COLUMN_COUNT];
2027 vf->info = g_new0(ViewFileInfoList, 1);
2029 VFLIST_INFO(vf, click_fd) = NULL;
2030 VFLIST_INFO(vf, select_fd) = NULL;
2031 VFLIST_INFO(vf, thumbs_enabled) = FALSE;
2033 VFLIST_INFO(vf, select_idle_id) = -1;
2035 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
2036 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
2037 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
2038 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
2039 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
2040 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
2041 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
2042 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
2043 flist_types[i] = G_TYPE_BOOLEAN;
2045 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
2047 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2048 g_object_unref(store);
2050 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2051 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
2052 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
2054 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2055 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2057 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2059 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2060 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2062 vflist_listview_add_column(vf, FILE_COLUMN_NAME, _("Name"), FALSE, FALSE, FALSE);
2063 vflist_listview_add_column(vf, FILE_COLUMN_SIDECARS, _("SC"), FALSE, FALSE, FALSE);
2065 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2066 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2071 void vflist_thumb_set(ViewFile *vf, gint enable)
2073 if (VFLIST_INFO(vf, thumbs_enabled) == enable) return;
2075 VFLIST_INFO(vf, thumbs_enabled) = enable;
2076 if (vf->layout) vflist_refresh(vf);
2079 void vflist_marks_set(ViewFile *vf, gint enable)
2081 GList *columns, *work;
2083 if (vf->marks_enabled == enable) return;
2085 vf->marks_enabled = enable;
2087 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2092 GtkTreeViewColumn *column = work->data;
2093 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2096 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2097 gtk_tree_view_column_set_visible(column, enable);
2100 g_list_free(columns);
2101 //vflist_refresh(vf);
2105 *-----------------------------------------------------------------------------
2106 * maintenance (for rename, move, remove)
2107 *-----------------------------------------------------------------------------
2110 static gint vflist_maint_find_closest(ViewFile *vf, gint row, gint count, GList *ignore_list)
2120 gint f = vflist_index_by_path(vf, work->data);
2121 if (f >= 0) list = g_list_prepend(list, GINT_TO_POINTER(f));
2131 gpointer p = work->data;
2133 if (row == GPOINTER_TO_INT(p))
2138 if (rev == GPOINTER_TO_INT(p))
2143 if (!c) list = g_list_remove(list, p);
2151 if (row > count - 1)
2164 gint vflist_maint_renamed(ViewFile *vf, FileData *fd)
2170 if (g_list_index(vf->list, fd) < 0) return FALSE;
2172 source_base = remove_level_from_path(fd->change->source);
2173 dest_base = remove_level_from_path(fd->change->dest);
2176 if (strcmp(source_base, dest_base) == 0)
2178 GtkTreeStore *store;
2180 GtkTreeIter position;
2184 old_row = g_list_index(vf->list, fd);
2186 vf->list = g_list_remove(vf->list, fd);
2188 vf->list = filelist_insert_sort(vf->list, fd, vf->sort_method, vf->sort_ascend);
2189 n = g_list_index(vf->list, fd);
2191 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2192 if (vflist_find_row(vf, fd, &iter) >= 0 &&
2193 gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &position, NULL, n))
2197 gtk_tree_store_move_before(store, &iter, &position);
2201 gtk_tree_store_move_after(store, &iter, &position);
2204 gtk_tree_store_set(store, &iter, FILE_COLUMN_NAME, fd->name, -1);
2210 ret = vflist_maint_removed(vf, fd, NULL);
2213 g_free(source_base);
2219 gint vflist_maint_removed(ViewFile *vf, FileData *fd, GList *ignore_list)
2226 row = g_list_index(vf->list, fd);
2227 if (row < 0) return FALSE;
2229 if (vflist_index_is_selected(vf, row) &&
2230 layout_image_get_collection(vf->layout, NULL) == NULL)
2234 n = vflist_count(vf, NULL);
2237 new_row = vflist_maint_find_closest(vf, row, n, ignore_list);
2238 DEBUG_1("row = %d, closest is %d", row, new_row);
2251 vflist_select_none(vf);
2254 fd = vflist_index_get_data(vf, new_row);
2255 if (vflist_find_row(vf, fd, &iter) >= 0)
2257 GtkTreeSelection *selection;
2259 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2260 gtk_tree_selection_select_iter(selection, &iter);
2261 vflist_move_cursor(vf, &iter);
2266 fd = vflist_index_get_data(vf, row);
2267 if (vflist_find_row(vf, fd, &iter) >= 0)
2269 GtkTreeStore *store;
2270 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2271 gtk_tree_store_remove(store, &iter);
2273 list = g_list_nth(vf->list, row);
2276 /* thumbnail loader check */
2277 if (fd == vf->thumbs_filedata) vf->thumbs_filedata = NULL;
2278 if (vf->thumbs_count > 0) vf->thumbs_count--;
2280 vf->list = g_list_remove(vf->list, fd);
2281 file_data_unref(fd);
2283 vflist_send_update(vf);
2288 gint vflist_maint_moved(ViewFile *vf, FileData *fd, GList *ignore_list)
2293 if (!fd->change->source || !vf->path) return FALSE;
2295 buf = remove_level_from_path(fd->change->source);
2297 if (strcmp(buf, vf->path) == 0)
2299 ret = vflist_maint_removed(vf, fd, ignore_list);