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 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 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 void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
360 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
364 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)
380 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), TRUE);
381 active = (VFLIST_INFO(vf, click_fd) != NULL);
383 menu = popup_menu_short_lived();
384 g_signal_connect(G_OBJECT(menu), "destroy",
385 G_CALLBACK(vf_popup_destroy_cb), vf);
387 if (vf->clicked_mark > 0)
389 gint mark = vf->clicked_mark;
390 gchar *str_set_mark = g_strdup_printf(_("_Set mark %d"), mark);
391 gchar *str_res_mark = g_strdup_printf(_("_Reset mark %d"), mark);
392 gchar *str_toggle_mark = g_strdup_printf(_("_Toggle mark %d"), mark);
393 gchar *str_sel_mark = g_strdup_printf(_("_Select mark %d"), mark);
394 gchar *str_sel_mark_or = g_strdup_printf(_("_Add mark %d"), mark);
395 gchar *str_sel_mark_and = g_strdup_printf(_("_Intersection with mark %d"), mark);
396 gchar *str_sel_mark_minus = g_strdup_printf(_("_Unselect mark %d"), mark);
398 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
400 vf->active_mark = mark;
401 vf->clicked_mark = 0;
403 menu_item_add_sensitive(menu, str_set_mark, active,
404 G_CALLBACK(vf_pop_menu_set_mark_sel_cb), vf);
406 menu_item_add_sensitive(menu, str_res_mark, active,
407 G_CALLBACK(vf_pop_menu_res_mark_sel_cb), vf);
409 menu_item_add_sensitive(menu, str_toggle_mark, active,
410 G_CALLBACK(vf_pop_menu_toggle_mark_sel_cb), vf);
412 menu_item_add_divider(menu);
414 menu_item_add_sensitive(menu, str_sel_mark, active,
415 G_CALLBACK(vf_pop_menu_sel_mark_cb), vf);
416 menu_item_add_sensitive(menu, str_sel_mark_or, active,
417 G_CALLBACK(vf_pop_menu_sel_mark_or_cb), vf);
418 menu_item_add_sensitive(menu, str_sel_mark_and, active,
419 G_CALLBACK(vf_pop_menu_sel_mark_and_cb), vf);
420 menu_item_add_sensitive(menu, str_sel_mark_minus, active,
421 G_CALLBACK(vf_pop_menu_sel_mark_minus_cb), vf);
423 menu_item_add_divider(menu);
425 g_free(str_set_mark);
426 g_free(str_res_mark);
427 g_free(str_toggle_mark);
428 g_free(str_sel_mark);
429 g_free(str_sel_mark_and);
430 g_free(str_sel_mark_or);
431 g_free(str_sel_mark_minus);
434 submenu_add_edit(menu, &item, G_CALLBACK(vf_pop_menu_edit_cb), vf);
435 gtk_widget_set_sensitive(item, active);
437 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
438 G_CALLBACK(vf_pop_menu_info_cb), vf);
439 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
440 G_CALLBACK(vf_pop_menu_view_cb), vf);
442 menu_item_add_divider(menu);
443 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
444 G_CALLBACK(vf_pop_menu_copy_cb), vf);
445 menu_item_add_sensitive(menu, _("_Move..."), active,
446 G_CALLBACK(vf_pop_menu_move_cb), vf);
447 menu_item_add_sensitive(menu, _("_Rename..."), active,
448 G_CALLBACK(vf_pop_menu_rename_cb), vf);
449 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
450 G_CALLBACK(vf_pop_menu_delete_cb), vf);
451 if (options->show_copy_path)
452 menu_item_add_sensitive(menu, _("_Copy path"), active,
453 G_CALLBACK(vf_pop_menu_copy_path_cb), vf);
455 menu_item_add_divider(menu);
457 submenu = submenu_add_sort(NULL, G_CALLBACK(vf_pop_menu_sort_cb), vf,
458 FALSE, FALSE, TRUE, vf->sort_method);
459 menu_item_add_divider(submenu);
460 menu_item_add_check(submenu, _("Ascending"), vf->sort_ascend,
461 G_CALLBACK(vf_pop_menu_sort_ascend_cb), vf);
463 item = menu_item_add(menu, _("_Sort"), NULL, NULL);
464 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
466 menu_item_add_check(menu, _("View as _icons"), FALSE,
467 G_CALLBACK(vf_pop_menu_toggle_view_type_cb), vf);
468 menu_item_add_check(menu, _("Show _thumbnails"), VFLIST_INFO(vf, thumbs_enabled),
469 G_CALLBACK(vflist_pop_menu_thumbs_cb), vf);
470 menu_item_add_stock(menu, _("Re_fresh"), GTK_STOCK_REFRESH, G_CALLBACK(vf_pop_menu_refresh_cb), vf);
476 *-----------------------------------------------------------------------------
478 *-----------------------------------------------------------------------------
481 static gint vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
487 if (strlen(new) == 0) return FALSE;
489 old_path = concat_dir_and_file(vf->path, old);
490 new_path = concat_dir_and_file(vf->path, new);
492 if (strchr(new, '/') != NULL)
494 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
495 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
498 else if (isfile(new_path))
500 gchar *text = g_strdup_printf(_("A file with name %s already exists."), new);
501 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
506 gint row = vf_index_by_path(vf, old_path);
509 GList *work = g_list_nth(vf->list, row);
510 FileData *fd = work->data;
512 if (!file_data_add_change_info(fd, FILEDATA_CHANGE_RENAME, old_path, new_path) || !rename_file_ext(fd))
514 gchar *text = g_strdup_printf(_("Unable to rename file:\n%s\nto:\n%s"), old, new);
515 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
527 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
535 if (vflist_find_row(vf, VFLIST_INFO(vf, click_fd), &iter) < 0) return;
536 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
537 tpath = gtk_tree_model_get_path(store, &iter);
538 tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
539 gtk_tree_path_free(tpath);
541 popup_menu_position_clamp(menu, x, y, 0);
544 gint vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
549 if (event->keyval != GDK_Menu) return FALSE;
551 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
557 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
558 gtk_tree_model_get_iter(store, &iter, tpath);
559 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST_INFO(vf, click_fd), -1);
560 gtk_tree_path_free(tpath);
564 VFLIST_INFO(vf, click_fd) = NULL;
567 vf->popup = vflist_pop_menu(vf);
568 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
573 gint vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
579 GtkTreeViewColumn *column;
581 vf->clicked_mark = 0;
583 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
584 &tpath, &column, NULL, NULL))
587 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
589 if (bevent->button == MOUSE_BUTTON_LEFT &&
590 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
593 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
594 vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
596 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
598 gtk_tree_model_get_iter(store, &iter, tpath);
599 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
601 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE);
603 gtk_tree_path_free(tpath);
606 VFLIST_INFO(vf, click_fd) = fd;
608 if (bevent->button == MOUSE_BUTTON_RIGHT)
610 vf->popup = vflist_pop_menu(vf);
611 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
612 bevent->button, bevent->time);
616 if (!fd) return FALSE;
618 if (bevent->button == MOUSE_BUTTON_MIDDLE)
620 if (!vflist_row_is_selected(vf, fd))
622 vflist_color_set(vf, fd, TRUE);
628 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
629 !(bevent->state & GDK_SHIFT_MASK ) &&
630 !(bevent->state & GDK_CONTROL_MASK ) &&
631 vflist_row_is_selected(vf, fd))
633 GtkTreeSelection *selection;
635 gtk_widget_grab_focus(widget);
638 /* returning FALSE and further processing of the event is needed for
639 correct operation of the expander, to show the sidecar files.
640 It however resets the selection of multiple files. With this condition
641 it should work for both cases */
642 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
643 return (gtk_tree_selection_count_selected_rows(selection) > 1);
647 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
649 if (vf->layout) layout_image_full_screen_start(vf->layout);
656 gint vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
663 if (bevent->button == MOUSE_BUTTON_MIDDLE)
665 vflist_color_set(vf, VFLIST_INFO(vf, click_fd), FALSE);
668 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
673 if ((bevent->x != 0 || bevent->y != 0) &&
674 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
675 &tpath, NULL, NULL, NULL))
679 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
680 gtk_tree_model_get_iter(store, &iter, tpath);
681 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
682 gtk_tree_path_free(tpath);
685 if (bevent->button == MOUSE_BUTTON_MIDDLE)
687 if (fd && VFLIST_INFO(vf, click_fd) == fd)
689 GtkTreeSelection *selection;
691 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
692 if (vflist_row_is_selected(vf, fd))
694 gtk_tree_selection_unselect_iter(selection, &iter);
698 gtk_tree_selection_select_iter(selection, &iter);
704 if (fd && VFLIST_INFO(vf, click_fd) == fd &&
705 !(bevent->state & GDK_SHIFT_MASK ) &&
706 !(bevent->state & GDK_CONTROL_MASK ) &&
707 vflist_row_is_selected(vf, fd))
709 GtkTreeSelection *selection;
711 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
712 gtk_tree_selection_unselect_all(selection);
713 gtk_tree_selection_select_iter(selection, &iter);
714 vflist_move_cursor(vf, &iter);
715 // return TRUE;// FIXME - expand
721 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
723 FileData *read_ahead_fd = NULL;
728 cur_fd = layout_image_get_fd(vf->layout);
729 if (sel_fd == cur_fd) return; /* no change */
731 row = g_list_index(vf->list, sel_fd);
732 // FIXME sidecar data
734 if (sel_fd && options->image.enable_read_ahead && row >= 0)
736 if (row > g_list_index(vf->list, cur_fd) &&
737 row + 1 < vf_count(vf, NULL))
739 read_ahead_fd = vf_index_get_data(vf, row + 1);
743 read_ahead_fd = vf_index_get_data(vf, row - 1);
747 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
750 static gint vflist_select_idle_cb(gpointer data)
756 VFLIST_INFO(vf, select_idle_id) = -1;
762 if (VFLIST_INFO(vf, select_fd))
764 vflist_select_image(vf, VFLIST_INFO(vf, select_fd));
765 VFLIST_INFO(vf, select_fd) = NULL;
768 VFLIST_INFO(vf, select_idle_id) = -1;
772 static void vflist_select_idle_cancel(ViewFile *vf)
774 if (VFLIST_INFO(vf, select_idle_id) != -1) g_source_remove(VFLIST_INFO(vf, select_idle_id));
775 VFLIST_INFO(vf, select_idle_id) = -1;
778 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
779 gboolean path_currently_selected, gpointer data)
784 if (!path_currently_selected &&
785 gtk_tree_model_get_iter(store, &iter, tpath))
787 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST_INFO(vf, select_fd), -1);
791 VFLIST_INFO(vf, select_fd) = NULL;
795 VFLIST_INFO(vf, select_idle_id) == -1)
797 VFLIST_INFO(vf, select_idle_id) = g_idle_add(vflist_select_idle_cb, vf);
804 *-----------------------------------------------------------------------------
806 *-----------------------------------------------------------------------------
810 static gboolean vflist_dummy_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
811 gboolean path_currently_selected, gpointer data)
818 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
822 gchar *sidecars = NULL;
824 if (fd->sidecar_files)
825 sidecars = file_data_sc_list_to_string(fd);
826 size = text_from_size(fd->size);
828 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
829 FILE_COLUMN_THUMB, (VFLIST_INFO(vf, thumbs_enabled)) ? fd->pixbuf : NULL,
830 FILE_COLUMN_NAME, fd->name,
831 FILE_COLUMN_SIDECARS, sidecars,
832 FILE_COLUMN_SIZE, size,
833 FILE_COLUMN_DATE, text_from_time(fd->date),
834 FILE_COLUMN_COLOR, FALSE, -1);
835 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
836 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, fd->marks[i], -1);
843 static void vflist_setup_iter_with_sidecars(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
849 vflist_setup_iter(vf, store, iter, fd);
852 /* this is almost the same code as in vflist_populate_view
853 maybe it should be made more generic and used in both places */
856 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &s_iter, iter);
858 work = fd->sidecar_files;
862 FileData *sfd = work->data;
867 FileData *old_sfd = NULL;
871 gtk_tree_model_get(GTK_TREE_MODEL(store), &s_iter, FILE_COLUMN_POINTER, &old_sfd, -1);
879 match = filelist_sort_compare_filedata_full(sfd, old_sfd, SORT_NAME, TRUE);
894 gtk_tree_store_insert_before(store, &new, iter, &s_iter);
898 gtk_tree_store_append(store, &new, iter);
901 vflist_setup_iter(vf, store, &new, sfd);
907 valid = gtk_tree_store_remove(store, &s_iter);
911 vflist_setup_iter(vf, store, &s_iter, sfd);
913 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &s_iter);
923 valid = gtk_tree_store_remove(store, &s_iter);
927 void vflist_sort_set(ViewFile *vf, SortType type, gint ascend)
930 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
935 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
936 if (!vf->list) return;
942 FileData *fd = work->data;
943 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
948 vf->sort_method = type;
949 vf->sort_ascend = ascend;
951 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
953 new_order = g_malloc(i * sizeof(gint));
959 FileData *fd = work->data;
960 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
965 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
966 gtk_tree_store_reorder(store, NULL, new_order);
969 g_hash_table_destroy(fd_idx_hash);
973 *-----------------------------------------------------------------------------
975 *-----------------------------------------------------------------------------
978 static gint vflist_thumb_next(ViewFile *vf);
980 static void vflist_thumb_status(ViewFile *vf, gdouble val, const gchar *text)
982 if (vf->func_thumb_status)
984 vf->func_thumb_status(vf, val, text, vf->data_thumb_status);
988 static void vflist_thumb_cleanup(ViewFile *vf)
990 vflist_thumb_status(vf, 0.0, NULL);
992 vf->thumbs_count = 0;
993 vf->thumbs_running = FALSE;
995 thumb_loader_free(vf->thumbs_loader);
996 vf->thumbs_loader = NULL;
998 vf->thumbs_filedata = NULL;
1001 static void vflist_thumb_stop(ViewFile *vf)
1003 if (vf->thumbs_running) vflist_thumb_cleanup(vf);
1006 static void vflist_thumb_do(ViewFile *vf, ThumbLoader *tl, FileData *fd)
1008 GtkTreeStore *store;
1011 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1013 if (fd->pixbuf) g_object_unref(fd->pixbuf);
1014 fd->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
1016 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1017 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->pixbuf, -1);
1019 vflist_thumb_status(vf, (gdouble)(vf->thumbs_count) / vflist_sidecar_list_count(vf->list), _("Loading thumbs..."));
1022 static void vflist_thumb_error_cb(ThumbLoader *tl, gpointer data)
1024 ViewFile *vf = data;
1026 if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1028 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
1031 while (vflist_thumb_next(vf));
1034 static void vflist_thumb_done_cb(ThumbLoader *tl, gpointer data)
1036 ViewFile *vf = data;
1038 if (vf->thumbs_filedata && vf->thumbs_loader == tl)
1040 vflist_thumb_do(vf, tl, vf->thumbs_filedata);
1043 while (vflist_thumb_next(vf));
1046 static gint vflist_thumb_next(ViewFile *vf)
1049 FileData *fd = NULL;
1051 /* first check the visible files */
1053 if (GTK_WIDGET_REALIZED(vf->listview) &&
1054 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1056 GtkTreeModel *store;
1060 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1061 gtk_tree_model_get_iter(store, &iter, tpath);
1062 gtk_tree_path_free(tpath);
1064 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1066 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1067 if (fd->pixbuf) fd = NULL;
1069 valid = gtk_tree_model_iter_next(store, &iter);
1073 /* then find first undone */
1077 GList *work = vf->list;
1080 FileData *fd_p = work->data;
1085 GList *work2 = fd_p->sidecar_files;
1087 while (work2 && !fd)
1090 if (!fd_p->pixbuf) fd = fd_p;
1091 work2 = work2->next;
1101 vflist_thumb_cleanup(vf);
1107 vf->thumbs_filedata = fd;
1109 thumb_loader_free(vf->thumbs_loader);
1111 vf->thumbs_loader = thumb_loader_new(options->thumbnails.max_width, options->thumbnails.max_height);
1112 thumb_loader_set_callbacks(vf->thumbs_loader,
1113 vflist_thumb_done_cb,
1114 vflist_thumb_error_cb,
1118 if (!thumb_loader_start(vf->thumbs_loader, fd->path))
1120 /* set icon to unknown, continue */
1121 DEBUG_1("thumb loader start failed %s", vf->thumbs_loader->path);
1122 vflist_thumb_do(vf, vf->thumbs_loader, fd);
1130 static void vflist_thumb_update(ViewFile *vf)
1132 vflist_thumb_stop(vf);
1133 if (!VFLIST_INFO(vf, thumbs_enabled)) return;
1135 vflist_thumb_status(vf, 0.0, _("Loading thumbs..."));
1136 vf->thumbs_running = TRUE;
1138 while (vflist_thumb_next(vf));
1142 *-----------------------------------------------------------------------------
1144 *-----------------------------------------------------------------------------
1147 FileData *vflist_index_get_data(ViewFile *vf, gint row)
1149 return g_list_nth_data(vf->list, row);
1152 static gint vflist_row_by_path(ViewFile *vf, const gchar *path, FileData **fd)
1157 if (!path) return -1;
1162 FileData *fd_n = work->data;
1163 if (strcmp(path, fd_n->path) == 0)
1176 gint vflist_index_by_path(ViewFile *vf, const gchar *path)
1178 return vflist_row_by_path(vf, path, NULL);
1181 gint vflist_count(ViewFile *vf, gint64 *bytes)
1191 FileData *fd = work->data;
1199 return g_list_length(vf->list);
1202 GList *vflist_get_list(ViewFile *vf)
1210 FileData *fd = work->data;
1213 list = g_list_prepend(list, file_data_ref(fd));
1216 return g_list_reverse(list);
1220 *-----------------------------------------------------------------------------
1222 *-----------------------------------------------------------------------------
1225 static gint vflist_row_is_selected(ViewFile *vf, FileData *fd)
1227 GtkTreeModel *store;
1228 GtkTreeSelection *selection;
1233 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1234 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1236 while (!found && work)
1238 GtkTreePath *tpath = work->data;
1242 gtk_tree_model_get_iter(store, &iter, tpath);
1243 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1244 if (fd_n == fd) found = TRUE;
1247 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1253 gint vflist_index_is_selected(ViewFile *vf, gint row)
1257 fd = vf_index_get_data(vf, row);
1258 return vflist_row_is_selected(vf, fd);
1261 gint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1263 GtkTreeModel *store;
1264 GtkTreeSelection *selection;
1268 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1269 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1279 GtkTreePath *tpath = work->data;
1283 gtk_tree_model_get_iter(store, &iter, tpath);
1284 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1293 count = g_list_length(slist);
1294 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1300 GList *vflist_selection_get_list(ViewFile *vf)
1302 GtkTreeModel *store;
1303 GtkTreeSelection *selection;
1308 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1309 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1313 GtkTreePath *tpath = work->data;
1317 gtk_tree_model_get_iter(store, &iter, tpath);
1318 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1320 list = g_list_prepend(list, file_data_ref(fd));
1324 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1327 return g_list_reverse(list);
1330 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1332 GtkTreeModel *store;
1333 GtkTreeSelection *selection;
1338 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1339 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1343 GtkTreePath *tpath = work->data;
1347 gtk_tree_model_get_iter(store, &iter, tpath);
1348 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1350 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1354 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1357 return g_list_reverse(list);
1360 void vflist_select_all(ViewFile *vf)
1362 GtkTreeSelection *selection;
1364 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1365 gtk_tree_selection_select_all(selection);
1367 VFLIST_INFO(vf, select_fd) = NULL;
1370 void vflist_select_none(ViewFile *vf)
1372 GtkTreeSelection *selection;
1374 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1375 gtk_tree_selection_unselect_all(selection);
1378 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1383 tpath = gtk_tree_model_get_path(store, iter);
1384 result = gtk_tree_path_prev(tpath);
1386 gtk_tree_model_get_iter(store, iter, tpath);
1388 gtk_tree_path_free(tpath);
1393 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1395 if (!gtk_tree_model_get_iter_first(store, iter))
1400 GtkTreeIter next = *iter;
1402 if (gtk_tree_model_iter_next(store, &next))
1411 void vflist_select_invert(ViewFile *vf)
1414 GtkTreeSelection *selection;
1415 GtkTreeModel *store;
1418 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1419 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1421 /* Backward iteration prevents scrolling to the end of the list,
1422 * it scrolls to the first selected row instead. */
1423 valid = tree_model_get_iter_last(store, &iter);
1427 gint selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1430 gtk_tree_selection_unselect_iter(selection, &iter);
1432 gtk_tree_selection_select_iter(selection, &iter);
1434 valid = tree_model_iter_prev(store, &iter);
1438 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1442 if (vflist_find_row(vf, fd, &iter) < 0) return;
1444 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1446 if (!vflist_row_is_selected(vf, fd))
1448 GtkTreeSelection *selection;
1449 GtkTreeModel *store;
1452 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1453 gtk_tree_selection_unselect_all(selection);
1454 gtk_tree_selection_select_iter(selection, &iter);
1455 vflist_move_cursor(vf, &iter);
1457 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1458 tpath = gtk_tree_model_get_path(store, &iter);
1459 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1460 gtk_tree_path_free(tpath);
1464 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1466 GtkTreeModel *store;
1468 GtkTreeSelection *selection;
1472 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1474 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1475 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1477 valid = gtk_tree_model_get_iter_first(store, &iter);
1481 gboolean mark_val, selected;
1482 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1484 mark_val = fd->marks[n];
1485 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1489 case MTS_MODE_SET: selected = mark_val;
1491 case MTS_MODE_OR: selected = mark_val | selected;
1493 case MTS_MODE_AND: selected = mark_val & selected;
1495 case MTS_MODE_MINUS: selected = !mark_val & selected;
1500 gtk_tree_selection_select_iter(selection, &iter);
1502 gtk_tree_selection_unselect_iter(selection, &iter);
1504 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1508 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1510 GtkTreeModel *store;
1511 GtkTreeSelection *selection;
1516 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1518 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1519 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1523 GtkTreePath *tpath = work->data;
1527 gtk_tree_model_get_iter(store, &iter, tpath);
1528 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1532 case STM_MODE_SET: fd->marks[n] = 1;
1534 case STM_MODE_RESET: fd->marks[n] = 0;
1536 case STM_MODE_TOGGLE: fd->marks[n] = !fd->marks[n];
1540 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_MARKS + n, fd->marks[n], -1);
1544 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1549 *-----------------------------------------------------------------------------
1551 *-----------------------------------------------------------------------------
1554 static void vflist_listview_set_height(GtkWidget *listview, gint thumb)
1556 GtkTreeViewColumn *column;
1557 GtkCellRenderer *cell;
1560 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_COLUMN_THUMB - 1);
1561 if (!column) return;
1563 gtk_tree_view_column_set_fixed_width(column, ((thumb) ? options->thumbnails.max_width : 4) + 10);
1565 list = gtk_tree_view_column_get_cell_renderers(column);
1570 g_object_set(G_OBJECT(cell), "height", (thumb) ? options->thumbnails.max_height : -1, NULL);
1571 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1574 static void vflist_populate_view(ViewFile *vf)
1576 GtkTreeStore *store;
1580 GtkTreeRowReference *visible_row = NULL;
1584 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1585 thumbs = VFLIST_INFO(vf, thumbs_enabled);
1587 vflist_thumb_stop(vf);
1591 gtk_tree_store_clear(store);
1596 if (GTK_WIDGET_REALIZED(vf->listview) &&
1597 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1599 visible_row = gtk_tree_row_reference_new(GTK_TREE_MODEL(store), tpath);
1600 gtk_tree_path_free(tpath);
1603 vflist_listview_set_height(vf->listview, thumbs);
1605 valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
1611 FileData *fd = work->data;
1616 FileData *old_fd = NULL;
1620 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1628 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
1630 match = -1; /* probably should not happen*/
1645 gtk_tree_store_insert_before(store, &new, NULL, &iter);
1649 gtk_tree_store_append(store, &new, NULL);
1651 vflist_setup_iter_with_sidecars(vf, store, &new, fd);
1657 valid = gtk_tree_store_remove(store, &iter);
1661 vflist_setup_iter_with_sidecars(vf, store, &iter, fd);
1663 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1673 valid = gtk_tree_store_remove(store, &iter);
1678 if (gtk_tree_row_reference_valid(visible_row))
1680 tpath = gtk_tree_row_reference_get_path(visible_row);
1681 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(vf->listview), tpath, NULL, TRUE, 0.0, 0.0);
1682 gtk_tree_path_free(tpath);
1684 gtk_tree_row_reference_free(visible_row);
1688 vflist_thumb_update(vf);
1691 gint vflist_refresh(ViewFile *vf)
1696 old_list = vf->list;
1701 ret = filelist_read(vf->path, &vf->list, NULL);
1704 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1705 vflist_populate_view(vf);
1707 filelist_free(old_list);
1712 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1714 #define CELL_HEIGHT_OVERRIDE 512
1716 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1720 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1721 if (spec && G_IS_PARAM_SPEC_INT(spec))
1723 GParamSpecInt *spec_int;
1725 spec_int = G_PARAM_SPEC_INT(spec);
1726 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1730 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1732 static GdkColor color;
1733 static GtkWidget *done = NULL;
1739 style = gtk_widget_get_style(widget);
1740 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1741 shift_color(&color, -1, 0);
1748 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1749 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1751 ViewFile *vf = data;
1754 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1755 g_object_set(G_OBJECT(cell),
1756 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
1757 "cell-background-set", set, NULL);
1760 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gint image, gint right_justify, gint expand)
1762 GtkTreeViewColumn *column;
1763 GtkCellRenderer *renderer;
1765 column = gtk_tree_view_column_new();
1766 gtk_tree_view_column_set_title(column, title);
1767 gtk_tree_view_column_set_min_width(column, 4);
1771 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1772 renderer = gtk_cell_renderer_text_new();
1775 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1777 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1778 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1780 gtk_tree_view_column_set_expand(column, TRUE);
1784 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1785 renderer = gtk_cell_renderer_pixbuf_new();
1786 cell_renderer_height_override(renderer);
1787 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1788 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1791 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
1792 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1793 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1795 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1798 static void vflist_listview_mark_toggled(GtkCellRendererToggle *cell, gchar *path_str, GtkTreeStore *store)
1800 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1806 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
1809 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
1811 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
1813 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &mark, -1);
1815 fd->marks[col_idx - FILE_COLUMN_MARKS] = mark;
1817 gtk_tree_store_set(store, &iter, col_idx, mark, -1);
1818 gtk_tree_path_free(path);
1821 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
1823 GtkTreeViewColumn *column;
1824 GtkCellRenderer *renderer;
1825 GtkTreeStore *store;
1828 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1830 renderer = gtk_cell_renderer_toggle_new();
1831 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
1833 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1834 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
1835 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
1837 index = gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
1838 gtk_tree_view_column_set_fixed_width(column, 16);
1839 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
1842 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled), store);
1846 *-----------------------------------------------------------------------------
1848 *-----------------------------------------------------------------------------
1851 gint vflist_set_path(ViewFile *vf, const gchar *path)
1853 GtkTreeStore *store;
1855 if (!path) return FALSE;
1856 if (vf->path && strcmp(path, vf->path) == 0) return TRUE;
1859 vf->path = g_strdup(path);
1861 /* force complete reload */
1862 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1863 gtk_tree_store_clear(store);
1865 filelist_free(vf->list);
1868 return vf_refresh(vf);
1871 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1873 ViewFile *vf = data;
1875 vflist_select_idle_cancel(vf);
1876 vflist_thumb_stop(vf);
1878 filelist_free(vf->list);
1881 ViewFile *vflist_new(ViewFile *vf, const gchar *path)
1883 GtkTreeStore *store;
1884 GtkTreeSelection *selection;
1886 GType flist_types[FILE_COLUMN_COUNT];
1889 vf->info = g_new0(ViewFileInfoList, 1);
1891 VFLIST_INFO(vf, click_fd) = NULL;
1892 VFLIST_INFO(vf, select_fd) = NULL;
1893 VFLIST_INFO(vf, thumbs_enabled) = FALSE;
1895 VFLIST_INFO(vf, select_idle_id) = -1;
1897 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
1898 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
1899 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
1900 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
1901 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
1902 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
1903 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
1904 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
1905 flist_types[i] = G_TYPE_BOOLEAN;
1907 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
1909 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1910 g_object_unref(store);
1912 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1913 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1914 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
1916 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
1917 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
1919 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
1921 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
1922 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
1924 vflist_listview_add_column(vf, FILE_COLUMN_NAME, _("Name"), FALSE, FALSE, FALSE);
1925 vflist_listview_add_column(vf, FILE_COLUMN_SIDECARS, _("SC"), FALSE, FALSE, FALSE);
1927 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
1928 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
1933 void vflist_thumb_set(ViewFile *vf, gint enable)
1935 if (VFLIST_INFO(vf, thumbs_enabled) == enable) return;
1937 VFLIST_INFO(vf, thumbs_enabled) = enable;
1938 if (vf->layout) vf_refresh(vf);
1941 void vflist_marks_set(ViewFile *vf, gint enable)
1943 GList *columns, *work;
1945 if (vf->marks_enabled == enable) return;
1947 vf->marks_enabled = enable;
1949 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
1954 GtkTreeViewColumn *column = work->data;
1955 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
1958 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
1959 gtk_tree_view_column_set_visible(column, enable);
1962 g_list_free(columns);
1967 *-----------------------------------------------------------------------------
1968 * maintenance (for rename, move, remove)
1969 *-----------------------------------------------------------------------------
1972 static gint vflist_maint_find_closest(ViewFile *vf, gint row, gint count, GList *ignore_list)
1982 gint f = vf_index_by_path(vf, work->data);
1983 if (f >= 0) list = g_list_prepend(list, GINT_TO_POINTER(f));
1993 gpointer p = work->data;
1995 if (row == GPOINTER_TO_INT(p))
2000 if (rev == GPOINTER_TO_INT(p))
2005 if (!c) list = g_list_remove(list, p);
2013 if (row > count - 1)
2026 gint vflist_maint_renamed(ViewFile *vf, FileData *fd)
2032 if (g_list_index(vf->list, fd) < 0) return FALSE;
2034 source_base = remove_level_from_path(fd->change->source);
2035 dest_base = remove_level_from_path(fd->change->dest);
2038 if (strcmp(source_base, dest_base) == 0)
2040 GtkTreeStore *store;
2042 GtkTreeIter position;
2046 old_row = g_list_index(vf->list, fd);
2048 vf->list = g_list_remove(vf->list, fd);
2050 vf->list = filelist_insert_sort(vf->list, fd, vf->sort_method, vf->sort_ascend);
2051 n = g_list_index(vf->list, fd);
2053 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2054 if (vflist_find_row(vf, fd, &iter) >= 0 &&
2055 gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &position, NULL, n))
2059 gtk_tree_store_move_before(store, &iter, &position);
2063 gtk_tree_store_move_after(store, &iter, &position);
2066 gtk_tree_store_set(store, &iter, FILE_COLUMN_NAME, fd->name, -1);
2072 ret = vflist_maint_removed(vf, fd, NULL);
2075 g_free(source_base);
2081 gint vflist_maint_removed(ViewFile *vf, FileData *fd, GList *ignore_list)
2088 row = g_list_index(vf->list, fd);
2089 if (row < 0) return FALSE;
2091 if (vflist_index_is_selected(vf, row) &&
2092 layout_image_get_collection(vf->layout, NULL) == NULL)
2096 n = vf_count(vf, NULL);
2099 new_row = vflist_maint_find_closest(vf, row, n, ignore_list);
2100 DEBUG_1("row = %d, closest is %d", row, new_row);
2116 fd = vf_index_get_data(vf, new_row);
2117 if (vflist_find_row(vf, fd, &iter) >= 0)
2119 GtkTreeSelection *selection;
2121 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2122 gtk_tree_selection_select_iter(selection, &iter);
2123 vflist_move_cursor(vf, &iter);
2128 fd = vf_index_get_data(vf, row);
2129 if (vflist_find_row(vf, fd, &iter) >= 0)
2131 GtkTreeStore *store;
2132 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2133 gtk_tree_store_remove(store, &iter);
2135 list = g_list_nth(vf->list, row);
2138 /* thumbnail loader check */
2139 if (fd == vf->thumbs_filedata) vf->thumbs_filedata = NULL;
2140 if (vf->thumbs_count > 0) vf->thumbs_count--;
2142 vf->list = g_list_remove(vf->list, fd);
2143 file_data_unref(fd);
2150 gint vflist_maint_moved(ViewFile *vf, FileData *fd, GList *ignore_list)
2155 if (!fd->change->source || !vf->path) return FALSE;
2157 buf = remove_level_from_path(fd->change->source);
2159 if (strcmp(buf, vf->path) == 0)
2161 ret = vflist_maint_removed(vf, fd, ignore_list);