Part fix #711: Deleting image should not scroll file list unnecessarily
[geeqie.git] / src / view_file / view_file_icon.c
index f9d3810..64c9baa 100644 (file)
 #include "layout_image.h"
 #include "menu.h"
 #include "metadata.h"
+#include "misc.h"
 #include "thumb.h"
 #include "utilops.h"
 #include "ui_fileops.h"
 #include "ui_menu.h"
+#include "ui_misc.h"
 #include "ui_tree_edit.h"
 #include "uri_utils.h"
 #include "view_file.h"
@@ -48,7 +50,8 @@
 
 /* between these, the icon width is increased by thumb_max_width / 2 */
 #define THUMB_MIN_ICON_WIDTH 128
-#define THUMB_MAX_ICON_WIDTH 150
+#define THUMB_MAX_ICON_WIDTH 160
+#define THUMB_MIN_ICON_WIDTH_WITH_MARKS TOGGLE_SPACING * FILEDATA_MARKS_SIZE
 
 #define VFICON_MAX_COLUMNS 32
 #define THUMB_BORDER_PADDING 2
@@ -124,6 +127,23 @@ void vficon_pop_menu_show_names_cb(GtkWidget *widget, gpointer data)
        vficon_toggle_filenames(vf);
 }
 
+static void vficon_toggle_star_rating(ViewFile *vf)
+{
+       GtkAllocation allocation;
+
+       options->show_star_rating = !options->show_star_rating;
+
+       gtk_widget_get_allocation(vf->listview, &allocation);
+       vficon_populate_at_new_size(vf, allocation.width, allocation.height, TRUE);
+}
+
+void vficon_pop_menu_show_star_rating_cb(GtkWidget *widget, gpointer data)
+{
+       ViewFile *vf = data;
+
+       vficon_toggle_star_rating(vf);
+}
+
 void vficon_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
 {
        ViewFile *vf = data;
@@ -191,11 +211,12 @@ static gint vficon_get_icon_width(ViewFile *vf)
 {
        gint width;
 
-       if (!VFICON(vf)->show_text) return options->thumbnails.max_width;
+       if (!VFICON(vf)->show_text && !vf->marks_enabled ) return options->thumbnails.max_width;
 
        width = options->thumbnails.max_width + options->thumbnails.max_width / 2;
        if (width < THUMB_MIN_ICON_WIDTH) width = THUMB_MIN_ICON_WIDTH;
        if (width > THUMB_MAX_ICON_WIDTH) width = options->thumbnails.max_width;
+       if (vf->marks_enabled && width < THUMB_MIN_ICON_WIDTH_WITH_MARKS) width = THUMB_MIN_ICON_WIDTH_WITH_MARKS;
 
        return width;
 }
@@ -619,6 +640,13 @@ void vficon_marks_set(ViewFile *vf, gint enable)
        vficon_populate_at_new_size(vf, allocation.width, allocation.height, TRUE);
 }
 
+void vficon_star_rating_set(ViewFile *vf, gint enable)
+{
+       GtkAllocation allocation;
+       gtk_widget_get_allocation(vf->listview, &allocation);
+       vficon_populate_at_new_size(vf, allocation.width, allocation.height, TRUE);
+}
+
 /*
  *-------------------------------------------------------------------
  * selections
@@ -894,6 +922,26 @@ void vficon_select_by_fd(ViewFile *vf, FileData *fd)
        vficon_set_focus(vf, fd);
 }
 
+void vficon_select_list(ViewFile *vf, GList *list)
+{
+       GList *work;
+       FileData *fd;
+
+       if (!list) return;
+
+       work = list;
+       while (work)
+               {
+               fd = work->data;
+               if (g_list_find(vf->list, fd))
+                       {
+                       VFICON(vf)->selection = g_list_append(VFICON(vf)->selection, fd);
+                       vficon_selection_add(vf, fd, SELECTION_SELECTED, NULL);
+                       }
+               work = work->next;
+               }
+}
+
 void vficon_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
 {
        GList *work;
@@ -1087,7 +1135,7 @@ static void vficon_set_focus(ViewFile *vf, FileData *fd)
                        /* ensure focus row col are correct */
                        vficon_find_position(vf, VFICON(vf)->focus_fd, &VFICON(vf)->focus_row, &VFICON(vf)->focus_column);
 #if GTK_CHECK_VERSION(3,0,0)
-/* FIXME: Refer to issue #467 on Github. The thumbnail position is not
+/** @FIXME Refer to issue #467 on Github. The thumbnail position is not
  * preserved when the icon view is refreshed. Caused by an unknown call from
  * the idle loop. This patch hides the problem.
  */
@@ -1341,8 +1389,15 @@ gboolean vficon_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer dat
 
                        if (bevent->type == GDK_2BUTTON_PRESS && vf->layout)
                                {
-                               vficon_selection_remove(vf, VFICON(vf)->click_fd, SELECTION_PRELIGHT, &iter);
-                               layout_image_full_screen_start(vf->layout);
+                               if (VFICON(vf)->click_fd->format_class == FORMAT_CLASS_COLLECTION)
+                                       {
+                                       collection_window_new(VFICON(vf)->click_fd->path);
+                                       }
+                               else
+                                       {
+                                       vficon_selection_remove(vf, VFICON(vf)->click_fd, SELECTION_PRELIGHT, &iter);
+                                       layout_image_full_screen_start(vf->layout);
+                                       }
                                }
                        break;
                case MOUSE_BUTTON_RIGHT:
@@ -1365,6 +1420,11 @@ gboolean vficon_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer d
 
        tip_schedule(vf);
 
+       if (defined_mouse_buttons(widget, bevent, vf->layout))
+               {
+               return TRUE;
+               }
+
        if ((gint)bevent->x != 0 || (gint)bevent->y != 0)
                {
                fd = vficon_find_data_by_coord(vf, (gint)bevent->x, (gint)bevent->y, &iter);
@@ -1540,7 +1600,7 @@ static void vficon_populate(ViewFile *vf, gboolean resize, gboolean keep_positio
                                {
                                g_object_set(G_OBJECT(cell), "fixed_width", thumb_width,
                                                             "fixed_height", options->thumbnails.max_height,
-                                                            "show_text", VFICON(vf)->show_text,
+                                                            "show_text", VFICON(vf)->show_text || options->show_star_rating,
                                                             "show_marks", vf->marks_enabled,
                                                             "num_marks", FILEDATA_MARKS_SIZE,
                                                             NULL);
@@ -1623,6 +1683,7 @@ static void vficon_populate(ViewFile *vf, gboolean resize, gboolean keep_positio
 
        vf_send_update(vf);
        vf_thumb_update(vf);
+       vf_star_update(vf);
 }
 
 static void vficon_populate_at_new_size(ViewFile *vf, gint w, gint h, gboolean force)
@@ -1688,6 +1749,19 @@ void vficon_thumb_progress_count(GList *list, gint *count, gint *done)
                }
 }
 
+void vficon_read_metadata_progress_count(GList *list, gint *count, gint *done)
+{
+       GList *work = list;
+       while (work)
+               {
+               FileData *fd = work->data;
+               work = work->next;
+
+               if (fd->metadata_in_idle_loaded) (*done)++;
+               (*count)++;
+               }
+}
+
 void vficon_set_thumb_fd(ViewFile *vf, FileData *fd)
 {
        GtkTreeModel *store;
@@ -1703,14 +1777,12 @@ void vficon_set_thumb_fd(ViewFile *vf, FileData *fd)
        gtk_list_store_set(GTK_LIST_STORE(store), &iter, FILE_COLUMN_POINTER, list, -1);
 }
 
-// TOOD(xsdg): This could be broken
+/* Returns the next fd without a loaded pixbuf, so the thumb-loader can load the pixbuf for it. */
 FileData *vficon_thumb_next_fd(ViewFile *vf)
 {
        GtkTreePath *tpath;
-       FileData *fd = NULL;
-
-       /* first check the visible files */
 
+       /* First see if there are visible files that don't have a loaded thumb... */
        if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
                {
                GtkTreeModel *store;
@@ -1722,130 +1794,139 @@ FileData *vficon_thumb_next_fd(ViewFile *vf)
                gtk_tree_path_free(tpath);
                tpath = NULL;
 
-               while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
+               while (valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
                        {
                        GList *list;
-
                        gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
 
-                       while (!fd && list)
+                       /** @todo (xsdg): for loop here. */
+                       for (; list; list = list->next)
                                {
-                               FileData *new_fd = list->data;
-                               if (new_fd && !fd->thumb_pixbuf) fd = new_fd;
-                               list = list->next;
+                               FileData *fd = list->data;
+                               if (fd && !fd->thumb_pixbuf) return fd;
                                }
 
                        valid = gtk_tree_model_iter_next(store, &iter);
                        }
                }
 
-       /* then find first undone */
-
-       if (!fd)
+       /* Then iterate through the entire list to load all of them. */
+       GList *work;
+       for (work = vf->list; work; work = work->next)
                {
-               GList *work = vf->list;
-               while (work && !fd)
-                       {
-                       FileData *fd_p = work->data;
-                       work = work->next;
+               FileData *fd = work->data;
 
-                       // Note: This implementation differs from view_file_list.c because sidecar files are not
-                       // distinct list elements here, as they are in the list view.
-                       if (!fd_p->thumb_pixbuf) fd = fd_p;
-                       }
+               // Note: This implementation differs from view_file_list.c because sidecar files are not
+               // distinct list elements here, as they are in the list view.
+               if (!fd->thumb_pixbuf) return fd;
                }
 
-       return fd;
+       return NULL;
 }
 
-void vficon_thumb_reset_all(ViewFile *vf)
+void vficon_set_star_fd(ViewFile *vf, FileData *fd)
 {
-       GList *work = vf->list;
+       GtkTreeModel *store;
+       GtkTreeIter iter;
+       GList *list;
 
-       while (work)
-               {
-               FileData *fd = work->data;
-               if (fd->thumb_pixbuf)
-                       {
-                       g_object_unref(fd->thumb_pixbuf);
-                       fd->thumb_pixbuf = NULL;
-                       }
-               work = work->next;
-               }
-}
+       if (!g_list_find(vf->list, fd)) return;
+       if (!vficon_find_iter(vf, fd, &iter, NULL)) return;
 
+       store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
 
-/*
- *-----------------------------------------------------------------------------
- * row stuff
- *-----------------------------------------------------------------------------
- */
+       gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
+       gtk_list_store_set(GTK_LIST_STORE(store), &iter, FILE_COLUMN_POINTER, list, -1);
+}
 
-FileData *vficon_index_get_data(ViewFile *vf, gint row)
+FileData *vficon_star_next_fd(ViewFile *vf)
 {
-       FileData *fd;
+       GtkTreePath *tpath;
 
-       fd = g_list_nth_data(vf->list, row);
-       return fd ? fd : NULL;
-}
+       /* first check the visible files */
+
+       if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
+               {
+               GtkTreeModel *store;
+               GtkTreeIter iter;
+               gboolean valid = TRUE;
 
+               store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
+               gtk_tree_model_get_iter(store, &iter, tpath);
+               gtk_tree_path_free(tpath);
+               tpath = NULL;
 
-gint vficon_index_by_fd(ViewFile *vf, FileData *in_fd)
-{
-       gint p = 0;
-       GList *work;
+               while (valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
+                       {
+                       GList *list;
+                       gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
 
-       if (!in_fd) return -1;
+                       for (; list; list = list->next)
+                               {
+                               FileData *fd = list->data;
+                               if (fd && fd->rating == STAR_RATING_NOT_READ)
+                                       {
+                                       vf->stars_filedata = fd;
 
-       work = vf->list;
-       while (work)
-               {
-               FileData *fd = work->data;
-               if (fd == in_fd) return p;
-               work = work->next;
-               p++;
+                                       if (vf->stars_id == 0)
+                                               {
+                                               vf->stars_id = g_idle_add_full(G_PRIORITY_LOW, vf_stars_cb, vf, NULL);
+                                               }
+
+                                       return fd;
+                                       }
+                               }
+
+                       valid = gtk_tree_model_iter_next(store, &iter);
+                       }
                }
 
-       return -1;
-}
+       /* Then iterate through the entire list to load all of them. */
 
-guint vficon_count(ViewFile *vf, gint64 *bytes)
-{
-       if (bytes)
+       GList *work;
+       for (work = vf->list; work; work = work->next)
                {
-               gint64 b = 0;
-               GList *work;
+               FileData *fd = work->data;
 
-               work = vf->list;
-               while (work)
+               if (fd && fd->rating == STAR_RATING_NOT_READ)
                        {
-                       FileData *fd = work->data;
-                       work = work->next;
+                       vf->stars_filedata = fd;
 
-                       b += fd->size;
-                       }
+                       if (vf->stars_id == 0)
+                               {
+                               vf->stars_id = g_idle_add_full(G_PRIORITY_LOW, vf_stars_cb, vf, NULL);
+                               }
 
-               *bytes = b;
+                       return fd;
+                       }
                }
 
-       return g_list_length(vf->list);
+       return NULL;
 }
 
-GList *vficon_get_list(ViewFile *vf)
+/*
+ *-----------------------------------------------------------------------------
+ * row stuff
+ *-----------------------------------------------------------------------------
+ */
+
+gint vficon_index_by_fd(ViewFile *vf, FileData *in_fd)
 {
-       GList *list = NULL;
+       gint p = 0;
        GList *work;
 
+       if (!in_fd) return -1;
+
        work = vf->list;
        while (work)
                {
                FileData *fd = work->data;
+               if (fd == in_fd) return p;
                work = work->next;
-
-               list = g_list_prepend(list, file_data_ref(fd));
+               p++;
                }
 
-       return g_list_reverse(list);
+       return -1;
 }
 
 /*
@@ -1862,13 +1943,24 @@ static gboolean vficon_refresh_real(ViewFile *vf, gboolean keep_position)
        FileData *first_selected = NULL;
        GList *new_filelist = NULL;
        GList *new_fd_list = NULL;
+       GList *old_selected = NULL;
+       GtkTreePath *end_path = NULL;
+       GtkTreePath *start_path = NULL;
 
        focus_fd = VFICON(vf)->focus_fd;
 
+       gtk_tree_view_get_visible_range(GTK_TREE_VIEW(vf->listview), &start_path, &end_path);
+
        if (vf->dir_fd)
                {
                ret = filelist_read(vf->dir_fd, &new_filelist, NULL);
                new_filelist = file_data_filter_marks_list(new_filelist, vf_marks_get_filter(vf));
+               new_filelist = g_list_first(new_filelist);
+               new_filelist = file_data_filter_file_filter_list(new_filelist, vf_file_filter_get_filter(vf));
+
+               new_filelist = g_list_first(new_filelist);
+               new_filelist = file_data_filter_class_list(new_filelist, vf_class_get_filter(vf));
+
                }
 
        vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend); /* the list might not be sorted if there were renames */
@@ -1876,6 +1968,7 @@ static gboolean vficon_refresh_real(ViewFile *vf, gboolean keep_position)
 
        if (VFICON(vf)->selection)
                {
+               old_selected = g_list_copy(VFICON(vf)->selection);
                first_selected = VFICON(vf)->selection->data;
                file_data_ref(first_selected);
                g_list_free(VFICON(vf)->selection);
@@ -1960,6 +2053,26 @@ static gboolean vficon_refresh_real(ViewFile *vf, gboolean keep_position)
 
        VFICON(vf)->selection = g_list_reverse(VFICON(vf)->selection);
 
+       /* Preserve the original selection order */
+       if (old_selected)
+               {
+               GList *reversed_old_selected;
+
+               reversed_old_selected = g_list_reverse(old_selected);
+               while (reversed_old_selected)
+                       {
+                       GList *tmp;
+                       tmp = g_list_find(VFICON(vf)->selection, reversed_old_selected->data);
+                       if (tmp)
+                               {
+                               VFICON(vf)->selection = g_list_remove_link(VFICON(vf)->selection, tmp);
+                               VFICON(vf)->selection = g_list_concat(tmp, VFICON(vf)->selection);
+                               }
+                       reversed_old_selected = reversed_old_selected->next;
+                       }
+               g_list_free(old_selected);
+               }
+
        filelist_free(new_filelist);
 
        vficon_populate(vf, TRUE, keep_position);
@@ -1971,12 +2084,14 @@ static gboolean vficon_refresh_real(ViewFile *vf, gboolean keep_position)
                }
        file_data_unref(first_selected);
 
-       /* attempt to keep focus on same icon when refreshing */
-       if (focus_fd && g_list_find(vf->list, focus_fd))
+       if (start_path)
                {
-               vficon_set_focus(vf, focus_fd);
+               gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(vf->listview), start_path, NULL, FALSE, 0.0, 0.0);
                }
 
+       gtk_tree_path_free(start_path);
+       gtk_tree_path_free(end_path);
+
        return ret;
 }
 
@@ -2005,6 +2120,7 @@ static void vficon_cell_data_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer
        FileData *fd;
        ColumnData *cd = data;
        ViewFile *vf = cd->vf;
+       gchar *star_rating;
 
        if (!GQV_IS_CELL_RENDERER_ICON(cell)) return;
 
@@ -2017,24 +2133,56 @@ static void vficon_cell_data_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer
                GdkColor color_fg;
                GdkColor color_bg;
                GtkStyle *style;
-               gchar *name_sidecars;
+               gchar *name_sidecars = NULL;
                gchar *link;
                GtkStateType state = GTK_STATE_NORMAL;
 
                g_assert(fd->magick == FD_MAGICK);
 
+               if (options->show_star_rating && fd->rating != STAR_RATING_NOT_READ)
+                       {
+                       star_rating = convert_rating_to_stars(fd->rating);
+                       }
+               else
+                       {
+                       star_rating = NULL;
+                       }
+
                link = islink(fd->path) ? GQ_LINK_STR : "";
                if (fd->sidecar_files)
                        {
                        gchar *sidecars = file_data_sc_list_to_string(fd);
-                       name_sidecars = g_strdup_printf("%s%s %s", link, fd->name, sidecars);
+                       if (options->show_star_rating && VFICON(vf)->show_text)
+                               {
+                               name_sidecars = g_strdup_printf("%s%s %s\n%s", link, fd->name, sidecars, star_rating);
+                               }
+                       else if (options->show_star_rating)
+                               {
+                               name_sidecars = g_strdup_printf("%s", star_rating);
+                               }
+                       else if (VFICON(vf)->show_text)
+                               {
+                               name_sidecars = g_strdup_printf("%s%s %s", link, fd->name, sidecars);
+                               }
                        g_free(sidecars);
                        }
                else
                        {
                        gchar *disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
-                       name_sidecars = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
+                       if (options->show_star_rating && VFICON(vf)->show_text)
+                               {
+                               name_sidecars = g_strdup_printf("%s%s%s\n%s", link, fd->name, disabled_grouping, star_rating);
+                               }
+                       else if (options->show_star_rating)
+                               {
+                               name_sidecars = g_strdup_printf("%s", star_rating);
+                               }
+                       else if (VFICON(vf)->show_text)
+                               {
+                               name_sidecars = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
+                               }
                        }
+               g_free(star_rating);
 
                style = gtk_widget_get_style(vf->listview);
                if (fd->selected & SELECTION_SELECTED)
@@ -2145,6 +2293,7 @@ void vficon_destroy_cb(GtkWidget *widget, gpointer data)
        tip_unschedule(vf);
 
        vf_thumb_cleanup(vf);
+       vf_star_cleanup(vf);
 
        g_list_free(vf->list);
        g_list_free(VFICON(vf)->selection);