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"
31 #include "view_file.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);
58 *-----------------------------------------------------------------------------
60 *-----------------------------------------------------------------------------
67 } ViewFileFindRowData;
69 static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
71 ViewFileFindRowData *find = data;
73 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
84 static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter)
87 ViewFileFindRowData data = {fd, iter, 0, 0};
89 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
90 gtk_tree_model_foreach(store, vflist_find_row_cb, &data);
102 static gint vflist_find_sidecar_list_idx(GList *work, FileData *fd)
107 FileData *fd_p = work->data;
108 if (fd == fd_p) return i;
112 GList *work2 = fd_p->sidecar_files;
116 if (fd == fd_p) return i;
127 static gint vflist_sidecar_list_count(GList *work)
132 FileData *fd = work->data;
135 GList *work2 = fd->sidecar_files;
147 static void vflist_color_set(ViewFile *vf, FileData *fd, gint color_set)
152 if (vflist_find_row(vf, fd, &iter) < 0) return;
153 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
154 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
157 static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
162 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
164 tpath = gtk_tree_model_get_path(store, iter);
165 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
166 gtk_tree_path_free(tpath);
170 static gint vflist_column_idx(ViewFile *vf, gint store_idx)
172 GList *columns, *work;
175 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
179 GtkTreeViewColumn *column = work->data;
180 if (store_idx == GPOINTER_TO_INT(g_object_get_data (G_OBJECT(column), "column_store_idx")))
186 g_list_free(columns);
192 *-----------------------------------------------------------------------------
194 *-----------------------------------------------------------------------------
197 static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
198 GtkSelectionData *selection_data, guint info,
199 guint time, gpointer data)
203 gchar *uri_text = NULL;
206 if (!VFLIST_INFO(vf, click_fd)) return;
208 if (vflist_row_is_selected(vf, VFLIST_INFO(vf, click_fd)))
210 list = vf_selection_get_list(vf);
214 list = g_list_append(NULL, file_data_ref(VFLIST_INFO(vf, click_fd)));
219 uri_text = uri_text_from_filelist(list, &total, (info == TARGET_TEXT_PLAIN));
224 gtk_selection_data_set(selection_data, selection_data->target,
225 8, (guchar *)uri_text, total);
229 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
233 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), TRUE);
235 if (VFLIST_INFO(vf, thumbs_enabled) &&
236 VFLIST_INFO(vf, click_fd) && VFLIST_INFO(vf, click_fd)->pixbuf)
240 if (vflist_row_is_selected(vf, VFLIST_INFO(vf, click_fd)))
241 items = vf_selection_count(vf, NULL);
245 dnd_set_drag_icon(widget, context, VFLIST_INFO(vf, click_fd)->pixbuf, items);
249 static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
253 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
255 if (context->action == GDK_ACTION_MOVE)
261 void vflist_dnd_init(ViewFile *vf)
263 gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
264 dnd_file_drag_types, dnd_file_drag_types_count,
265 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
266 g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
267 G_CALLBACK(vflist_dnd_get), vf);
268 g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
269 G_CALLBACK(vflist_dnd_begin), vf);
270 g_signal_connect(G_OBJECT(vf->listview), "drag_end",
271 G_CALLBACK(vflist_dnd_end), vf);
275 *-----------------------------------------------------------------------------
277 *-----------------------------------------------------------------------------
280 GList *vflist_pop_menu_file_list(ViewFile *vf)
282 if (!VFLIST_INFO(vf, click_fd)) return NULL;
284 if (vflist_row_is_selected(vf, VFLIST_INFO(vf, click_fd)))
286 return vf_selection_get_list(vf);
289 return g_list_append(NULL, file_data_ref(VFLIST_INFO(vf, click_fd)));
292 static void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
296 if (vflist_row_is_selected(vf, VFLIST_INFO(vf, click_fd)))
300 list = vf_selection_get_list(vf);
301 view_window_new_from_list(list);
306 view_window_new(VFLIST_INFO(vf, click_fd));
310 static void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
315 list = vf_pop_menu_file_list(vf);
316 if (options->file_ops.enable_in_place_rename &&
317 list && !list->next && VFLIST_INFO(vf, click_fd))
324 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
325 if (vflist_find_row(vf, VFLIST_INFO(vf, click_fd), &iter) >= 0)
329 tpath = gtk_tree_model_get_path(store, &iter);
330 tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
331 vflist_column_idx(vf, FILE_COLUMN_NAME), VFLIST_INFO(vf, click_fd)->name,
332 vflist_row_rename_cb, vf);
333 gtk_tree_path_free(tpath);
338 file_util_rename(NULL, list, vf->listview);
341 static void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
345 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
348 layout_thumb_set(vf->layout, !VFLIST_INFO(vf, thumbs_enabled));
352 vflist_thumb_set(vf, !VFLIST_INFO(vf, thumbs_enabled));
356 static void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
360 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
364 static void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
367 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
368 VFLIST_INFO(vf, click_fd) = NULL;
373 static GtkWidget *vflist_pop_menu(ViewFile *vf, FileData *fd, gint col_idx)
380 vflist_color_set(vf, fd, TRUE);
381 active = (fd != NULL);
383 menu = popup_menu_short_lived();
384 g_signal_connect(G_OBJECT(menu), "destroy",
385 G_CALLBACK(vflist_popup_destroy_cb), vf);
387 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
389 gint mark = col_idx - FILE_COLUMN_MARKS;
390 gchar *str_set_mark = g_strdup_printf(_("_Set mark %d"), mark + 1);
391 gchar *str_res_mark = g_strdup_printf(_("_Reset mark %d"), mark + 1);
392 gchar *str_toggle_mark = g_strdup_printf(_("_Toggle mark %d"), mark + 1);
393 gchar *str_sel_mark = g_strdup_printf(_("_Select mark %d"), mark + 1);
394 gchar *str_sel_mark_or = g_strdup_printf(_("_Add mark %d"), mark + 1);
395 gchar *str_sel_mark_and = g_strdup_printf(_("_Intersection with mark %d"), mark + 1);
396 gchar *str_sel_mark_minus = g_strdup_printf(_("_Unselect mark %d"), mark + 1);
399 vf->active_mark = mark;
400 menu_item_add_sensitive(menu, str_set_mark, active,
401 G_CALLBACK(vf_pop_menu_set_mark_sel_cb), vf);
403 menu_item_add_sensitive(menu, str_res_mark, active,
404 G_CALLBACK(vf_pop_menu_res_mark_sel_cb), vf);
406 menu_item_add_sensitive(menu, str_toggle_mark, active,
407 G_CALLBACK(vf_pop_menu_toggle_mark_sel_cb), vf);
409 menu_item_add_divider(menu);
411 menu_item_add_sensitive(menu, str_sel_mark, active,
412 G_CALLBACK(vf_pop_menu_sel_mark_cb), vf);
413 menu_item_add_sensitive(menu, str_sel_mark_or, active,
414 G_CALLBACK(vf_pop_menu_sel_mark_or_cb), vf);
415 menu_item_add_sensitive(menu, str_sel_mark_and, active,
416 G_CALLBACK(vf_pop_menu_sel_mark_and_cb), vf);
417 menu_item_add_sensitive(menu, str_sel_mark_minus, active,
418 G_CALLBACK(vf_pop_menu_sel_mark_minus_cb), vf);
420 menu_item_add_divider(menu);
422 g_free(str_set_mark);
423 g_free(str_res_mark);
424 g_free(str_toggle_mark);
425 g_free(str_sel_mark);
426 g_free(str_sel_mark_and);
427 g_free(str_sel_mark_or);
428 g_free(str_sel_mark_minus);
431 submenu_add_edit(menu, &item, G_CALLBACK(vf_pop_menu_edit_cb), vf);
432 gtk_widget_set_sensitive(item, active);
434 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
435 G_CALLBACK(vf_pop_menu_info_cb), vf);
436 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
437 G_CALLBACK(vflist_pop_menu_view_cb), vf);
439 menu_item_add_divider(menu);
440 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
441 G_CALLBACK(vf_pop_menu_copy_cb), vf);
442 menu_item_add_sensitive(menu, _("_Move..."), active,
443 G_CALLBACK(vf_pop_menu_move_cb), vf);
444 menu_item_add_sensitive(menu, _("_Rename..."), active,
445 G_CALLBACK(vflist_pop_menu_rename_cb), vf);
446 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
447 G_CALLBACK(vf_pop_menu_delete_cb), vf);
448 if (options->show_copy_path)
449 menu_item_add_sensitive(menu, _("_Copy path"), active,
450 G_CALLBACK(vf_pop_menu_copy_path_cb), vf);
452 menu_item_add_divider(menu);
454 submenu = submenu_add_sort(NULL, G_CALLBACK(vf_pop_menu_sort_cb), vf,
455 FALSE, FALSE, TRUE, vf->sort_method);
456 menu_item_add_divider(submenu);
457 menu_item_add_check(submenu, _("Ascending"), vf->sort_ascend,
458 G_CALLBACK(vf_pop_menu_sort_ascend_cb), vf);
460 item = menu_item_add(menu, _("_Sort"), NULL, NULL);
461 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
463 menu_item_add_check(menu, _("View as _icons"), FALSE,
464 G_CALLBACK(vf_pop_menu_toggle_view_type_cb), vf);
465 menu_item_add_check(menu, _("Show _thumbnails"), VFLIST_INFO(vf, thumbs_enabled),
466 G_CALLBACK(vflist_pop_menu_thumbs_cb), vf);
467 menu_item_add_stock(menu, _("Re_fresh"), GTK_STOCK_REFRESH, G_CALLBACK(vflist_pop_menu_refresh_cb), vf);
473 *-----------------------------------------------------------------------------
475 *-----------------------------------------------------------------------------
478 static gint vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
484 if (strlen(new) == 0) return FALSE;
486 old_path = concat_dir_and_file(vf->path, old);
487 new_path = concat_dir_and_file(vf->path, new);
489 if (strchr(new, '/') != NULL)
491 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
492 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
495 else if (isfile(new_path))
497 gchar *text = g_strdup_printf(_("A file with name %s already exists."), new);
498 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
503 gint row = vf_index_by_path(vf, old_path);
506 GList *work = g_list_nth(vf->list, row);
507 FileData *fd = work->data;
509 if (!file_data_add_change_info(fd, FILEDATA_CHANGE_RENAME, old_path, new_path) || !rename_file_ext(fd))
511 gchar *text = g_strdup_printf(_("Unable to rename file:\n%s\nto:\n%s"), old, new);
512 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
524 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
532 if (vflist_find_row(vf, VFLIST_INFO(vf, click_fd), &iter) < 0) return;
533 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
534 tpath = gtk_tree_model_get_path(store, &iter);
535 tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
536 gtk_tree_path_free(tpath);
538 popup_menu_position_clamp(menu, x, y, 0);
541 gint vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
546 if (event->keyval != GDK_Menu) return FALSE;
548 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
554 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
555 gtk_tree_model_get_iter(store, &iter, tpath);
556 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST_INFO(vf, click_fd), -1);
557 gtk_tree_path_free(tpath);
561 VFLIST_INFO(vf, click_fd) = NULL;
564 vf->popup = vflist_pop_menu(vf, VFLIST_INFO(vf, click_fd), 0);
565 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
570 gint vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
576 GtkTreeViewColumn *column;
579 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
580 &tpath, &column, NULL, NULL))
583 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
585 if (bevent->button == MOUSE_BUTTON_LEFT &&
586 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
589 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
591 gtk_tree_model_get_iter(store, &iter, tpath);
592 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
594 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE);
596 gtk_tree_path_free(tpath);
599 VFLIST_INFO(vf, click_fd) = fd;
601 if (bevent->button == MOUSE_BUTTON_RIGHT)
603 vf->popup = vflist_pop_menu(vf, VFLIST_INFO(vf, click_fd), col_idx);
604 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
605 bevent->button, bevent->time);
609 if (!fd) return FALSE;
611 if (bevent->button == MOUSE_BUTTON_MIDDLE)
613 if (!vflist_row_is_selected(vf, fd))
615 vflist_color_set(vf, fd, TRUE);
621 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
622 !(bevent->state & GDK_SHIFT_MASK ) &&
623 !(bevent->state & GDK_CONTROL_MASK ) &&
624 vflist_row_is_selected(vf, fd))
626 GtkTreeSelection *selection;
628 gtk_widget_grab_focus(widget);
631 /* returning FALSE and further processing of the event is needed for
632 correct operation of the expander, to show the sidecar files.
633 It however resets the selection of multiple files. With this condition
634 it should work for both cases */
635 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
636 return (gtk_tree_selection_count_selected_rows(selection) > 1);
640 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
642 if (vf->layout) layout_image_full_screen_start(vf->layout);
649 gint vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
656 if (bevent->button == MOUSE_BUTTON_MIDDLE)
658 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
661 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
666 if ((bevent->x != 0 || bevent->y != 0) &&
667 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
668 &tpath, NULL, NULL, NULL))
672 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
673 gtk_tree_model_get_iter(store, &iter, tpath);
674 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
675 gtk_tree_path_free(tpath);
678 if (bevent->button == MOUSE_BUTTON_MIDDLE)
680 if (fd && VFLIST_INFO(vf, click_fd) == fd)
682 GtkTreeSelection *selection;
684 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
685 if (vflist_row_is_selected(vf, fd))
687 gtk_tree_selection_unselect_iter(selection, &iter);
691 gtk_tree_selection_select_iter(selection, &iter);
697 if (fd && VFLIST_INFO(vf, click_fd) == fd &&
698 !(bevent->state & GDK_SHIFT_MASK ) &&
699 !(bevent->state & GDK_CONTROL_MASK ) &&
700 vflist_row_is_selected(vf, fd))
702 GtkTreeSelection *selection;
704 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
705 gtk_tree_selection_unselect_all(selection);
706 gtk_tree_selection_select_iter(selection, &iter);
707 vflist_move_cursor(vf, &iter);
708 // return TRUE;// FIXME - expand
714 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
716 FileData *read_ahead_fd = NULL;
721 cur_fd = layout_image_get_fd(vf->layout);
722 if (sel_fd == cur_fd) return; /* no change */
724 row = g_list_index(vf->list, sel_fd);
725 // FIXME sidecar data
727 if (sel_fd && options->image.enable_read_ahead && row >= 0)
729 if (row > g_list_index(vf->list, cur_fd) &&
730 row + 1 < vf_count(vf, NULL))
732 read_ahead_fd = vf_index_get_data(vf, row + 1);
736 read_ahead_fd = vf_index_get_data(vf, row - 1);
740 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
743 static gint vflist_select_idle_cb(gpointer data)
749 VFLIST_INFO(vf, select_idle_id) = -1;
755 if (VFLIST_INFO(vf, select_fd))
757 vflist_select_image(vf, VFLIST_INFO(vf, select_fd));
758 VFLIST_INFO(vf, select_fd) = NULL;
761 VFLIST_INFO(vf, select_idle_id) = -1;
765 static void vflist_select_idle_cancel(ViewFile *vf)
767 if (VFLIST_INFO(vf, select_idle_id) != -1) g_source_remove(VFLIST_INFO(vf, select_idle_id));
768 VFLIST_INFO(vf, select_idle_id) = -1;
771 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
772 gboolean path_currently_selected, gpointer data)
777 if (!path_currently_selected &&
778 gtk_tree_model_get_iter(store, &iter, tpath))
780 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST_INFO(vf, select_fd), -1);
784 VFLIST_INFO(vf, select_fd) = NULL;
788 VFLIST_INFO(vf, select_idle_id) == -1)
790 VFLIST_INFO(vf, select_idle_id) = g_idle_add(vflist_select_idle_cb, vf);
797 *-----------------------------------------------------------------------------
799 *-----------------------------------------------------------------------------
803 static gboolean vflist_dummy_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
804 gboolean path_currently_selected, gpointer data)
811 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
815 gchar *sidecars = NULL;
817 if (fd->sidecar_files)
818 sidecars = file_data_sc_list_to_string(fd);
819 size = text_from_size(fd->size);
821 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
822 FILE_COLUMN_THUMB, (VFLIST_INFO(vf, thumbs_enabled)) ? fd->pixbuf : NULL,
823 FILE_COLUMN_NAME, fd->name,
824 FILE_COLUMN_SIDECARS, sidecars,
825 FILE_COLUMN_SIZE, size,
826 FILE_COLUMN_DATE, text_from_time(fd->date),
827 FILE_COLUMN_COLOR, FALSE, -1);
828 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
829 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, fd->marks[i], -1);
836 static void vflist_setup_iter_with_sidecars(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
842 vflist_setup_iter(vf, store, iter, fd);
845 /* this is almost the same code as in vflist_populate_view
846 maybe it should be made more generic and used in both places */
849 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &s_iter, iter);
851 work = fd->sidecar_files;
855 FileData *sfd = work->data;
860 FileData *old_sfd = NULL;
864 gtk_tree_model_get(GTK_TREE_MODEL(store), &s_iter, FILE_COLUMN_POINTER, &old_sfd, -1);
872 match = filelist_sort_compare_filedata_full(sfd, old_sfd, SORT_NAME, TRUE);
887 gtk_tree_store_insert_before(store, &new, iter, &s_iter);
891 gtk_tree_store_append(store, &new, iter);
894 vflist_setup_iter(vf, store, &new, sfd);
900 valid = gtk_tree_store_remove(store, &s_iter);
904 vflist_setup_iter(vf, store, &s_iter, sfd);
906 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &s_iter);
916 valid = gtk_tree_store_remove(store, &s_iter);
920 void vflist_sort_set(ViewFile *vf, SortType type, gint ascend)
923 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
928 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
929 if (!vf->list) return;
935 FileData *fd = work->data;
936 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
941 vf->sort_method = type;
942 vf->sort_ascend = ascend;
944 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
946 new_order = g_malloc(i * sizeof(gint));
952 FileData *fd = work->data;
953 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
958 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
959 gtk_tree_store_reorder(store, NULL, new_order);
962 g_hash_table_destroy(fd_idx_hash);
966 *-----------------------------------------------------------------------------
968 *-----------------------------------------------------------------------------
971 static gint vflist_thumb_next(ViewFile *vf);
973 static void vflist_thumb_status(ViewFile *vf, gdouble val, const gchar *text)
975 if (vf->func_thumb_status)
977 vf->func_thumb_status(vf, val, text, vf->data_thumb_status);
981 static void vflist_thumb_cleanup(ViewFile *vf)
983 vflist_thumb_status(vf, 0.0, NULL);
985 vf->thumbs_count = 0;
986 vf->thumbs_running = FALSE;
988 thumb_loader_free(vf->thumbs_loader);
989 vf->thumbs_loader = NULL;
991 vf->thumbs_filedata = NULL;
994 static void vflist_thumb_stop(ViewFile *vf)
996 if (vf->thumbs_running) vflist_thumb_cleanup(vf);
999 static void vflist_thumb_do(ViewFile *vf, ThumbLoader *tl, FileData *fd)
1001 GtkTreeStore *store;
1004 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1006 if (fd->pixbuf) g_object_unref(fd->pixbuf);
1007 fd->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
1009 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1010 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->pixbuf, -1);
1012 vflist_thumb_status(vf, (gdouble)(vf->thumbs_count) / vflist_sidecar_list_count(vf->list), _("Loading thumbs..."));
1015 static void vflist_thumb_error_cb(ThumbLoader *tl, gpointer data)
1017 ViewFile *vf = data;
1019 if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1021 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
1024 while (vflist_thumb_next(vf));
1027 static void vflist_thumb_done_cb(ThumbLoader *tl, gpointer data)
1029 ViewFile *vf = data;
1031 if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1033 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
1036 while (vflist_thumb_next(vf));
1039 static gint vflist_thumb_next(ViewFile *vf)
1042 FileData *fd = NULL;
1044 /* first check the visible files */
1046 if (GTK_WIDGET_REALIZED(vf->listview) &&
1047 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1049 GtkTreeModel *store;
1053 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1054 gtk_tree_model_get_iter(store, &iter, tpath);
1055 gtk_tree_path_free(tpath);
1057 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1059 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1060 if (fd->pixbuf) fd = NULL;
1062 valid = gtk_tree_model_iter_next(store, &iter);
1066 /* then find first undone */
1070 GList *work = vf->list;
1073 FileData *fd_p = work->data;
1078 GList *work2 = fd_p->sidecar_files;
1080 while (work2 && !fd)
1083 if (!fd_p->pixbuf) fd = fd_p;
1084 work2 = work2->next;
1094 vflist_thumb_cleanup(vf);
1100 vf->thumbs_filedata = fd;
1102 thumb_loader_free(vf->thumbs_loader);
1104 vf->thumbs_loader = thumb_loader_new(options->thumbnails.max_width, options->thumbnails.max_height);
1105 thumb_loader_set_callbacks(vf->thumbs_loader,
1106 vflist_thumb_done_cb,
1107 vflist_thumb_error_cb,
1111 if (!thumb_loader_start(vf->thumbs_loader, fd->path))
1113 /* set icon to unknown, continue */
1114 DEBUG_1("thumb loader start failed %s", vf->thumbs_loader->path);
1115 vflist_thumb_do(vf, vf->thumbs_loader, fd);
1123 static void vflist_thumb_update(ViewFile *vf)
1125 vflist_thumb_stop(vf);
1126 if (!VFLIST_INFO(vf, thumbs_enabled)) return;
1128 vflist_thumb_status(vf, 0.0, _("Loading thumbs..."));
1129 vf->thumbs_running = TRUE;
1131 while (vflist_thumb_next(vf));
1135 *-----------------------------------------------------------------------------
1137 *-----------------------------------------------------------------------------
1140 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1142 return g_list_nth_data(vf->list, row);
1145 static gint vflist_row_by_path(ViewFile *vf, const gchar *path, FileData **fd)
1150 if (!path) return -1;
1155 FileData *fd_n = work->data;
1156 if (strcmp(path, fd_n->path) == 0)
1169 gint vflist_index_by_path(ViewFile *vf, const gchar *path)
1171 return vflist_row_by_path(vf, path, NULL);
1174 gint vflist_count(ViewFile *vf, gint64 *bytes)
1184 FileData *fd = work->data;
1192 return g_list_length(vf->list);
1195 GList *vflist_get_list(ViewFile *vf)
1203 FileData *fd = work->data;
1206 list = g_list_prepend(list, file_data_ref(fd));
1209 return g_list_reverse(list);
1213 *-----------------------------------------------------------------------------
1215 *-----------------------------------------------------------------------------
1218 static gint vflist_row_is_selected(ViewFile *vf, FileData *fd)
1220 GtkTreeModel *store;
1221 GtkTreeSelection *selection;
1226 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1227 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1229 while (!found && work)
1231 GtkTreePath *tpath = work->data;
1235 gtk_tree_model_get_iter(store, &iter, tpath);
1236 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1237 if (fd_n == fd) found = TRUE;
1240 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1246 gint vflist_index_is_selected(ViewFile *vf, gint row)
1250 fd = vf_index_get_data(vf, row);
1251 return vflist_row_is_selected(vf, fd);
1254 gint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1256 GtkTreeModel *store;
1257 GtkTreeSelection *selection;
1261 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1262 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1272 GtkTreePath *tpath = work->data;
1276 gtk_tree_model_get_iter(store, &iter, tpath);
1277 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1286 count = g_list_length(slist);
1287 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1293 GList *vflist_selection_get_list(ViewFile *vf)
1295 GtkTreeModel *store;
1296 GtkTreeSelection *selection;
1301 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1302 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1306 GtkTreePath *tpath = work->data;
1310 gtk_tree_model_get_iter(store, &iter, tpath);
1311 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1313 list = g_list_prepend(list, file_data_ref(fd));
1317 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1320 return g_list_reverse(list);
1323 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1325 GtkTreeModel *store;
1326 GtkTreeSelection *selection;
1331 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1332 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1336 GtkTreePath *tpath = work->data;
1340 gtk_tree_model_get_iter(store, &iter, tpath);
1341 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1343 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1347 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1350 return g_list_reverse(list);
1353 void vflist_select_all(ViewFile *vf)
1355 GtkTreeSelection *selection;
1357 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1358 gtk_tree_selection_select_all(selection);
1360 VFLIST_INFO(vf, select_fd) = NULL;
1363 void vflist_select_none(ViewFile *vf)
1365 GtkTreeSelection *selection;
1367 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1368 gtk_tree_selection_unselect_all(selection);
1371 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1376 tpath = gtk_tree_model_get_path(store, iter);
1377 result = gtk_tree_path_prev(tpath);
1379 gtk_tree_model_get_iter(store, iter, tpath);
1381 gtk_tree_path_free(tpath);
1386 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1388 if (!gtk_tree_model_get_iter_first(store, iter))
1393 GtkTreeIter next = *iter;
1395 if (gtk_tree_model_iter_next(store, &next))
1404 void vflist_select_invert(ViewFile *vf)
1407 GtkTreeSelection *selection;
1408 GtkTreeModel *store;
1411 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1412 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1414 /* Backward iteration prevents scrolling to the end of the list,
1415 * it scrolls to the first selected row instead. */
1416 valid = tree_model_get_iter_last(store, &iter);
1420 gint selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1423 gtk_tree_selection_unselect_iter(selection, &iter);
1425 gtk_tree_selection_select_iter(selection, &iter);
1427 valid = tree_model_iter_prev(store, &iter);
1431 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1435 if (vflist_find_row(vf, fd, &iter) < 0) return;
1437 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1439 if (!vflist_row_is_selected(vf, fd))
1441 GtkTreeSelection *selection;
1442 GtkTreeModel *store;
1445 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1446 gtk_tree_selection_unselect_all(selection);
1447 gtk_tree_selection_select_iter(selection, &iter);
1448 vflist_move_cursor(vf, &iter);
1450 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1451 tpath = gtk_tree_model_get_path(store, &iter);
1452 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1453 gtk_tree_path_free(tpath);
1457 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1459 GtkTreeModel *store;
1461 GtkTreeSelection *selection;
1464 g_assert(mark >= 0 && mark < FILEDATA_MARKS_SIZE);
1466 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1467 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1469 valid = gtk_tree_model_get_iter_first(store, &iter);
1473 gboolean mark_val, selected;
1474 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1476 mark_val = fd->marks[mark];
1477 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1481 case MTS_MODE_SET: selected = mark_val;
1483 case MTS_MODE_OR: selected = mark_val | selected;
1485 case MTS_MODE_AND: selected = mark_val & selected;
1487 case MTS_MODE_MINUS: selected = !mark_val & selected;
1492 gtk_tree_selection_select_iter(selection, &iter);
1494 gtk_tree_selection_unselect_iter(selection, &iter);
1496 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1500 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1502 GtkTreeModel *store;
1503 GtkTreeSelection *selection;
1507 g_assert(mark >= 0 && mark < FILEDATA_MARKS_SIZE);
1509 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1510 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1514 GtkTreePath *tpath = work->data;
1518 gtk_tree_model_get_iter(store, &iter, tpath);
1519 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1523 case STM_MODE_SET: fd->marks[mark] = 1;
1525 case STM_MODE_RESET: fd->marks[mark] = 0;
1527 case STM_MODE_TOGGLE: fd->marks[mark] = !fd->marks[mark];
1531 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_MARKS + mark, fd->marks[mark], -1);
1535 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1540 *-----------------------------------------------------------------------------
1542 *-----------------------------------------------------------------------------
1545 static void vflist_listview_set_height(GtkWidget *listview, gint thumb)
1547 GtkTreeViewColumn *column;
1548 GtkCellRenderer *cell;
1551 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_COLUMN_THUMB - 1);
1552 if (!column) return;
1554 gtk_tree_view_column_set_fixed_width(column, ((thumb) ? options->thumbnails.max_width : 4) + 10);
1556 list = gtk_tree_view_column_get_cell_renderers(column);
1561 g_object_set(G_OBJECT(cell), "height", (thumb) ? options->thumbnails.max_height : -1, NULL);
1562 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1565 static void vflist_populate_view(ViewFile *vf)
1567 GtkTreeStore *store;
1571 GtkTreeRowReference *visible_row = NULL;
1575 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1576 thumbs = VFLIST_INFO(vf, thumbs_enabled);
1578 vflist_thumb_stop(vf);
1582 gtk_tree_store_clear(store);
1587 if (GTK_WIDGET_REALIZED(vf->listview) &&
1588 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1590 visible_row = gtk_tree_row_reference_new(GTK_TREE_MODEL(store), tpath);
1591 gtk_tree_path_free(tpath);
1594 vflist_listview_set_height(vf->listview, thumbs);
1596 valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
1602 FileData *fd = work->data;
1607 FileData *old_fd = NULL;
1611 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1619 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
1621 match = -1; /* probably should not happen*/
1636 gtk_tree_store_insert_before(store, &new, NULL, &iter);
1640 gtk_tree_store_append(store, &new, NULL);
1642 vflist_setup_iter_with_sidecars(vf, store, &new, fd);
1648 valid = gtk_tree_store_remove(store, &iter);
1652 vflist_setup_iter_with_sidecars(vf, store, &iter, fd);
1654 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1664 valid = gtk_tree_store_remove(store, &iter);
1669 if (gtk_tree_row_reference_valid(visible_row))
1671 tpath = gtk_tree_row_reference_get_path(visible_row);
1672 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(vf->listview), tpath, NULL, TRUE, 0.0, 0.0);
1673 gtk_tree_path_free(tpath);
1675 gtk_tree_row_reference_free(visible_row);
1679 vflist_thumb_update(vf);
1682 gint vflist_refresh(ViewFile *vf)
1687 old_list = vf->list;
1692 ret = filelist_read(vf->path, &vf->list, NULL);
1695 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1696 vflist_populate_view(vf);
1698 filelist_free(old_list);
1703 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1705 #define CELL_HEIGHT_OVERRIDE 512
1707 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1711 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1712 if (spec && G_IS_PARAM_SPEC_INT(spec))
1714 GParamSpecInt *spec_int;
1716 spec_int = G_PARAM_SPEC_INT(spec);
1717 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1721 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1723 static GdkColor color;
1724 static GtkWidget *done = NULL;
1730 style = gtk_widget_get_style(widget);
1731 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1732 shift_color(&color, -1, 0);
1739 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1740 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1742 ViewFile *vf = data;
1745 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1746 g_object_set(G_OBJECT(cell),
1747 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1748 "cell-background-set", set, NULL);
1751 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gint image, gint right_justify, gint expand)
1753 GtkTreeViewColumn *column;
1754 GtkCellRenderer *renderer;
1756 column = gtk_tree_view_column_new();
1757 gtk_tree_view_column_set_title(column, title);
1758 gtk_tree_view_column_set_min_width(column, 4);
1762 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1763 renderer = gtk_cell_renderer_text_new();
1766 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1768 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1769 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1771 gtk_tree_view_column_set_expand(column, TRUE);
1775 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1776 renderer = gtk_cell_renderer_pixbuf_new();
1777 cell_renderer_height_override(renderer);
1778 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1779 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1782 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1783 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1784 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1786 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1789 static void vflist_listview_mark_toggled(GtkCellRendererToggle *cell, gchar *path_str, GtkTreeStore *store)
1791 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1797 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1800 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1802 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1804 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &mark, -1);
1806 fd->marks[col_idx - FILE_COLUMN_MARKS] = mark;
1808 gtk_tree_store_set(store, &iter, col_idx, mark, -1);
1809 gtk_tree_path_free(path);
1812 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1814 GtkTreeViewColumn *column;
1815 GtkCellRenderer *renderer;
1816 GtkTreeStore *store;
1819 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1821 renderer = gtk_cell_renderer_toggle_new();
1822 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1824 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1825 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1826 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1828 index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1829 gtk_tree_view_column_set_fixed_width(column, 16);
1830 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1833 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled), store);
1837 *-----------------------------------------------------------------------------
1839 *-----------------------------------------------------------------------------
1842 gint vflist_set_path(ViewFile *vf, const gchar *path)
1844 GtkTreeStore *store;
1846 if (!path) return FALSE;
1847 if (vf->path && strcmp(path, vf->path) == 0) return TRUE;
1850 vf->path = g_strdup(path);
1852 /* force complete reload */
1853 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1854 gtk_tree_store_clear(store);
1856 filelist_free(vf->list);
1859 return vf_refresh(vf);
1862 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1864 ViewFile *vf = data;
1866 vflist_select_idle_cancel(vf);
1867 vflist_thumb_stop(vf);
1869 filelist_free(vf->list);
1872 ViewFile *vflist_new(ViewFile *vf, const gchar *path)
1874 GtkTreeStore *store;
1875 GtkTreeSelection *selection;
1877 GType flist_types[FILE_COLUMN_COUNT];
1880 vf->info = g_new0(ViewFileInfoList, 1);
1882 VFLIST_INFO(vf, click_fd) = NULL;
1883 VFLIST_INFO(vf, select_fd) = NULL;
1884 VFLIST_INFO(vf, thumbs_enabled) = FALSE;
1886 VFLIST_INFO(vf, select_idle_id) = -1;
1888 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1889 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1890 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1891 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
1892 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1893 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1894 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1895 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1896 flist_types[i] = G_TYPE_BOOLEAN;
1898 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
1900 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1901 g_object_unref(store);
1903 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1904 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1905 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
1907 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
1908 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
1910 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
1912 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
1913 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
1915 vflist_listview_add_column(vf, FILE_COLUMN_NAME, _("Name"), FALSE, FALSE, FALSE);
1916 vflist_listview_add_column(vf, FILE_COLUMN_SIDECARS, _("SC"), FALSE, FALSE, FALSE);
1918 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
1919 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
1924 void vflist_thumb_set(ViewFile *vf, gint enable)
1926 if (VFLIST_INFO(vf, thumbs_enabled) == enable) return;
1928 VFLIST_INFO(vf, thumbs_enabled) = enable;
1929 if (vf->layout) vf_refresh(vf);
1932 void vflist_marks_set(ViewFile *vf, gint enable)
1934 GList *columns, *work;
1936 if (vf->marks_enabled == enable) return;
1938 vf->marks_enabled = enable;
1940 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
1945 GtkTreeViewColumn *column = work->data;
1946 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
1949 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
1950 gtk_tree_view_column_set_visible(column, enable);
1953 g_list_free(columns);
1958 *-----------------------------------------------------------------------------
1959 * maintenance (for rename, move, remove)
1960 *-----------------------------------------------------------------------------
1963 static gint vflist_maint_find_closest(ViewFile *vf, gint row, gint count, GList *ignore_list)
1973 gint f = vf_index_by_path(vf, work->data);
1974 if (f >= 0) list = g_list_prepend(list, GINT_TO_POINTER(f));
1984 gpointer p = work->data;
1986 if (row == GPOINTER_TO_INT(p))
1991 if (rev == GPOINTER_TO_INT(p))
1996 if (!c) list = g_list_remove(list, p);
2004 if (row > count - 1)
2017 gint vflist_maint_renamed(ViewFile *vf, FileData *fd)
2023 if (g_list_index(vf->list, fd) < 0) return FALSE;
2025 source_base = remove_level_from_path(fd->change->source);
2026 dest_base = remove_level_from_path(fd->change->dest);
2029 if (strcmp(source_base, dest_base) == 0)
2031 GtkTreeStore *store;
2033 GtkTreeIter position;
2037 old_row = g_list_index(vf->list, fd);
2039 vf->list = g_list_remove(vf->list, fd);
2041 vf->list = filelist_insert_sort(vf->list, fd, vf->sort_method, vf->sort_ascend);
2042 n = g_list_index(vf->list, fd);
2044 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2045 if (vflist_find_row(vf, fd, &iter) >= 0 &&
2046 gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &position, NULL, n))
2050 gtk_tree_store_move_before(store, &iter, &position);
2054 gtk_tree_store_move_after(store, &iter, &position);
2057 gtk_tree_store_set(store, &iter, FILE_COLUMN_NAME, fd->name, -1);
2063 ret = vflist_maint_removed(vf, fd, NULL);
2066 g_free(source_base);
2072 gint vflist_maint_removed(ViewFile *vf, FileData *fd, GList *ignore_list)
2079 row = g_list_index(vf->list, fd);
2080 if (row < 0) return FALSE;
2082 if (vflist_index_is_selected(vf, row) &&
2083 layout_image_get_collection(vf->layout, NULL) == NULL)
2087 n = vf_count(vf, NULL);
2090 new_row = vflist_maint_find_closest(vf, row, n, ignore_list);
2091 DEBUG_1("row = %d, closest is %d", row, new_row);
2107 fd = vf_index_get_data(vf, new_row);
2108 if (vflist_find_row(vf, fd, &iter) >= 0)
2110 GtkTreeSelection *selection;
2112 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2113 gtk_tree_selection_select_iter(selection, &iter);
2114 vflist_move_cursor(vf, &iter);
2119 fd = vf_index_get_data(vf, row);
2120 if (vflist_find_row(vf, fd, &iter) >= 0)
2122 GtkTreeStore *store;
2123 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2124 gtk_tree_store_remove(store, &iter);
2126 list = g_list_nth(vf->list, row);
2129 /* thumbnail loader check */
2130 if (fd == vf->thumbs_filedata) vf->thumbs_filedata = NULL;
2131 if (vf->thumbs_count > 0) vf->thumbs_count--;
2133 vf->list = g_list_remove(vf->list, fd);
2134 file_data_unref(fd);
2141 gint vflist_maint_moved(ViewFile *vf, FileData *fd, GList *ignore_list)
2146 if (!fd->change->source || !vf->path) return FALSE;
2148 buf = remove_level_from_path(fd->change->source);
2150 if (strcmp(buf, vf->path) == 0)
2152 ret = vflist_maint_removed(vf, fd, ignore_list);