2 * Copyright (C) 2004 John Ellis
3 * Copyright (C) 2008 - 2016 The Geeqie Team
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "view_file_list.h"
26 #include "cache_maint.h"
32 #include "layout_image.h"
38 #include "ui_fileops.h"
41 #include "ui_tree_edit.h"
42 #include "uri_utils.h"
43 #include "view_file.h"
46 #include <gdk/gdkkeysyms.h> /* for keyboard values */
48 /* Index to tree store */
50 FILE_COLUMN_POINTER = 0,
53 FILE_COLUMN_FORMATTED,
54 FILE_COLUMN_FORMATTED_WITH_STARS,
57 FILE_COLUMN_STAR_RATING,
63 FILE_COLUMN_MARKS_LAST = FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
68 /* Index to tree view */
70 FILE_VIEW_COLUMN_MARKS = 0,
71 FILE_VIEW_COLUMN_MARKS_LAST = FILE_VIEW_COLUMN_MARKS + FILEDATA_MARKS_SIZE - 1,
72 FILE_VIEW_COLUMN_THUMB,
73 FILE_VIEW_COLUMN_FORMATTED,
74 FILE_VIEW_COLUMN_FORMATTED_WITH_STARS,
75 FILE_VIEW_COLUMN_STAR_RATING,
76 FILE_VIEW_COLUMN_SIZE,
77 FILE_VIEW_COLUMN_DATE,
78 FILE_VIEW_COLUMN_COUNT
83 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd);
84 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data);
85 static void vflist_populate_view(ViewFile *vf, gboolean force);
86 static gboolean vflist_is_multiline(ViewFile *vf);
87 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded);
91 *-----------------------------------------------------------------------------
93 *-----------------------------------------------------------------------------
100 } ViewFileFindRowData;
102 static gboolean vflist_find_row_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
104 ViewFileFindRowData *find = data;
106 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
117 static gint vflist_find_row(ViewFile *vf, FileData *fd, GtkTreeIter *iter)
120 ViewFileFindRowData data = {fd, iter, FALSE, 0};
122 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
123 gtk_tree_model_foreach(store, vflist_find_row_cb, &data);
133 static FileData *vflist_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter)
136 GtkTreeViewColumn *column;
138 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), x, y,
139 &tpath, &column, NULL, NULL))
145 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
146 gtk_tree_model_get_iter(store, &row, tpath);
147 gtk_tree_path_free(tpath);
148 gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &fd, -1);
156 static gboolean vflist_store_clear_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
159 gtk_tree_model_get(model, iter, FILE_COLUMN_POINTER, &fd, -1);
161 /* it seems that gtk_tree_store_clear may call some callbacks
162 that use the column. Set the pointer to NULL to be safe. */
163 gtk_tree_store_set(GTK_TREE_STORE(model), iter, FILE_COLUMN_POINTER, NULL, -1);
168 static void vflist_store_clear(ViewFile *vf, gboolean unlock_files)
173 if (unlock_files && vf->marks_enabled)
175 // unlock locked files in this directory
176 filelist_read(vf->dir_fd, &files, NULL);
179 FileData *fd = files->data;
181 file_data_unlock(fd);
182 file_data_unref(fd); // undo the ref that got added in filelist_read
187 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
188 gtk_tree_model_foreach(store, vflist_store_clear_cb, NULL);
189 gtk_tree_store_clear(GTK_TREE_STORE(store));
192 void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set)
197 if (vflist_find_row(vf, fd, &iter) < 0) return;
198 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
199 gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
202 static void vflist_move_cursor(ViewFile *vf, GtkTreeIter *iter)
207 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
209 tpath = gtk_tree_model_get_path(store, iter);
210 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
211 gtk_tree_path_free(tpath);
216 *-----------------------------------------------------------------------------
218 *-----------------------------------------------------------------------------
221 static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
222 GtkSelectionData *selection_data, guint info,
223 guint time, gpointer data)
228 if (!VFLIST(vf)->click_fd) return;
230 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
232 list = vf_selection_get_list(vf);
236 list = g_list_append(NULL, file_data_ref(VFLIST(vf)->click_fd));
240 uri_selection_data_set_uris_from_filelist(selection_data, list);
244 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
248 vflist_color_set(vf, VFLIST(vf)->click_fd, TRUE);
250 if (VFLIST(vf)->thumbs_enabled &&
251 VFLIST(vf)->click_fd && VFLIST(vf)->click_fd->thumb_pixbuf)
255 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
256 items = vf_selection_count(vf, NULL);
260 dnd_set_drag_icon(widget, context, VFLIST(vf)->click_fd->thumb_pixbuf, items);
264 static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
268 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
270 if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE)
276 static void vflist_drag_data_received(GtkWidget *entry_widget, GdkDragContext *context,
277 int x, int y, GtkSelectionData *selection,
278 guint info, guint time, gpointer data)
282 if (info == TARGET_TEXT_PLAIN) {
283 FileData *fd = vflist_find_data_by_coord(vf, x, y, NULL);
286 /* Add keywords to file */
287 gchar *str = (gchar *) gtk_selection_data_get_text(selection);
288 GList *kw_list = string_to_keywords_list(str);
290 metadata_append_list(fd, KEYWORD_KEY, kw_list);
291 string_list_free(kw_list);
297 void vflist_dnd_init(ViewFile *vf)
299 gtk_drag_source_set(vf->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
300 dnd_file_drag_types, dnd_file_drag_types_count,
301 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
302 gtk_drag_dest_set(vf->listview, GTK_DEST_DEFAULT_ALL,
303 dnd_file_drag_types, dnd_file_drag_types_count,
304 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
306 g_signal_connect(G_OBJECT(vf->listview), "drag_data_get",
307 G_CALLBACK(vflist_dnd_get), vf);
308 g_signal_connect(G_OBJECT(vf->listview), "drag_begin",
309 G_CALLBACK(vflist_dnd_begin), vf);
310 g_signal_connect(G_OBJECT(vf->listview), "drag_end",
311 G_CALLBACK(vflist_dnd_end), vf);
312 g_signal_connect(G_OBJECT(vf->listview), "drag_data_received",
313 G_CALLBACK(vflist_drag_data_received), vf);
317 *-----------------------------------------------------------------------------
319 *-----------------------------------------------------------------------------
322 GList *vflist_selection_get_one(ViewFile *vf, FileData *fd)
324 GList *list = g_list_append(NULL, file_data_ref(fd));
326 if (fd->sidecar_files)
328 /* check if the row is expanded */
332 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
333 if (vflist_find_row(vf, fd, &iter) >= 0)
337 tpath = gtk_tree_model_get_path(store, &iter);
338 if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
340 /* unexpanded - add whole group */
341 GList *work = fd->sidecar_files;
344 FileData *sfd = work->data;
345 list = g_list_prepend(list, file_data_ref(sfd));
349 gtk_tree_path_free(tpath);
351 list = g_list_reverse(list);
357 GList *vflist_pop_menu_file_list(ViewFile *vf)
359 if (!VFLIST(vf)->click_fd) return NULL;
361 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
363 return vf_selection_get_list(vf);
365 return vflist_selection_get_one(vf, VFLIST(vf)->click_fd);
369 void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
373 if (vflist_row_is_selected(vf, VFLIST(vf)->click_fd))
377 list = vf_selection_get_list(vf);
378 view_window_new_from_list(list);
383 view_window_new(VFLIST(vf)->click_fd);
387 void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
392 list = vf_pop_menu_file_list(vf);
393 if (options->file_ops.enable_in_place_rename &&
394 list && !list->next && VFLIST(vf)->click_fd)
401 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
402 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) >= 0)
406 tpath = gtk_tree_model_get_path(store, &iter);
407 tree_edit_by_path(GTK_TREE_VIEW(vf->listview), tpath,
408 FILE_VIEW_COLUMN_FORMATTED, VFLIST(vf)->click_fd->name,
409 vflist_row_rename_cb, vf);
410 gtk_tree_path_free(tpath);
415 file_util_rename(NULL, list, vf->listview);
418 void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
422 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
425 layout_thumb_set(vf->layout, !VFLIST(vf)->thumbs_enabled);
429 vflist_thumb_set(vf, !VFLIST(vf)->thumbs_enabled);
433 void vflist_star_rating_set(ViewFile *vf, gboolean enable)
435 GList *columns, *work;
437 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
442 GtkTreeViewColumn *column = work->data;
443 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
446 if (vflist_is_multiline(vf))
448 if (col_idx == FILE_COLUMN_FORMATTED_WITH_STARS)
450 gtk_tree_view_column_set_visible(column, enable);
452 if (col_idx == FILE_COLUMN_FORMATTED)
454 gtk_tree_view_column_set_visible(column, !enable);
459 if (col_idx == FILE_COLUMN_STAR_RATING)
461 gtk_tree_view_column_set_visible(column, enable);
465 g_list_free(columns);
468 void vflist_pop_menu_show_star_rating_cb(GtkWidget *widget, gpointer data)
472 options->show_star_rating = !options->show_star_rating;
474 vflist_populate_view(vf, TRUE);
476 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
477 vflist_star_rating_set(vf, options->show_star_rating);
480 void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
484 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
486 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
489 void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
492 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
493 VFLIST(vf)->click_fd = NULL;
499 *-----------------------------------------------------------------------------
501 *-----------------------------------------------------------------------------
504 static gboolean vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
509 if (!new || !new[0]) return FALSE;
511 new_path = g_build_filename(vf->dir_fd->path, new, NULL);
513 if (strchr(new, G_DIR_SEPARATOR) != NULL)
515 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
516 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vf->listview);
521 gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL);
522 FileData *fd = file_data_new_group(old_path); /* get the fd from cache */
523 file_util_rename_simple(fd, new_path, vf->listview);
533 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
541 if (vflist_find_row(vf, VFLIST(vf)->click_fd, &iter) < 0) return;
542 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
543 tpath = gtk_tree_model_get_path(store, &iter);
544 tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
545 gtk_tree_path_free(tpath);
547 popup_menu_position_clamp(menu, x, y, 0);
550 gboolean vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
555 if (event->keyval != GDK_KEY_Menu) return FALSE;
557 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &tpath, NULL);
563 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
564 gtk_tree_model_get_iter(store, &iter, tpath);
565 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->click_fd, -1);
566 gtk_tree_path_free(tpath);
570 VFLIST(vf)->click_fd = NULL;
573 vf->popup = vf_pop_menu(vf);
574 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, vflist_menu_position_cb, vf, 0, GDK_CURRENT_TIME);
579 gboolean vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
585 GtkTreeViewColumn *column;
587 vf->clicked_mark = 0;
589 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
590 &tpath, &column, NULL, NULL))
593 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
595 if (bevent->button == MOUSE_BUTTON_LEFT &&
596 col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
599 if (col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST)
600 vf->clicked_mark = 1 + (col_idx - FILE_COLUMN_MARKS);
602 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
604 gtk_tree_model_get_iter(store, &iter, tpath);
605 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
606 gtk_tree_path_free(tpath);
609 VFLIST(vf)->click_fd = fd;
611 if (bevent->button == MOUSE_BUTTON_RIGHT)
613 vf->popup = vf_pop_menu(vf);
614 gtk_menu_popup(GTK_MENU(vf->popup), NULL, NULL, NULL, NULL,
615 bevent->button, bevent->time);
619 if (!fd) return FALSE;
621 if (bevent->button == MOUSE_BUTTON_MIDDLE)
623 if (!vflist_row_is_selected(vf, fd))
625 vflist_color_set(vf, fd, TRUE);
631 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_BUTTON_PRESS &&
632 !(bevent->state & GDK_SHIFT_MASK ) &&
633 !(bevent->state & GDK_CONTROL_MASK ) &&
634 vflist_row_is_selected(vf, fd))
636 GtkTreeSelection *selection;
638 gtk_widget_grab_focus(widget);
641 /* returning FALSE and further processing of the event is needed for
642 correct operation of the expander, to show the sidecar files.
643 It however resets the selection of multiple files. With this condition
644 it should work for both cases */
645 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
646 return (gtk_tree_selection_count_selected_rows(selection) > 1);
649 if (bevent->button == MOUSE_BUTTON_LEFT && bevent->type == GDK_2BUTTON_PRESS)
651 if (VFLIST(vf)->click_fd->format_class == FORMAT_CLASS_COLLECTION)
653 collection_window_new(VFLIST(vf)->click_fd->path);
657 if (vf->layout) layout_image_full_screen_start(vf->layout);
664 gboolean vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
671 if (defined_mouse_buttons(widget, bevent, vf->layout))
676 if (bevent->button == MOUSE_BUTTON_MIDDLE)
678 vflist_color_set(vf, VFLIST(vf)->click_fd, FALSE);
681 if (bevent->button != MOUSE_BUTTON_LEFT && bevent->button != MOUSE_BUTTON_MIDDLE)
686 if ((bevent->x != 0 || bevent->y != 0) &&
687 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
688 &tpath, NULL, NULL, NULL))
692 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
693 gtk_tree_model_get_iter(store, &iter, tpath);
694 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
695 gtk_tree_path_free(tpath);
698 if (bevent->button == MOUSE_BUTTON_MIDDLE)
700 if (fd && VFLIST(vf)->click_fd == fd)
702 GtkTreeSelection *selection;
704 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
705 if (vflist_row_is_selected(vf, fd))
707 gtk_tree_selection_unselect_iter(selection, &iter);
711 gtk_tree_selection_select_iter(selection, &iter);
717 if (fd && VFLIST(vf)->click_fd == fd &&
718 !(bevent->state & GDK_SHIFT_MASK ) &&
719 !(bevent->state & GDK_CONTROL_MASK ) &&
720 vflist_row_is_selected(vf, fd))
722 GtkTreeSelection *selection;
724 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
725 gtk_tree_selection_unselect_all(selection);
726 gtk_tree_selection_select_iter(selection, &iter);
727 vflist_move_cursor(vf, &iter);
733 static void vflist_select_image(ViewFile *vf, FileData *sel_fd)
735 FileData *read_ahead_fd = NULL;
741 cur_fd = layout_image_get_fd(vf->layout);
742 if (sel_fd == cur_fd) return; /* no change */
744 row = g_list_index(vf->list, sel_fd);
745 // FIXME sidecar data
747 if (sel_fd && options->image.enable_read_ahead && row >= 0)
749 if (row > g_list_index(vf->list, cur_fd) &&
750 (guint) (row + 1) < vf_count(vf, NULL))
752 read_ahead_fd = vf_index_get_data(vf, row + 1);
756 read_ahead_fd = vf_index_get_data(vf, row - 1);
760 layout_image_set_with_ahead(vf->layout, sel_fd, read_ahead_fd);
763 static gboolean vflist_select_idle_cb(gpointer data)
769 VFLIST(vf)->select_idle_id = 0;
775 if (VFLIST(vf)->select_fd)
777 vflist_select_image(vf, VFLIST(vf)->select_fd);
778 VFLIST(vf)->select_fd = NULL;
781 VFLIST(vf)->select_idle_id = 0;
785 static void vflist_select_idle_cancel(ViewFile *vf)
787 if (VFLIST(vf)->select_idle_id)
789 g_source_remove(VFLIST(vf)->select_idle_id);
790 VFLIST(vf)->select_idle_id = 0;
794 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
795 gboolean path_currently_selected, gpointer data)
799 GtkTreePath *cursor_path;
801 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vf->listview), &cursor_path, NULL);
804 gtk_tree_model_get_iter(store, &iter, cursor_path);
805 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &VFLIST(vf)->select_fd, -1);
806 gtk_tree_path_free(cursor_path);
810 VFLIST(vf)->select_fd = NULL;
814 !VFLIST(vf)->select_idle_id)
816 VFLIST(vf)->select_idle_id = g_idle_add(vflist_select_idle_cb, vf);
822 static void vflist_expand_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
825 vflist_set_expanded(vf, iter, TRUE);
828 static void vflist_collapse_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
831 vflist_set_expanded(vf, iter, FALSE);
835 *-----------------------------------------------------------------------------
837 *-----------------------------------------------------------------------------
841 static gchar* vflist_get_formatted(ViewFile *vf, const gchar *name, const gchar *sidecars, const gchar *size, const gchar *time, gboolean expanded, gboolean with_stars, const gchar *star_rating)
843 gboolean multiline = vflist_is_multiline(vf);
850 text = g_strdup_printf("%s %s\n%s\n%s\n%s", name, expanded ? "" : sidecars, size, time, star_rating);
854 text = g_strdup_printf("%s %s\n%s\n%s", name, expanded ? "" : sidecars, size, time);
859 text = g_strdup_printf("%s %s", name, expanded ? "" : sidecars);
864 static void vflist_set_expanded(ViewFile *vf, GtkTreeIter *iter, gboolean expanded)
872 gchar *formatted_with_stars;
874 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
876 gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
877 FILE_COLUMN_NAME, &name,
878 FILE_COLUMN_SIDECARS, &sidecars,
879 FILE_COLUMN_SIZE, &size,
880 FILE_COLUMN_DATE, &time,
881 FILE_COLUMN_STAR_RATING, &star_rating,
884 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded, FALSE, NULL);
885 formatted_with_stars = vflist_get_formatted(vf, name, sidecars, size, time, expanded, TRUE, star_rating);
887 gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED, formatted,
888 FILE_COLUMN_EXPANDED, expanded,
890 gtk_tree_store_set(store, iter, FILE_COLUMN_FORMATTED_WITH_STARS, formatted_with_stars,
891 FILE_COLUMN_EXPANDED, expanded,
898 g_free(formatted_with_stars);
901 static void vflist_setup_iter(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *iter, FileData *fd)
904 gchar *sidecars = NULL;
906 const gchar *time = text_from_time(fd->date);
907 gchar *link = islink(fd->path) ? GQ_LINK_STR : "";
908 const gchar *disabled_grouping;
910 gchar *formatted_with_stars;
911 gboolean expanded = FALSE;
914 if (options->show_star_rating && fd->rating != STAR_RATING_NOT_READ)
916 star_rating = convert_rating_to_stars(fd->rating);
923 if (fd->sidecar_files) /* expanded has no effect on files without sidecars */
925 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_EXPANDED, &expanded, -1);
928 sidecars = file_data_sc_list_to_string(fd);
930 disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
931 name = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
932 size = text_from_size(fd->size);
934 formatted = vflist_get_formatted(vf, name, sidecars, size, time, expanded, FALSE, NULL);
935 formatted_with_stars = vflist_get_formatted(vf, name, sidecars, size, time, expanded, TRUE, star_rating);
937 gtk_tree_store_set(store, iter, FILE_COLUMN_POINTER, fd,
938 FILE_COLUMN_VERSION, fd->version,
939 FILE_COLUMN_THUMB, fd->thumb_pixbuf,
940 FILE_COLUMN_FORMATTED, formatted,
941 FILE_COLUMN_FORMATTED_WITH_STARS, formatted_with_stars,
942 FILE_COLUMN_SIDECARS, sidecars,
943 FILE_COLUMN_NAME, name,
944 FILE_COLUMN_STAR_RATING, star_rating,
945 FILE_COLUMN_SIZE, size,
946 FILE_COLUMN_DATE, time,
947 #define STORE_SET_IS_SLOW 1
948 #if STORE_SET_IS_SLOW
949 /* this is 3x faster on a directory with 20000 files */
950 FILE_COLUMN_MARKS + 0, file_data_get_mark(fd, 0),
951 FILE_COLUMN_MARKS + 1, file_data_get_mark(fd, 1),
952 FILE_COLUMN_MARKS + 2, file_data_get_mark(fd, 2),
953 FILE_COLUMN_MARKS + 3, file_data_get_mark(fd, 3),
954 FILE_COLUMN_MARKS + 4, file_data_get_mark(fd, 4),
955 FILE_COLUMN_MARKS + 5, file_data_get_mark(fd, 5),
956 FILE_COLUMN_MARKS + 6, file_data_get_mark(fd, 6),
957 FILE_COLUMN_MARKS + 7, file_data_get_mark(fd, 7),
958 FILE_COLUMN_MARKS + 8, file_data_get_mark(fd, 8),
959 FILE_COLUMN_MARKS + 9, file_data_get_mark(fd, 9),
960 #if FILEDATA_MARKS_SIZE != 10
961 #error this needs to be updated
964 FILE_COLUMN_COLOR, FALSE, -1);
966 #if !STORE_SET_IS_SLOW
969 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
970 gtk_tree_store_set(store, iter, FILE_COLUMN_MARKS + i, file_data_get_mark(fd, i), -1);
979 static void vflist_setup_iter_recursive(ViewFile *vf, GtkTreeStore *store, GtkTreeIter *parent_iter, GList *list, GList *selected, gboolean force)
984 gint num_ordered = 0;
985 gint num_prepended = 0;
987 valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, parent_iter);
993 FileData *fd = work->data;
994 gboolean done = FALSE;
998 FileData *old_fd = NULL;
999 gint old_version = 0;
1003 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1004 FILE_COLUMN_POINTER, &old_fd,
1005 FILE_COLUMN_VERSION, &old_version,
1015 match = filelist_sort_compare_filedata_full(fd, old_fd, SORT_NAME, TRUE); /* always sort sidecars by name */
1017 match = filelist_sort_compare_filedata_full(fd, old_fd, vf->sort_method, vf->sort_ascend);
1019 if (match == 0) g_warning("multiple fd for the same path");
1035 gtk_tree_store_insert_before(store, &new, parent_iter, &iter);
1040 here should be used gtk_tree_store_append, but this function seems to be O(n)
1041 and it seems to be much faster to add new entries to the beginning and reorder later
1044 gtk_tree_store_prepend(store, &new, parent_iter);
1047 vflist_setup_iter(vf, store, &new, file_data_ref(fd));
1048 vflist_setup_iter_recursive(vf, store, &new, fd->sidecar_files, selected, force);
1050 if (g_list_find(selected, fd))
1052 /* renamed files - the same fd appears at different position - select it again*/
1053 GtkTreeSelection *selection;
1054 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1055 gtk_tree_selection_select_iter(selection, &new);
1062 file_data_unref(old_fd);
1063 valid = gtk_tree_store_remove(store, &iter);
1068 if (fd->version != old_version || force)
1070 vflist_setup_iter(vf, store, &iter, fd);
1071 vflist_setup_iter_recursive(vf, store, &iter, fd->sidecar_files, selected, force);
1074 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1085 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1086 file_data_unref(old_fd);
1088 valid = gtk_tree_store_remove(store, &iter);
1091 /* move the prepended entries to the correct position */
1095 gint num_total = num_prepended + num_ordered;
1096 gint *new_order = g_malloc(num_total * sizeof(gint));
1098 for (i = 0; i < num_total; i++)
1100 if (i < num_ordered)
1101 new_order[i] = num_prepended + i;
1103 new_order[i] = num_total - 1 - i;
1105 gtk_tree_store_reorder(store, parent_iter, new_order);
1111 void vflist_sort_set(ViewFile *vf, SortType type, gboolean ascend)
1114 GHashTable *fd_idx_hash = g_hash_table_new(NULL, NULL);
1116 GtkTreeStore *store;
1119 if (vf->sort_method == type && vf->sort_ascend == ascend) return;
1120 if (!vf->list) return;
1126 FileData *fd = work->data;
1127 g_hash_table_insert(fd_idx_hash, fd, GINT_TO_POINTER(i));
1132 vf->sort_method = type;
1133 vf->sort_ascend = ascend;
1135 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1137 new_order = g_malloc(i * sizeof(gint));
1143 FileData *fd = work->data;
1144 new_order[i] = GPOINTER_TO_INT(g_hash_table_lookup(fd_idx_hash, fd));
1149 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1150 gtk_tree_store_reorder(store, NULL, new_order);
1153 g_hash_table_destroy(fd_idx_hash);
1157 *-----------------------------------------------------------------------------
1159 *-----------------------------------------------------------------------------
1163 void vflist_thumb_progress_count(GList *list, gint *count, gint *done)
1168 FileData *fd = work->data;
1171 if (fd->thumb_pixbuf) (*done)++;
1173 if (fd->sidecar_files)
1175 vflist_thumb_progress_count(fd->sidecar_files, count, done);
1181 void vflist_read_metadata_progress_count(GList *list, gint *count, gint *done)
1186 FileData *fd = work->data;
1189 if (fd->metadata_in_idle_loaded) (*done)++;
1191 if (fd->sidecar_files)
1193 vflist_read_metadata_progress_count(fd->sidecar_files, count, done);
1199 void vflist_set_thumb_fd(ViewFile *vf, FileData *fd)
1201 GtkTreeStore *store;
1204 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1206 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1207 gtk_tree_store_set(store, &iter, FILE_COLUMN_THUMB, fd->thumb_pixbuf, -1);
1210 FileData *vflist_thumb_next_fd(ViewFile *vf)
1213 FileData *fd = NULL;
1215 /* first check the visible files */
1217 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1219 GtkTreeModel *store;
1221 gboolean valid = TRUE;
1223 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1224 gtk_tree_model_get_iter(store, &iter, tpath);
1225 gtk_tree_path_free(tpath);
1228 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1232 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1234 if (!nfd->thumb_pixbuf) fd = nfd;
1236 valid = gtk_tree_model_iter_next(store, &iter);
1240 /* then find first undone */
1244 GList *work = vf->list;
1247 FileData *fd_p = work->data;
1248 if (!fd_p->thumb_pixbuf)
1252 GList *work2 = fd_p->sidecar_files;
1254 while (work2 && !fd)
1257 if (!fd_p->thumb_pixbuf) fd = fd_p;
1258 work2 = work2->next;
1268 void vflist_set_star_fd(ViewFile *vf, FileData *fd)
1270 GtkTreeStore *store;
1277 gchar *formatted_with_stars;
1280 if (!fd || vflist_find_row(vf, fd, &iter) < 0) return;
1282 star_rating = metadata_read_rating_stars(fd);
1284 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1285 gtk_tree_store_set(store, &iter, FILE_COLUMN_STAR_RATING, star_rating, -1);
1287 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1288 FILE_COLUMN_NAME, &name,
1289 FILE_COLUMN_SIDECARS, &sidecars,
1290 FILE_COLUMN_SIZE, &size,
1291 FILE_COLUMN_DATE, &time,
1292 FILE_COLUMN_EXPANDED, &expanded,
1295 formatted_with_stars = vflist_get_formatted(vf, name, sidecars, size, time, expanded, TRUE, star_rating);
1297 gtk_tree_store_set(store, &iter, FILE_COLUMN_FORMATTED_WITH_STARS, formatted_with_stars,
1298 FILE_COLUMN_EXPANDED, expanded,
1301 g_free(star_rating);
1302 g_free(formatted_with_stars);
1305 FileData *vflist_star_next_fd(ViewFile *vf)
1308 FileData *fd = NULL;
1309 FileData *nfd = NULL;
1311 /* first check the visible files */
1313 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
1315 GtkTreeModel *store;
1317 gboolean valid = TRUE;
1319 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1320 gtk_tree_model_get_iter(store, &iter, tpath);
1321 gtk_tree_path_free(tpath);
1324 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
1328 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &nfd, -1);
1330 if (nfd && nfd->rating == STAR_RATING_NOT_READ)
1335 valid = gtk_tree_model_iter_next(store, &iter);
1340 vf->stars_filedata = fd;
1342 if (vf->stars_id == 0)
1344 vf->stars_id = g_idle_add_full(G_PRIORITY_LOW, vf_stars_cb, vf, NULL);
1349 /* then find first undone */
1353 GList *work = vf->list;
1357 FileData *fd_p = work->data;
1359 if (fd_p && fd_p->rating == STAR_RATING_NOT_READ)
1373 vf->stars_filedata = fd;
1375 if (vf->stars_id == 0)
1377 vf->stars_id = g_idle_add_full(G_PRIORITY_LOW, vf_stars_cb, vf, NULL);
1386 *-----------------------------------------------------------------------------
1388 *-----------------------------------------------------------------------------
1391 gint vflist_index_by_fd(ViewFile *vf, FileData *fd)
1394 GList *work, *work2;
1399 FileData *list_fd = work->data;
1400 if (list_fd == fd) return p;
1402 work2 = list_fd->sidecar_files;
1405 /* FIXME: return the same index also for sidecars
1406 it is sufficient for next/prev navigation but it should be rewritten
1407 without using indexes at all
1409 FileData *sidecar_fd = work2->data;
1410 if (sidecar_fd == fd) return p;
1411 work2 = work2->next;
1422 *-----------------------------------------------------------------------------
1424 *-----------------------------------------------------------------------------
1427 static gboolean vflist_row_is_selected(ViewFile *vf, FileData *fd)
1429 GtkTreeModel *store;
1430 GtkTreeSelection *selection;
1433 gboolean found = FALSE;
1435 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1436 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1438 while (!found && work)
1440 GtkTreePath *tpath = work->data;
1444 gtk_tree_model_get_iter(store, &iter, tpath);
1445 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1446 if (fd_n == fd) found = TRUE;
1449 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1455 gboolean vflist_index_is_selected(ViewFile *vf, gint row)
1459 fd = vf_index_get_data(vf, row);
1460 return vflist_row_is_selected(vf, fd);
1463 guint vflist_selection_count(ViewFile *vf, gint64 *bytes)
1465 GtkTreeModel *store;
1466 GtkTreeSelection *selection;
1470 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1471 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1481 GtkTreePath *tpath = work->data;
1485 gtk_tree_model_get_iter(store, &iter, tpath);
1486 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1495 count = g_list_length(slist);
1496 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1502 GList *vflist_selection_get_list(ViewFile *vf)
1504 GtkTreeModel *store;
1505 GtkTreeSelection *selection;
1510 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1511 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1515 GtkTreePath *tpath = work->data;
1519 gtk_tree_model_get_iter(store, &iter, tpath);
1520 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1522 list = g_list_prepend(list, file_data_ref(fd));
1524 if (!fd->parent && !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vf->listview), tpath))
1526 /* unexpanded - add whole group */
1527 GList *work2 = fd->sidecar_files;
1530 FileData *sfd = work2->data;
1531 list = g_list_prepend(list, file_data_ref(sfd));
1532 work2 = work2->next;
1538 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1541 return g_list_reverse(list);
1544 GList *vflist_selection_get_list_by_index(ViewFile *vf)
1546 GtkTreeModel *store;
1547 GtkTreeSelection *selection;
1552 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1553 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1557 GtkTreePath *tpath = work->data;
1561 gtk_tree_model_get_iter(store, &iter, tpath);
1562 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1564 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vf->list, fd)));
1568 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1571 return g_list_reverse(list);
1574 void vflist_select_all(ViewFile *vf)
1576 GtkTreeSelection *selection;
1578 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1579 gtk_tree_selection_select_all(selection);
1581 VFLIST(vf)->select_fd = NULL;
1584 void vflist_select_none(ViewFile *vf)
1586 GtkTreeSelection *selection;
1588 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1589 gtk_tree_selection_unselect_all(selection);
1592 static gboolean tree_model_iter_prev(GtkTreeModel *store, GtkTreeIter *iter)
1597 tpath = gtk_tree_model_get_path(store, iter);
1598 result = gtk_tree_path_prev(tpath);
1600 gtk_tree_model_get_iter(store, iter, tpath);
1602 gtk_tree_path_free(tpath);
1607 static gboolean tree_model_get_iter_last(GtkTreeModel *store, GtkTreeIter *iter)
1609 if (!gtk_tree_model_get_iter_first(store, iter))
1614 GtkTreeIter next = *iter;
1616 if (gtk_tree_model_iter_next(store, &next))
1625 void vflist_select_invert(ViewFile *vf)
1628 GtkTreeSelection *selection;
1629 GtkTreeModel *store;
1632 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1633 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1635 /* Backward iteration prevents scrolling to the end of the list,
1636 * it scrolls to the first selected row instead. */
1637 valid = tree_model_get_iter_last(store, &iter);
1641 gboolean selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1644 gtk_tree_selection_unselect_iter(selection, &iter);
1646 gtk_tree_selection_select_iter(selection, &iter);
1648 valid = tree_model_iter_prev(store, &iter);
1652 void vflist_select_by_fd(ViewFile *vf, FileData *fd)
1656 if (vflist_find_row(vf, fd, &iter) < 0) return;
1658 tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, TRUE);
1660 if (!vflist_row_is_selected(vf, fd))
1662 GtkTreeSelection *selection;
1663 GtkTreeModel *store;
1666 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1667 gtk_tree_selection_unselect_all(selection);
1668 gtk_tree_selection_select_iter(selection, &iter);
1669 vflist_move_cursor(vf, &iter);
1671 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1672 tpath = gtk_tree_model_get_path(store, &iter);
1673 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vf->listview), tpath, NULL, FALSE);
1674 gtk_tree_path_free(tpath);
1678 static void vflist_select_closest(ViewFile *vf, FileData *sel_fd)
1681 FileData *fd = NULL;
1683 if (sel_fd->parent) sel_fd = sel_fd->parent;
1692 match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
1694 if (match >= 0) break;
1697 if (fd) vflist_select_by_fd(vf, fd);
1701 void vflist_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
1703 GtkTreeModel *store;
1705 GtkTreeSelection *selection;
1709 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1711 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
1712 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1714 valid = gtk_tree_model_get_iter_first(store, &iter);
1718 gboolean mark_val, selected;
1719 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, -1);
1721 mark_val = file_data_get_mark(fd, n);
1722 selected = gtk_tree_selection_iter_is_selected(selection, &iter);
1726 case MTS_MODE_SET: selected = mark_val;
1728 case MTS_MODE_OR: selected = mark_val || selected;
1730 case MTS_MODE_AND: selected = mark_val && selected;
1732 case MTS_MODE_MINUS: selected = !mark_val && selected;
1737 gtk_tree_selection_select_iter(selection, &iter);
1739 gtk_tree_selection_unselect_iter(selection, &iter);
1741 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1745 void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
1747 GtkTreeModel *store;
1748 GtkTreeSelection *selection;
1753 g_assert(mark >= 1 && mark <= FILEDATA_MARKS_SIZE);
1755 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
1756 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1760 GtkTreePath *tpath = work->data;
1764 gtk_tree_model_get_iter(store, &iter, tpath);
1765 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1767 /* the change has a very limited range and the standard notification would trigger
1768 complete re-read of the directory - try to do only minimal update instead */
1769 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification */
1773 case STM_MODE_SET: file_data_set_mark(fd, n, 1);
1775 case STM_MODE_RESET: file_data_set_mark(fd, n, 0);
1777 case STM_MODE_TOGGLE: file_data_set_mark(fd, n, !file_data_get_mark(fd, n));
1781 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
1783 vf_refresh_idle(vf);
1787 /* mark functions can have various side effects - update all columns to be sure */
1788 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
1789 /* mark functions can change sidecars too */
1790 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
1794 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1798 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1803 *-----------------------------------------------------------------------------
1805 *-----------------------------------------------------------------------------
1808 static void vflist_listview_set_columns(GtkWidget *listview, gboolean thumb, gboolean multiline)
1810 GtkTreeViewColumn *column;
1811 GtkCellRenderer *cell;
1814 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_THUMB);
1815 if (!column) return;
1817 gtk_tree_view_column_set_fixed_width(column, options->thumbnails.max_width + 4);
1819 list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1824 g_object_set(G_OBJECT(cell), "height", options->thumbnails.max_height, NULL);
1825 gtk_tree_view_column_set_visible(column, thumb);
1827 if (options->show_star_rating)
1829 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED_WITH_STARS);
1830 if (!column) return;
1831 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1832 gtk_tree_view_column_set_visible(column, TRUE);
1834 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1835 if (!column) return;
1836 gtk_tree_view_column_set_visible(column, FALSE);
1840 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED);
1841 if (!column) return;
1842 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column);
1843 gtk_tree_view_column_set_visible(column, TRUE);
1845 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_FORMATTED_WITH_STARS);
1846 if (!column) return;
1847 gtk_tree_view_column_set_visible(column, FALSE);
1850 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_STAR_RATING);
1851 if (!column) return;
1852 gtk_tree_view_column_set_visible(column, !multiline && options->show_star_rating);
1854 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_SIZE);
1855 if (!column) return;
1856 gtk_tree_view_column_set_visible(column, !multiline);
1858 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_VIEW_COLUMN_DATE);
1859 if (!column) return;
1860 gtk_tree_view_column_set_visible(column, !multiline);
1863 static gboolean vflist_is_multiline(ViewFile *vf)
1865 return (VFLIST(vf)->thumbs_enabled && options->thumbnails.max_height >= 48);
1869 static void vflist_populate_view(ViewFile *vf, gboolean force)
1871 GtkTreeStore *store;
1874 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
1881 vflist_store_clear(vf, FALSE);
1886 vflist_listview_set_columns(vf->listview, VFLIST(vf)->thumbs_enabled, vflist_is_multiline(vf));
1888 selected = vflist_selection_get_list(vf);
1890 vflist_setup_iter_recursive(vf, store, NULL, vf->list, selected, force);
1892 if (selected && vflist_selection_count(vf, NULL) == 0)
1894 /* all selected files disappeared */
1895 vflist_select_closest(vf, selected->data);
1898 filelist_free(selected);
1901 vf_thumb_update(vf);
1905 gboolean vflist_refresh(ViewFile *vf)
1908 gboolean ret = TRUE;
1910 old_list = vf->list;
1913 DEBUG_1("%s vflist_refresh: read dir", get_exec_time());
1916 file_data_unregister_notify_func(vf_notify_cb, vf); /* we don't need the notification of changes detected by filelist_read */
1918 ret = filelist_read(vf->dir_fd, &vf->list, NULL);
1920 if (vf->marks_enabled)
1922 // When marks are enabled, lock FileDatas so that we don't end up re-parsing XML
1923 // each time a mark is changed.
1924 file_data_lock_list(vf->list);
1928 // FIXME: only do this when needed (aka when we just switched from
1929 // FIXME: marks-enabled to marks-disabled)
1930 file_data_unlock_list(vf->list);
1933 vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf));
1934 vf->list = g_list_first(vf->list);
1935 vf->list = file_data_filter_file_filter_list(vf->list, vf_file_filter_get_filter(vf));
1937 vf->list = g_list_first(vf->list);
1938 vf->list = file_data_filter_class_list(vf->list, vf_class_get_filter(vf));
1940 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
1942 DEBUG_1("%s vflist_refresh: sort", get_exec_time());
1943 vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend);
1946 DEBUG_1("%s vflist_refresh: populate view", get_exec_time());
1948 vflist_populate_view(vf, FALSE);
1950 DEBUG_1("%s vflist_refresh: free filelist", get_exec_time());
1952 filelist_free(old_list);
1953 DEBUG_1("%s vflist_refresh: done", get_exec_time());
1960 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1962 #define CELL_HEIGHT_OVERRIDE 512
1964 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1968 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1969 if (spec && G_IS_PARAM_SPEC_INT(spec))
1971 GParamSpecInt *spec_int;
1973 spec_int = G_PARAM_SPEC_INT(spec);
1974 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1978 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1980 static GdkColor color;
1981 static GtkWidget *done = NULL;
1987 style = gtk_widget_get_style(widget);
1988 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1989 shift_color(&color, -1, 0);
1996 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1997 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1999 ViewFile *vf = data;
2002 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
2003 g_object_set(G_OBJECT(cell),
2004 "cell-background-gdk", vflist_listview_color_shifted(vf->listview),
2005 "cell-background-set", set, NULL);
2008 static void vflist_listview_add_column(ViewFile *vf, gint n, const gchar *title, gboolean image, gboolean right_justify, gboolean expand)
2010 GtkTreeViewColumn *column;
2011 GtkCellRenderer *renderer;
2013 column = gtk_tree_view_column_new();
2014 gtk_tree_view_column_set_title(column, title);
2015 gtk_tree_view_column_set_min_width(column, 4);
2019 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
2020 renderer = gtk_cell_renderer_text_new();
2023 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
2025 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2026 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
2028 gtk_tree_view_column_set_expand(column, TRUE);
2032 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2033 renderer = gtk_cell_renderer_pixbuf_new();
2034 cell_renderer_height_override(renderer);
2035 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2036 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
2039 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vf, NULL);
2040 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
2041 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
2043 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
2046 static void vflist_listview_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
2048 ViewFile *vf = data;
2049 GtkTreeStore *store;
2050 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
2056 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview)));
2057 if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
2060 col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_store_idx"));
2062 g_assert(col_idx >= FILE_COLUMN_MARKS && col_idx <= FILE_COLUMN_MARKS_LAST);
2064 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &fd, col_idx, &marked, -1);
2067 /* the change has a very limited range and the standard notification would trigger
2068 complete re-read of the directory - try to do only minimal update instead */
2069 file_data_unregister_notify_func(vf_notify_cb, vf);
2070 file_data_set_mark(fd, col_idx - FILE_COLUMN_MARKS, marked);
2071 if (!file_data_filter_marks(fd, vf_marks_get_filter(vf))) /* file no longer matches the filter -> remove it */
2073 vf_refresh_idle(vf);
2077 /* mark functions can have various side effects - update all columns to be sure */
2078 vflist_setup_iter(vf, GTK_TREE_STORE(store), &iter, fd);
2079 /* mark functions can change sidecars too */
2080 vflist_setup_iter_recursive(vf, GTK_TREE_STORE(store), &iter, fd->sidecar_files, NULL, FALSE);
2082 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2084 gtk_tree_path_free(path);
2087 static void vflist_listview_add_column_toggle(ViewFile *vf, gint n, const gchar *title)
2089 GtkTreeViewColumn *column;
2090 GtkCellRenderer *renderer;
2092 renderer = gtk_cell_renderer_toggle_new();
2093 column = gtk_tree_view_column_new_with_attributes(title, renderer, "active", n, NULL);
2095 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2096 g_object_set_data(G_OBJECT(column), "column_store_idx", GUINT_TO_POINTER(n));
2097 g_object_set_data(G_OBJECT(renderer), "column_store_idx", GUINT_TO_POINTER(n));
2099 gtk_tree_view_append_column(GTK_TREE_VIEW(vf->listview), column);
2100 gtk_tree_view_column_set_fixed_width(column, 22);
2101 gtk_tree_view_column_set_visible(column, vf->marks_enabled);
2104 g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(vflist_listview_mark_toggled_cb), vf);
2108 *-----------------------------------------------------------------------------
2110 *-----------------------------------------------------------------------------
2113 gboolean vflist_set_fd(ViewFile *vf, FileData *dir_fd)
2116 if (!dir_fd) return FALSE;
2117 if (vf->dir_fd == dir_fd) return TRUE;
2119 file_data_unref(vf->dir_fd);
2120 vf->dir_fd = file_data_ref(dir_fd);
2122 /* force complete reload */
2123 vflist_store_clear(vf, TRUE);
2125 filelist_free(vf->list);
2128 ret = vf_refresh(vf);
2129 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
2133 void vflist_destroy_cb(GtkWidget *widget, gpointer data)
2135 ViewFile *vf = data;
2137 file_data_unregister_notify_func(vf_notify_cb, vf);
2139 vflist_select_idle_cancel(vf);
2140 vf_refresh_idle_cancel(vf);
2144 filelist_free(vf->list);
2147 ViewFile *vflist_new(ViewFile *vf, FileData *dir_fd)
2149 GtkTreeStore *store;
2150 GtkTreeSelection *selection;
2151 GType flist_types[FILE_COLUMN_COUNT];
2155 vf->info = g_new0(ViewFileInfoList, 1);
2157 flist_types[FILE_COLUMN_POINTER] = G_TYPE_POINTER;
2158 flist_types[FILE_COLUMN_VERSION] = G_TYPE_INT;
2159 flist_types[FILE_COLUMN_THUMB] = GDK_TYPE_PIXBUF;
2160 flist_types[FILE_COLUMN_FORMATTED] = G_TYPE_STRING;
2161 flist_types[FILE_COLUMN_FORMATTED_WITH_STARS] = G_TYPE_STRING;
2162 flist_types[FILE_COLUMN_NAME] = G_TYPE_STRING;
2163 flist_types[FILE_COLUMN_STAR_RATING] = G_TYPE_STRING;
2164 flist_types[FILE_COLUMN_SIDECARS] = G_TYPE_STRING;
2165 flist_types[FILE_COLUMN_SIZE] = G_TYPE_STRING;
2166 flist_types[FILE_COLUMN_DATE] = G_TYPE_STRING;
2167 flist_types[FILE_COLUMN_EXPANDED] = G_TYPE_BOOLEAN;
2168 flist_types[FILE_COLUMN_COLOR] = G_TYPE_BOOLEAN;
2169 for (i = FILE_COLUMN_MARKS; i < FILE_COLUMN_MARKS + FILEDATA_MARKS_SIZE; i++)
2170 flist_types[i] = G_TYPE_BOOLEAN;
2172 store = gtk_tree_store_newv(FILE_COLUMN_COUNT, flist_types);
2174 vf->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2175 g_object_unref(store);
2177 g_signal_connect(G_OBJECT(vf->listview), "row-expanded",
2178 G_CALLBACK(vflist_expand_cb), vf);
2180 g_signal_connect(G_OBJECT(vf->listview), "row-collapsed",
2181 G_CALLBACK(vflist_collapse_cb), vf);
2183 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vf->listview));
2184 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
2185 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vf, NULL);
2187 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vf->listview), FALSE);
2188 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vf->listview), FALSE);
2190 gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(vf->listview), -1);
2194 for (i = 0; i < FILEDATA_MARKS_SIZE; i++)
2196 vflist_listview_add_column_toggle(vf, i + FILE_COLUMN_MARKS, "");
2197 g_assert(column == FILE_VIEW_COLUMN_MARKS + i);
2201 vflist_listview_add_column(vf, FILE_COLUMN_THUMB, "", TRUE, FALSE, FALSE);
2202 g_assert(column == FILE_VIEW_COLUMN_THUMB);
2205 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED, _("Name"), FALSE, FALSE, TRUE);
2206 g_assert(column == FILE_VIEW_COLUMN_FORMATTED);
2209 vflist_listview_add_column(vf, FILE_COLUMN_FORMATTED_WITH_STARS, _("NameStars"), FALSE, FALSE, TRUE);
2210 g_assert(column == FILE_VIEW_COLUMN_FORMATTED_WITH_STARS);
2213 vflist_listview_add_column(vf, FILE_COLUMN_STAR_RATING, _("Stars"), FALSE, FALSE, FALSE);
2214 g_assert(column == FILE_VIEW_COLUMN_STAR_RATING);
2217 vflist_listview_add_column(vf, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE, FALSE);
2218 g_assert(column == FILE_VIEW_COLUMN_SIZE);
2221 vflist_listview_add_column(vf, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE, FALSE);
2222 g_assert(column == FILE_VIEW_COLUMN_DATE);
2225 file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM);
2229 void vflist_thumb_set(ViewFile *vf, gboolean enable)
2231 if (VFLIST(vf)->thumbs_enabled == enable) return;
2233 VFLIST(vf)->thumbs_enabled = enable;
2235 /* vflist_populate_view is better than vf_refresh:
2236 - no need to re-read the directory
2237 - force update because the formatted string has changed
2241 vflist_populate_view(vf, TRUE);
2242 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vf->listview));
2246 void vflist_marks_set(ViewFile *vf, gboolean enable)
2248 GList *columns, *work;
2250 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(vf->listview));
2255 GtkTreeViewColumn *column = work->data;
2256 gint col_idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_store_idx"));
2259 if (col_idx <= FILE_COLUMN_MARKS_LAST && col_idx >= FILE_COLUMN_MARKS)
2260 gtk_tree_view_column_set_visible(column, enable);
2265 // Previously disabled, which means that vf->list is complete
2266 file_data_lock_list(vf->list);
2270 // Previously enabled, which means that vf->list is incomplete
2273 g_list_free(columns);
2276 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */