diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 7c1155d309f0..8e80c6b14915 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -11,6 +11,7 @@ include(CheckCXXSymbolExists)
#
FILE(GLOB SOURCE_FILES
"bauhaus/bauhaus.c"
+ "common/act_on.c"
"common/atomic.c"
"common/bilateral.c"
"common/bilateralcl.c"
diff --git a/src/common/act_on.c b/src/common/act_on.c
new file mode 100644
index 000000000000..9599aaf31dca
--- /dev/null
+++ b/src/common/act_on.c
@@ -0,0 +1,436 @@
+/*
+ This file is part of darktable,
+ Copyright (C) 2021 darktable developers.
+
+ darktable is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ darktable is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with darktable. If not, see .
+*/
+
+#include "common/act_on.h"
+#include "common/collection.h"
+#include "common/darktable.h"
+#include "common/debug.h"
+#include "common/image.h"
+#include "common/image_cache.h"
+#include "common/selection.h"
+#include "control/control.h"
+#include "dtgtk/thumbtable.h"
+#include
+#include
+
+
+static int _find_custom(gconstpointer a, gconstpointer b)
+{
+ return (GPOINTER_TO_INT(a) != GPOINTER_TO_INT(b));
+}
+static void _insert_in_list(GList **list, const int imgid, gboolean only_visible)
+{
+ if(only_visible)
+ {
+ if(!g_list_find_custom(*list, GINT_TO_POINTER(imgid), _find_custom))
+ *list = g_list_append(*list, GINT_TO_POINTER(imgid));
+ return;
+ }
+
+ const dt_image_t *image = dt_image_cache_get(darktable.image_cache, imgid, 'r');
+ if(image)
+ {
+ const int img_group_id = image->group_id;
+ dt_image_cache_read_release(darktable.image_cache, image);
+
+ if(!darktable.gui || !darktable.gui->grouping || darktable.gui->expanded_group_id == img_group_id
+ || !dt_selection_get_collection(darktable.selection))
+ {
+ if(!g_list_find_custom(*list, GINT_TO_POINTER(imgid), _find_custom))
+ *list = g_list_append(*list, GINT_TO_POINTER(imgid));
+ }
+ else
+ {
+ sqlite3_stmt *stmt;
+ gchar *query = g_strdup_printf(
+ "SELECT id"
+ " FROM main.images"
+ " WHERE group_id = %d AND id IN (%s)",
+ img_group_id, dt_collection_get_query_no_group(dt_selection_get_collection(darktable.selection)));
+ DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), query, -1, &stmt, NULL);
+ while(sqlite3_step(stmt) == SQLITE_ROW)
+ {
+ const int imgidg = sqlite3_column_int(stmt, 0);
+ if(!g_list_find_custom(*list, GINT_TO_POINTER(imgidg), _find_custom))
+ *list = g_list_append(*list, GINT_TO_POINTER(imgidg));
+ }
+ sqlite3_finalize(stmt);
+ g_free(query);
+ }
+ }
+}
+
+// test if the cache is still valid
+static gboolean _test_cache(dt_act_on_cache_t *cache)
+{
+ const int mouseover = dt_control_get_mouse_over_id();
+
+ if(cache->ok && cache->image_over == mouseover
+ && cache->inside_table == dt_ui_thumbtable(darktable.gui->ui)->mouse_inside
+ && g_slist_length(cache->active_imgs) == g_slist_length(darktable.view_manager->active_images))
+ {
+ // we test active images if mouse outside table
+ gboolean ok = TRUE;
+ if(!dt_ui_thumbtable(darktable.gui->ui)->mouse_inside && cache->active_imgs)
+ {
+ GSList *l1 = cache->active_imgs;
+ GSList *l2 = darktable.view_manager->active_images;
+ while(l1 && l2)
+ {
+ if(GPOINTER_TO_INT(l1->data) != GPOINTER_TO_INT(l2->data))
+ {
+ ok = FALSE;
+ break;
+ }
+ l2 = g_slist_next(l2);
+ l1 = g_slist_next(l1);
+ }
+ }
+ if(ok) return TRUE;
+ }
+ return FALSE;
+}
+
+// cache the list of images to act on during global changes (libs, accels)
+// return TRUE if the cache is updated, FALSE if it's still up to date
+gboolean _cache_update(const gboolean only_visible, const gboolean force, const gboolean ordered)
+{
+ /** Here's how it works
+ *
+ * mouse over| x | x | x | | |
+ * mouse inside table| x | x | | | |
+ * mouse inside selection| x | | | | |
+ * active images| ? | ? | x | | x |
+ * | | | | | |
+ * | S | O | O | S | A |
+ * S = selection ; O = mouseover ; A = active images
+ * the mouse can be outside thumbtable in case of filmstrip + mouse in center widget
+ *
+ * if only_visible is FALSE, then it will add also not visible images because of grouping
+ * force define if we try to use cache or not
+ * if ordered is TRUE, we return the list in the gui order. Otherwise the order is undefined (but quicker)
+ **/
+
+ const int mouseover = dt_control_get_mouse_over_id();
+
+ dt_act_on_cache_t *cache;
+ if(only_visible)
+ cache = &darktable.view_manager->act_on_cache_visible;
+ else
+ cache = &darktable.view_manager->act_on_cache_all;
+
+ // if possible, we return the cached list
+ if(!force && cache->ordered == ordered && _test_cache(cache))
+ {
+ return FALSE;
+ }
+
+ GList *l = NULL;
+ gboolean inside_sel = FALSE;
+ if(mouseover > 0)
+ {
+ // column 1,2,3
+ if(dt_ui_thumbtable(darktable.gui->ui)->mouse_inside)
+ {
+ // column 1,2
+ sqlite3_stmt *stmt;
+ gchar *query = g_strdup_printf("SELECT imgid FROM main.selected_images WHERE imgid=%d", mouseover);
+ DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), query, -1, &stmt, NULL);
+ if(stmt != NULL && sqlite3_step(stmt) == SQLITE_ROW)
+ {
+ inside_sel = TRUE;
+ sqlite3_finalize(stmt);
+ }
+ g_free(query);
+
+ if(inside_sel)
+ {
+ // column 1
+
+ // first, we try to return cached list if we were already
+ // inside sel and the selection has not changed
+ if(!force && cache->ok && cache->image_over_inside_sel && cache->inside_table && cache->ordered == ordered)
+ {
+ return FALSE;
+ }
+
+ // we return the list of the selection
+ l = dt_selection_get_list(darktable.selection, only_visible, ordered);
+ }
+ else
+ {
+ // column 2
+ _insert_in_list(&l, mouseover, only_visible);
+ }
+ }
+ else
+ {
+ // column 3
+ _insert_in_list(&l, mouseover, only_visible);
+ // be absolutely sure we have the id in the list (in darkroom,
+ // the active image can be out of collection)
+ if(!only_visible) _insert_in_list(&l, mouseover, TRUE);
+ }
+ }
+ else
+ {
+ // column 4,5
+ if(darktable.view_manager->active_images)
+ {
+ // column 5
+ for(GSList *ll = darktable.view_manager->active_images; ll; ll = g_slist_next(ll))
+ {
+ const int id = GPOINTER_TO_INT(ll->data);
+ _insert_in_list(&l, id, only_visible);
+ // be absolutely sure we have the id in the list (in darkroom,
+ // the active image can be out of collection)
+ if(!only_visible) _insert_in_list(&l, id, TRUE);
+ }
+ }
+ else
+ {
+ // column 4
+ // we return the list of the selection
+ l = dt_selection_get_list(darktable.selection, only_visible, ordered);
+ }
+ }
+
+ // let's register the new list as cached
+ cache->image_over_inside_sel = inside_sel;
+ cache->ordered = ordered;
+ cache->image_over = mouseover;
+ GList *ltmp = cache->images;
+ cache->images = l;
+ g_list_free(ltmp);
+ cache->images_nb = g_list_length(cache->images);
+ GSList *sl = cache->active_imgs;
+ cache->active_imgs = g_slist_copy(darktable.view_manager->active_images);
+ g_slist_free(sl);
+ cache->inside_table = dt_ui_thumbtable(darktable.gui->ui)->mouse_inside;
+ cache->ok = TRUE;
+
+ // if needed, we show the list of cached images in terminal
+ if((darktable.unmuted & DT_DEBUG_ACT_ON) == DT_DEBUG_ACT_ON)
+ {
+ gchar *tx = dt_util_dstrcat(NULL, "[images to act on] new cache (%s) : ", only_visible ? "visible" : "all");
+ for(GList *ll = l; ll; ll = g_list_next(ll)) tx = dt_util_dstrcat(tx, "%d ", GPOINTER_TO_INT(ll->data));
+ dt_print(DT_DEBUG_ACT_ON, "%s\n", tx);
+ g_free(tx);
+ }
+
+ return TRUE;
+}
+
+// get the list of images to act on during global changes (libs, accels)
+GList *dt_act_on_get_images(const gboolean only_visible, const gboolean force, const gboolean ordered)
+{
+ // we first update the cache if needed
+ _cache_update(only_visible, force, ordered);
+
+ GList *l = NULL;
+ if(only_visible && darktable.view_manager->act_on_cache_visible.ok)
+ l = g_list_copy((GList *)darktable.view_manager->act_on_cache_visible.images);
+ else if(!only_visible && darktable.view_manager->act_on_cache_all.ok)
+ l = g_list_copy((GList *)darktable.view_manager->act_on_cache_all.images);
+
+ // and we return a copy of the cached list
+ return l;
+}
+
+// get the query to retrieve images to act on. this is useful to speedup actions if they already use sqlite queries
+gchar *dt_act_on_get_query(const gboolean only_visible)
+{
+ /** Here's how it works
+ *
+ * mouse over| x | x | x | | |
+ * mouse inside table| x | x | | | |
+ * mouse inside selection| x | | | | |
+ * active images| ? | ? | x | | x |
+ * | | | | | |
+ * | S | O | O | S | A |
+ * S = selection ; O = mouseover ; A = active images
+ * the mouse can be outside thumbtable in case of filmstrip + mouse in center widget
+ *
+ * if only_visible is FALSE, then it will add also not visible images because of grouping
+ * due to dt_selection_get_list_query limitation, order is always considered as undefined
+ **/
+
+ const int mouseover = dt_control_get_mouse_over_id();
+
+ GList *l = NULL;
+ gboolean inside_sel = FALSE;
+ if(mouseover > 0)
+ {
+ // column 1,2,3
+ if(dt_ui_thumbtable(darktable.gui->ui)->mouse_inside)
+ {
+ // column 1,2
+ sqlite3_stmt *stmt;
+ gchar *query = g_strdup_printf("SELECT imgid FROM main.selected_images WHERE imgid =%d", mouseover);
+ DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), query, -1, &stmt, NULL);
+ if(stmt != NULL && sqlite3_step(stmt) == SQLITE_ROW)
+ {
+ inside_sel = TRUE;
+ sqlite3_finalize(stmt);
+ }
+ g_free(query);
+
+ if(inside_sel)
+ {
+ // column 1
+ return dt_selection_get_list_query(darktable.selection, only_visible, FALSE);
+ }
+ else
+ {
+ // column 2
+ _insert_in_list(&l, mouseover, only_visible);
+ }
+ }
+ else
+ {
+ // column 3
+ _insert_in_list(&l, mouseover, only_visible);
+ // be absolutely sure we have the id in the list (in darkroom,
+ // the active image can be out of collection)
+ if(!only_visible) _insert_in_list(&l, mouseover, TRUE);
+ }
+ }
+ else
+ {
+ // column 4,5
+ if(darktable.view_manager->active_images)
+ {
+ // column 5
+ for(GSList *ll = darktable.view_manager->active_images; ll; ll = g_slist_next(ll))
+ {
+ const int id = GPOINTER_TO_INT(ll->data);
+ _insert_in_list(&l, id, only_visible);
+ // be absolutely sure we have the id in the list (in darkroom,
+ // the active image can be out of collection)
+ if(!only_visible) _insert_in_list(&l, id, TRUE);
+ }
+ }
+ else
+ {
+ // column 4
+ return dt_selection_get_list_query(darktable.selection, only_visible, FALSE);
+ }
+ }
+
+ // if we don't return the selection, we return the list of imgid separated by comma
+ // in the form it can be used inside queries
+ gchar *images = NULL;
+ for(; l; l = g_list_next(l))
+ {
+ images = dt_util_dstrcat(images, "%d,", GPOINTER_TO_INT(l->data));
+ }
+ if(images)
+ {
+ // remove trailing comma
+ images[strlen(images) - 1] = '\0';
+ }
+ else
+ images = g_strdup(" ");
+ return images;
+}
+
+// get the main image to act on during global changes (libs, accels)
+int dt_act_on_get_main_image()
+{
+ /** Here's how it works -- same as for list, except we don't care about mouse inside selection or table
+ *
+ * mouse over| x | | |
+ * active images| ? | | x |
+ * | | | |
+ * | O | S | A |
+ * First image of ...
+ * S = selection ; O = mouseover ; A = active images
+ **/
+
+ int ret = -1;
+ const int mouseover = dt_control_get_mouse_over_id();
+
+ if(mouseover > 0)
+ {
+ ret = mouseover;
+ }
+ else
+ {
+ if(darktable.view_manager->active_images)
+ {
+ ret = GPOINTER_TO_INT(darktable.view_manager->active_images->data);
+ }
+ else
+ {
+ sqlite3_stmt *stmt;
+ DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
+ "SELECT s.imgid"
+ " FROM main.selected_images as s, memory.collected_images as c"
+ " WHERE s.imgid=c.imgid"
+ " ORDER BY c.rowid LIMIT 1",
+ -1, &stmt, NULL);
+ if(stmt != NULL && sqlite3_step(stmt) == SQLITE_ROW)
+ {
+ ret = sqlite3_column_int(stmt, 0);
+ }
+ if(stmt) sqlite3_finalize(stmt);
+ }
+ }
+
+ if((darktable.unmuted & DT_DEBUG_ACT_ON) == DT_DEBUG_ACT_ON)
+ dt_print(DT_DEBUG_ACT_ON, "[images to act on] single image : %d\n", ret);
+
+ return ret;
+}
+
+// get only the number of images to act on
+int dt_act_on_get_images_nb(const gboolean only_visible, const gboolean force)
+{
+ // if the cache is valid (whatever the ordering) we return its value
+ if(!force)
+ {
+ dt_act_on_cache_t *cache;
+ if(only_visible)
+ cache = &darktable.view_manager->act_on_cache_visible;
+ else
+ cache = &darktable.view_manager->act_on_cache_all;
+
+ if(_test_cache(cache)) return cache->images_nb;
+ }
+
+
+ // otherwise we update the cache
+ _cache_update(only_visible, force, FALSE);
+
+ // and we return the number of images in cache
+ if(only_visible && darktable.view_manager->act_on_cache_visible.ok)
+ return darktable.view_manager->act_on_cache_visible.images_nb;
+ else if(!only_visible && darktable.view_manager->act_on_cache_all.ok)
+ return darktable.view_manager->act_on_cache_all.images_nb;
+ else
+ return 0;
+}
+
+// reset the cache
+void dt_act_on_reset_cache(const gboolean only_visible)
+{
+ if(only_visible)
+ darktable.view_manager->act_on_cache_visible.ok = FALSE;
+ else
+ darktable.view_manager->act_on_cache_all.ok = FALSE;
+}
\ No newline at end of file
diff --git a/src/common/act_on.h b/src/common/act_on.h
new file mode 100644
index 000000000000..2071fb8abab1
--- /dev/null
+++ b/src/common/act_on.h
@@ -0,0 +1,48 @@
+/*
+ This file is part of darktable,
+ Copyright (C) 2021 darktable developers.
+
+ darktable is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ darktable is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with darktable. If not, see .
+*/
+
+#pragma once
+
+#include
+
+// cache structure
+typedef struct dt_act_on_cache_t
+{
+ GList *images;
+ int images_nb;
+ gboolean ok;
+ int image_over;
+ gboolean inside_table;
+ GSList *active_imgs;
+ gboolean image_over_inside_sel;
+ gboolean ordered;
+} dt_act_on_cache_t;
+
+// get images to act on for globals change (via libs or accels)
+// The list needs to be freed by the caller
+GList *dt_act_on_get_images(const gboolean only_visible, const gboolean force, const gboolean ordered);
+gchar *dt_act_on_get_query(const gboolean only_visible);
+
+// get the main image to act on during global changes (libs, accels)
+int dt_act_on_get_main_image();
+
+// get only the number of images to act on
+int dt_act_on_get_images_nb(const gboolean only_visible, const gboolean force);
+
+// reset the cache
+void dt_act_on_reset_cache(const gboolean only_visible);
\ No newline at end of file
diff --git a/src/common/colorlabels.c b/src/common/colorlabels.c
index 10acbf1f1cfa..95dd0ebf82a1 100644
--- a/src/common/colorlabels.c
+++ b/src/common/colorlabels.c
@@ -251,7 +251,7 @@ static float _action_process_color_label(gpointer target, dt_action_element_t el
if(!isnan(move_size))
{
- GList *imgs = g_list_copy((GList *)dt_view_get_images_to_act_on(FALSE, TRUE, FALSE));
+ GList *imgs = dt_act_on_get_images(FALSE, TRUE, FALSE);
dt_colorlabels_toggle_label_on_list(imgs, element ? element - 1 : 5, TRUE);
// if we are in darkroom we show a message as there might be no other indication
diff --git a/src/common/image.c b/src/common/image.c
index ff597fef6870..881ebd986e32 100644
--- a/src/common/image.c
+++ b/src/common/image.c
@@ -637,7 +637,7 @@ void dt_image_set_location(const int32_t imgid, const dt_image_geoloc_t *geoloc,
{
GList *imgs = NULL;
if(imgid == -1)
- imgs = g_list_copy((GList *)dt_view_get_images_to_act_on(TRUE, TRUE, FALSE));
+ imgs = dt_act_on_get_images(TRUE, TRUE, FALSE);
else
imgs = g_list_prepend(imgs, GINT_TO_POINTER(imgid));
if(group_on) dt_grouping_add_grouped_images(&imgs);
@@ -2485,8 +2485,9 @@ void dt_image_synch_xmp(const int selected)
}
else
{
- const GList *imgs = dt_view_get_images_to_act_on(FALSE, TRUE, FALSE);
+ GList *imgs = dt_act_on_get_images(FALSE, TRUE, FALSE);
dt_image_synch_xmps(imgs);
+ g_list_free(imgs);
}
}
diff --git a/src/common/metadata.c b/src/common/metadata.c
index 1f1ad4e012f5..e8019cb310de 100644
--- a/src/common/metadata.c
+++ b/src/common/metadata.c
@@ -579,7 +579,7 @@ void dt_metadata_set(const int imgid, const char *key, const char *value, const
{
GList *imgs = NULL;
if(imgid == -1)
- imgs = g_list_copy((GList *)dt_view_get_images_to_act_on(TRUE, TRUE, FALSE));
+ imgs = dt_act_on_get_images(TRUE, TRUE, FALSE);
else
imgs = g_list_prepend(imgs, GINT_TO_POINTER(imgid));
if(imgs)
diff --git a/src/common/ratings.c b/src/common/ratings.c
index 9b6b2f857ad6..b537a51b43f4 100644
--- a/src/common/ratings.c
+++ b/src/common/ratings.c
@@ -231,7 +231,7 @@ static float _action_process_rating(gpointer target, dt_action_element_t element
}
}
- GList *imgs = g_list_copy((GList *)dt_view_get_images_to_act_on(FALSE, TRUE, FALSE));
+ GList *imgs = dt_act_on_get_images(FALSE, TRUE, FALSE);
dt_ratings_apply_on_list(imgs, element, TRUE);
// if we are in darkroom we show a message as there might be no other indication
diff --git a/src/common/selection.c b/src/common/selection.c
index 30ac02dc7937..7b33f7677ca5 100644
--- a/src/common/selection.c
+++ b/src/common/selection.c
@@ -42,7 +42,8 @@ const dt_collection_t *dt_selection_get_collection(struct dt_selection_t *select
static void _selection_raise_signal()
{
// discard cached images_to_act_on list
- darktable.view_manager->act_on.ok = FALSE;
+ dt_act_on_reset_cache(TRUE);
+ dt_act_on_reset_cache(FALSE);
DT_DEBUG_CONTROL_SIGNAL_RAISE(darktable.signals, DT_SIGNAL_SELECTION_CHANGED);
}
diff --git a/src/common/styles.c b/src/common/styles.c
index 74a79fed0f24..6de071d39967 100644
--- a/src/common/styles.c
+++ b/src/common/styles.c
@@ -98,8 +98,9 @@ void dt_style_item_free(gpointer data)
static gboolean _apply_style_shortcut_callback(GtkAccelGroup *accel_group, GObject *acceleratable,
guint keyval, GdkModifierType modifier, gpointer data)
{
- const GList *imgs = dt_view_get_images_to_act_on(TRUE, TRUE, FALSE);
+ GList *imgs = dt_act_on_get_images(TRUE, TRUE, FALSE);
dt_styles_apply_to_list(data, imgs, FALSE);
+ g_list_free(imgs);
return TRUE;
}
diff --git a/src/common/tags.c b/src/common/tags.c
index fc056b55a909..75dc7eb25a15 100644
--- a/src/common/tags.c
+++ b/src/common/tags.c
@@ -455,17 +455,17 @@ gboolean dt_tag_attach_images(const guint tagid, const GList *img, const gboolea
gboolean dt_tag_attach(const guint tagid, const gint imgid, const gboolean undo_on, const gboolean group_on)
{
- GList *imgs = NULL;
gboolean res = FALSE;
if(imgid == -1)
{
- imgs = (GList *)dt_view_get_images_to_act_on(!group_on, TRUE, FALSE);
+ GList *imgs = dt_act_on_get_images(!group_on, TRUE, FALSE);
res = dt_tag_attach_images(tagid, imgs, undo_on);
+ g_list_free(imgs);
}
else
{
if(dt_is_tag_attached(tagid, imgid)) return FALSE;
- imgs = g_list_append(imgs, GINT_TO_POINTER(imgid));
+ GList *imgs = g_list_append(NULL, GINT_TO_POINTER(imgid));
res = dt_tag_attach_images(tagid, imgs, undo_on);
g_list_free(imgs);
}
@@ -561,7 +561,7 @@ gboolean dt_tag_detach(const guint tagid, const gint imgid, const gboolean undo_
{
GList *imgs = NULL;
if(imgid == -1)
- imgs = g_list_copy((GList *)dt_view_get_images_to_act_on(!group_on, TRUE, FALSE));
+ imgs = dt_act_on_get_images(!group_on, TRUE, FALSE);
else
imgs = g_list_prepend(imgs, GINT_TO_POINTER(imgid));
if(group_on) dt_grouping_add_grouped_images(&imgs);
diff --git a/src/control/jobs/control_jobs.c b/src/control/jobs/control_jobs.c
index 748bc9d87dee..91d525c0359a 100644
--- a/src/control/jobs/control_jobs.c
+++ b/src/control/jobs/control_jobs.c
@@ -201,7 +201,7 @@ static dt_job_t *dt_control_generic_images_job_create(dt_job_execute_callback ex
}
if(progress_type != PROGRESS_NONE)
dt_control_job_add_progress(job, _(message), progress_type == PROGRESS_CANCELLABLE);
- params->index = g_list_copy((GList *)dt_view_get_images_to_act_on(only_visible, TRUE, FALSE));
+ params->index = dt_act_on_get_images(only_visible, TRUE, FALSE);
dt_control_job_set_params(job, params, dt_control_image_enumerator_cleanup);
@@ -1495,7 +1495,7 @@ static dt_job_t *_control_gpx_apply_job_create(const gchar *filename, int32_t fi
if(filmid != -1)
dt_control_image_enumerator_job_film_init(params, filmid);
else if(!imgs)
- params->index = g_list_copy((GList *)dt_view_get_images_to_act_on(TRUE, TRUE, FALSE));
+ params->index = dt_act_on_get_images(TRUE, TRUE, FALSE);
else
params->index = imgs;
dt_control_gpx_apply_t *data = params->data;
@@ -2035,7 +2035,7 @@ static dt_job_t *dt_control_datetime_job_create(const long int offset, const cha
if(imgs)
params->index = imgs;
else
- params->index = g_list_copy((GList *)dt_view_get_images_to_act_on(TRUE, TRUE, FALSE));
+ params->index = dt_act_on_get_images(TRUE, TRUE, FALSE);
dt_control_datetime_t *data = params->data;
data->offset = offset;
diff --git a/src/dtgtk/thumbtable.c b/src/dtgtk/thumbtable.c
index ea4f691d61e1..6bb200ad2e93 100644
--- a/src/dtgtk/thumbtable.c
+++ b/src/dtgtk/thumbtable.c
@@ -854,7 +854,7 @@ static void _filemanager_zoom(dt_thumbtable_t *table, int oldzoom, int newzoom)
if(!thumb)
{
// otherwise we use the classic retrieving method
- const int id = dt_view_get_image_to_act_on();
+ const int id = dt_act_on_get_main_image();
thumb = _thumbtable_get_thumb(table, id);
if(thumb)
{
@@ -1640,7 +1640,7 @@ static void _event_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointe
dt_thumbtable_t *table = (dt_thumbtable_t *)user_data;
- table->drag_list = g_list_copy((GList *)dt_view_get_images_to_act_on(FALSE, TRUE, TRUE));
+ table->drag_list = dt_act_on_get_images(FALSE, TRUE, TRUE);
#ifdef HAVE_MAP
dt_view_manager_t *vm = darktable.view_manager;
@@ -2181,19 +2181,19 @@ gboolean dt_thumbtable_set_offset_image(dt_thumbtable_t *table, const int imgid,
static gboolean _accel_copy(GtkAccelGroup *accel_group, GObject *acceleratable, const guint keyval,
GdkModifierType modifier, gpointer data)
{
- dt_history_copy(dt_view_get_image_to_act_on());
+ dt_history_copy(dt_act_on_get_main_image());
return TRUE;
}
static gboolean _accel_copy_parts(GtkAccelGroup *accel_group, GObject *acceleratable, const guint keyval,
GdkModifierType modifier, gpointer data)
{
- dt_history_copy_parts(dt_view_get_image_to_act_on());
+ dt_history_copy_parts(dt_act_on_get_main_image());
return TRUE;
}
static gboolean _accel_paste(GtkAccelGroup *accel_group, GObject *acceleratable, const guint keyval,
GdkModifierType modifier, gpointer data)
{
- GList *imgs = g_list_copy((GList *)dt_view_get_images_to_act_on(TRUE, TRUE, FALSE));
+ GList *imgs = dt_act_on_get_images(TRUE, TRUE, FALSE);
dt_dev_undo_start_record(darktable.develop);
@@ -2210,7 +2210,7 @@ static gboolean _accel_paste(GtkAccelGroup *accel_group, GObject *acceleratable,
static gboolean _accel_paste_parts(GtkAccelGroup *accel_group, GObject *acceleratable, const guint keyval,
GdkModifierType modifier, gpointer data)
{
- GList *imgs = g_list_copy((GList *)dt_view_get_images_to_act_on(TRUE, TRUE, FALSE));
+ GList *imgs = dt_act_on_get_images(TRUE, TRUE, FALSE);
dt_dev_undo_start_record(darktable.develop);
@@ -2226,7 +2226,7 @@ static gboolean _accel_paste_parts(GtkAccelGroup *accel_group, GObject *accelera
static gboolean _accel_hist_discard(GtkAccelGroup *accel_group, GObject *acceleratable, const guint keyval,
GdkModifierType modifier, gpointer data)
{
- GList *imgs = g_list_copy((GList *)dt_view_get_images_to_act_on(TRUE, TRUE, FALSE));
+ GList *imgs = dt_act_on_get_images(TRUE, TRUE, FALSE);
const gboolean ret = dt_history_delete_on_list(imgs, TRUE);
if(ret)
dt_collection_update_query(darktable.collection, DT_COLLECTION_CHANGE_RELOAD, DT_COLLECTION_PROP_UNDEF, imgs);
@@ -2239,7 +2239,7 @@ static gboolean _accel_duplicate(GtkAccelGroup *accel_group, GObject *accelerata
{
dt_undo_start_group(darktable.undo, DT_UNDO_DUPLICATE);
- const int32_t sourceid = dt_view_get_image_to_act_on();
+ const int32_t sourceid = dt_act_on_get_main_image();
const int32_t newimgid = dt_image_duplicate(sourceid);
if(newimgid <= 0) return FALSE;
diff --git a/src/libs/copy_history.c b/src/libs/copy_history.c
index ce2ed585c8d6..c9eed3aa15aa 100644
--- a/src/libs/copy_history.c
+++ b/src/libs/copy_history.c
@@ -69,15 +69,14 @@ static void _update(dt_lib_module_t *self)
dt_lib_cancel_postponed_update(self);
dt_lib_copy_history_t *d = (dt_lib_copy_history_t *)self->data;
- const GList *imgs = dt_view_get_images_to_act_on(TRUE, FALSE, FALSE);
- const int act_on_any = imgs != NULL;
- const int act_on_one = g_list_is_singleton(imgs);
+ const int nbimgs = dt_act_on_get_images_nb(TRUE, FALSE);
+ const int act_on_any = (nbimgs > 0);
+ const int act_on_one = (nbimgs == 1);
const int act_on_mult = act_on_any && !act_on_one;
const gboolean can_paste
= darktable.view_manager->copy_paste.copied_imageid > 0
&& (act_on_mult
- || (act_on_one
- && (darktable.view_manager->copy_paste.copied_imageid != dt_view_get_image_to_act_on())));
+ || (act_on_one && (darktable.view_manager->copy_paste.copied_imageid != dt_act_on_get_main_image())));
gtk_widget_set_sensitive(GTK_WIDGET(d->discard_button), act_on_any);
gtk_widget_set_sensitive(GTK_WIDGET(d->compress_button), act_on_any);
@@ -98,7 +97,7 @@ static void write_button_clicked(GtkWidget *widget, dt_lib_module_t *self)
static void load_button_clicked(GtkWidget *widget, dt_lib_module_t *self)
{
- GList *imgs = g_list_copy((GList *)dt_view_get_images_to_act_on(TRUE, TRUE, FALSE));
+ GList *imgs = dt_act_on_get_images(TRUE, TRUE, FALSE);
if(!imgs)
return;
const int act_on_one = g_list_is_singleton(imgs); // list length == 1?
@@ -182,13 +181,12 @@ static void load_button_clicked(GtkWidget *widget, dt_lib_module_t *self)
static void compress_button_clicked(GtkWidget *widget, gpointer user_data)
{
const GtkWidget *win = dt_ui_main_window(darktable.gui->ui);
- const GList *imgs = dt_view_get_images_to_act_on(TRUE, TRUE, FALSE);
+ GList *imgs = dt_act_on_get_images(TRUE, TRUE, FALSE);
if(!imgs) return; // do nothing if no images to be acted on
const int missing = dt_history_compress_on_list(imgs);
- dt_collection_update_query(darktable.collection, DT_COLLECTION_CHANGE_RELOAD, DT_COLLECTION_PROP_UNDEF,
- g_list_copy((GList *)imgs));
+ dt_collection_update_query(darktable.collection, DT_COLLECTION_CHANGE_RELOAD, DT_COLLECTION_PROP_UNDEF, imgs);
dt_control_queue_redraw_center();
if (missing)
{
@@ -211,7 +209,7 @@ static void copy_button_clicked(GtkWidget *widget, gpointer user_data)
{
dt_lib_module_t *self = (dt_lib_module_t *)user_data;
- const int id = dt_view_get_image_to_act_on();
+ const int id = dt_act_on_get_main_image();
if(id > 0 && dt_history_copy(id))
{
@@ -223,7 +221,7 @@ static void copy_parts_button_clicked(GtkWidget *widget, gpointer user_data)
{
dt_lib_module_t *self = (dt_lib_module_t *)user_data;
- const int id = dt_view_get_image_to_act_on();
+ const int id = dt_act_on_get_main_image();
if(id > 0 && dt_history_copy_parts(id))
{
@@ -235,7 +233,8 @@ static void discard_button_clicked(GtkWidget *widget, gpointer user_data)
{
gint res = GTK_RESPONSE_YES;
- const GList *imgs = dt_view_get_images_to_act_on(TRUE, TRUE, FALSE);
+ GList *imgs = dt_act_on_get_images(TRUE, TRUE, FALSE);
+ if(!imgs) return;
if(dt_conf_get_bool("ask_before_discard"))
{
@@ -243,8 +242,6 @@ static void discard_button_clicked(GtkWidget *widget, gpointer user_data)
const int number = g_list_length((GList *)imgs);
- if (number == 0) return;
-
GtkWidget *dialog = gtk_message_dialog_new(
GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
ngettext("do you really want to clear history of %d selected image?",
@@ -261,11 +258,14 @@ static void discard_button_clicked(GtkWidget *widget, gpointer user_data)
if(res == GTK_RESPONSE_YES)
{
dt_history_delete_on_list(imgs, TRUE);
- GList *imgs_copy = g_list_copy((GList *)imgs);
dt_collection_update_query(darktable.collection, DT_COLLECTION_CHANGE_RELOAD, DT_COLLECTION_PROP_UNDEF,
- imgs_copy); // frees imgs_copy
+ imgs); // frees imgs
dt_control_queue_redraw_center();
}
+ else
+ {
+ g_list_free(imgs);
+ }
}
static void paste_button_clicked(GtkWidget *widget, gpointer user_data)
@@ -279,33 +279,31 @@ static void paste_button_clicked(GtkWidget *widget, gpointer user_data)
dt_conf_set_int("plugins/lighttable/copy_history/pastemode", mode);
/* copy history from previously copied image and past onto selection */
- const GList *imgs = dt_view_get_images_to_act_on(TRUE, TRUE, FALSE);
+ GList *imgs = dt_act_on_get_images(TRUE, TRUE, FALSE);
if(dt_history_paste_on_list(imgs, TRUE))
{
- dt_collection_update_query(darktable.collection, DT_COLLECTION_CHANGE_RELOAD, DT_COLLECTION_PROP_UNDEF,
- g_list_copy((GList *)imgs));
+ dt_collection_update_query(darktable.collection, DT_COLLECTION_CHANGE_RELOAD, DT_COLLECTION_PROP_UNDEF, imgs);
+ }
+ else
+ {
+ g_list_free(imgs);
}
}
static void paste_parts_button_clicked(GtkWidget *widget, gpointer user_data)
{
/* copy history from previously copied image and past onto selection */
- const GList *imgs = dt_view_get_images_to_act_on(TRUE, TRUE, FALSE);
-
- // at the time the dialog is started, some signals are sent and this in turn call
- // back dt_view_get_images_to_act_on() which free list and create a new one. So we
- // make a copy because the above imgs will be invalidated.
+ GList *imgs = dt_act_on_get_images(TRUE, TRUE, FALSE);
- GList* imgs_copy = g_list_copy((GList*)imgs);
- if(dt_history_paste_parts_on_list(imgs_copy, TRUE))
+ if(dt_history_paste_parts_on_list(imgs, TRUE))
{
dt_collection_update_query(darktable.collection, DT_COLLECTION_CHANGE_RELOAD, DT_COLLECTION_PROP_UNDEF,
- imgs_copy); // frees imgs_copy
+ imgs); // frees imgs
}
else
{
- g_list_free(imgs_copy);
+ g_list_free(imgs);
}
}
diff --git a/src/libs/export.c b/src/libs/export.c
index a3e4c8030668..b133ad54385a 100644
--- a/src/libs/export.c
+++ b/src/libs/export.c
@@ -179,8 +179,7 @@ static void _update(dt_lib_module_t *self)
dt_lib_cancel_postponed_update(self);
const dt_lib_export_t *d = (dt_lib_export_t *)self->data;
- const GList *imgs = dt_view_get_images_to_act_on(TRUE, FALSE, FALSE);
- const gboolean has_act_on = imgs != NULL;
+ const gboolean has_act_on = (dt_act_on_get_images_nb(TRUE, FALSE) > 0);
const char *format_name = dt_conf_get_string_const(CONFIG_PREFIX "format_name");
const char *storage_name = dt_conf_get_string_const(CONFIG_PREFIX "storage_name");
@@ -364,7 +363,7 @@ static void _export_button_clicked(GtkWidget *widget, dt_lib_export_t *d)
gchar *icc_filename = dt_conf_get_string(CONFIG_PREFIX "iccprofile");
const dt_iop_color_intent_t icc_intent = dt_conf_get_int(CONFIG_PREFIX "iccintent");
- GList *list = g_list_copy((GList *)dt_view_get_images_to_act_on(TRUE, TRUE, TRUE));
+ GList *list = dt_act_on_get_images(TRUE, TRUE, TRUE);
dt_control_export(list, max_width, max_height, format_index, storage_index, high_quality, upscale, export_masks,
style, style_append, icc_type, icc_filename, icc_intent, d->metadata_export);
diff --git a/src/libs/geotagging.c b/src/libs/geotagging.c
index e7e68ee59349..9bd79a27aed9 100644
--- a/src/libs/geotagging.c
+++ b/src/libs/geotagging.c
@@ -1331,7 +1331,7 @@ static GDateTime *_get_image_datetime(dt_lib_module_t *self)
dt_lib_geotagging_t *d = (dt_lib_geotagging_t *)self->data;
GList *selected = dt_collection_get_selected(darktable.collection, 1);
const int selid = selected ? GPOINTER_TO_INT(selected->data) : 0;
- const int imgid = dt_view_get_image_to_act_on();
+ const int imgid = dt_act_on_get_main_image();
GDateTime *datetime = NULL;
if((selid != 0) || ((selid == 0) && (imgid != -1)))
{
diff --git a/src/libs/image.c b/src/libs/image.c
index 78f5ed090ddc..4a0bc7a82f06 100644
--- a/src/libs/image.c
+++ b/src/libs/image.c
@@ -190,14 +190,14 @@ static void _update(dt_lib_module_t *self)
{
dt_lib_cancel_postponed_update(self);
dt_lib_image_t *d = (dt_lib_image_t *)self->data;
- const GList *imgs = dt_view_get_images_to_act_on(FALSE, FALSE, FALSE);
+ const int nbimgs = dt_act_on_get_images_nb(FALSE, FALSE);
- const int act_on_any = imgs != NULL; // list length > 0 ?
- const int act_on_one = g_list_is_singleton(imgs); // list length == 1 ?
- const int act_on_mult = act_on_any && !act_on_one;// list length > 1 ?
+ const gboolean act_on_any = (nbimgs > 0);
+ const gboolean act_on_one = (nbimgs == 1);
+ const gboolean act_on_mult = (nbimgs > 1);
const uint32_t selected_cnt = dt_collection_get_selected_count(darktable.collection);
const gboolean can_paste
- = d->imageid > 0 && (act_on_mult || (act_on_one && (d->imageid != dt_view_get_image_to_act_on())));
+ = d->imageid > 0 && (act_on_mult || (act_on_one && (d->imageid != dt_act_on_get_main_image())));
gtk_widget_set_sensitive(GTK_WIDGET(d->remove_button), act_on_any);
gtk_widget_set_sensitive(GTK_WIDGET(d->delete_button), act_on_any);
@@ -238,7 +238,7 @@ static void _update(dt_lib_module_t *self)
else
{
// exact one image to act on
- const int imgid = dt_view_get_image_to_act_on();
+ const int imgid = dt_act_on_get_main_image();
if(imgid >= 0)
{
dt_image_t *img = dt_image_cache_get(darktable.image_cache, imgid, 'r');
@@ -317,7 +317,7 @@ static void _execute_metadata(dt_lib_module_t *self, const int action)
const gboolean geotag_flag = dt_conf_get_bool("plugins/lighttable/copy_metadata/geotags");
const gboolean dttag_flag = dt_conf_get_bool("plugins/lighttable/copy_metadata/tags");
const int imageid = d->imageid;
- const GList *imgs = dt_view_get_images_to_act_on(FALSE, TRUE, FALSE);
+ GList *imgs = dt_act_on_get_images(FALSE, TRUE, FALSE);
if(imgs)
{
// for all the above actions, we don't use the grpu_on tag, as grouped images have already been added to image
@@ -374,9 +374,13 @@ static void _execute_metadata(dt_lib_module_t *self, const int action)
dt_undo_end_group(darktable.undo);
dt_image_synch_xmps(imgs);
dt_collection_update_query(darktable.collection, DT_COLLECTION_CHANGE_RELOAD, DT_COLLECTION_PROP_METADATA,
- g_list_copy((GList *)imgs));
+ imgs);
dt_control_queue_redraw_center();
}
+ else
+ {
+ g_list_free(imgs);
+ }
}
}
@@ -384,7 +388,7 @@ static void copy_metadata_callback(GtkWidget *widget, dt_lib_module_t *self)
{
dt_lib_image_t *d = (dt_lib_image_t *)self->data;
- d->imageid = dt_view_get_image_to_act_on();
+ d->imageid = dt_act_on_get_main_image();
_update(self);
}
diff --git a/src/libs/metadata.c b/src/libs/metadata.c
index f692b1d936d4..12005202c2c8 100644
--- a/src/libs/metadata.c
+++ b/src/libs/metadata.c
@@ -126,7 +126,7 @@ static void _update(dt_lib_module_t *self)
dt_lib_cancel_postponed_update(self);
dt_lib_metadata_t *d = (dt_lib_metadata_t *)self->data;
- const GList *imgs = dt_view_get_images_to_act_on(FALSE, FALSE, FALSE);
+ GList *imgs = dt_act_on_get_images(FALSE, FALSE, FALSE);
// first we want to make sure the list of images to act on has changed
// this is not the case if mouse hover change but still stay in selection for ex.
@@ -146,10 +146,14 @@ static void _update(dt_lib_module_t *self)
l = g_list_next(l);
ll = g_list_next(ll);
}
- if(!changed) return;
+ if(!changed)
+ {
+ g_list_free(imgs);
+ return;
+ }
}
g_list_free(d->last_act_on);
- d->last_act_on = g_list_copy((GList *)imgs);
+ d->last_act_on = imgs;
GList *metadata[DT_METADATA_NUMBER];
uint32_t metadata_count[DT_METADATA_NUMBER];
@@ -162,7 +166,7 @@ static void _update(dt_lib_module_t *self)
// using dt_metadata_get() is not possible here. we want to do all this in a single pass, everything else
// takes ages.
- gchar *images = dt_view_get_images_to_act_on_query(FALSE);
+ gchar *images = dt_act_on_get_query(FALSE);
const uint32_t imgs_count = g_list_length((GList *)imgs);
if(images)
@@ -235,7 +239,7 @@ static void _write_metadata(dt_lib_module_t *self)
_append_kv(&key_value, dt_metadata_get_key(keyid), metadata[i]);
}
- const GList *imgs = dt_view_get_images_to_act_on(FALSE, TRUE, FALSE);
+ GList *imgs = dt_act_on_get_images(FALSE, TRUE, FALSE);
dt_metadata_set_list(imgs, key_value, TRUE);
for(unsigned int i = 0; i < DT_METADATA_NUMBER; i++)
@@ -248,6 +252,7 @@ static void _write_metadata(dt_lib_module_t *self)
DT_DEBUG_CONTROL_SIGNAL_RAISE(darktable.signals, DT_SIGNAL_METADATA_CHANGED, DT_METADATA_SIGNAL_NEW_VALUE);
dt_image_synch_xmps(imgs);
+ g_list_free(imgs);
_update(self);
}
@@ -352,10 +357,11 @@ void gui_reset(dt_lib_module_t *self)
{
dt_lib_metadata_t *d = (dt_lib_metadata_t *)self->data;
d->editing = FALSE;
- const GList *imgs = dt_view_get_images_to_act_on(FALSE, TRUE, FALSE);
+ GList *imgs = dt_act_on_get_images(FALSE, TRUE, FALSE);
dt_metadata_clear(imgs, TRUE);
DT_DEBUG_CONTROL_SIGNAL_RAISE(darktable.signals, DT_SIGNAL_MOUSE_OVER_IMAGE_CHANGE);
dt_image_synch_xmps(imgs);
+ g_list_free(imgs);
_update(self);
}
@@ -957,13 +963,14 @@ int set_params(dt_lib_module_t *self, const void *params, int size)
if(metadata[i][0] != '\0') _append_kv(&key_value, dt_metadata_get_key(i), metadata[i]);
}
- const GList *imgs = dt_view_get_images_to_act_on(FALSE, TRUE, FALSE);
+ GList *imgs = dt_act_on_get_images(FALSE, TRUE, FALSE);
dt_metadata_set_list(imgs, key_value, TRUE);
g_list_free(key_value);
DT_DEBUG_CONTROL_SIGNAL_RAISE(darktable.signals, DT_SIGNAL_MOUSE_OVER_IMAGE_CHANGE);
dt_image_synch_xmps(imgs);
+ g_list_free(imgs);
// force the ui refresh to update the info from preset
g_list_free(d->last_act_on);
d->last_act_on = NULL;
diff --git a/src/libs/metadata_view.c b/src/libs/metadata_view.c
index 457cd3d5d3cf..881ce2ff5cb2 100644
--- a/src/libs/metadata_view.c
+++ b/src/libs/metadata_view.c
@@ -505,7 +505,7 @@ static void _metadata_view_update_values(dt_lib_module_t *self)
}
else
{
- images = dt_view_get_images_to_act_on_query(FALSE);
+ images = dt_act_on_get_query(FALSE);
sqlite3_stmt *stmt;
gchar *query = g_strdup_printf("SELECT id, COUNT(id) "
"FROM main.images "
@@ -536,7 +536,7 @@ static void _metadata_view_update_values(dt_lib_module_t *self)
if(count > 1)
{
- if(!images) images = dt_view_get_images_to_act_on_query(FALSE);
+ if(!images) images = dt_act_on_get_query(FALSE);
sqlite3_stmt *stmt = NULL;
gchar *query = g_strdup_printf("SELECT COUNT(DISTINCT film_id), "
"2, " //id always different
diff --git a/src/libs/styles.c b/src/libs/styles.c
index 1881970e50d2..6bb5e0cfa3ec 100644
--- a/src/libs/styles.c
+++ b/src/libs/styles.c
@@ -222,12 +222,13 @@ static void _styles_row_activated_callback(GtkTreeView *view, GtkTreePath *path,
gchar *name;
gtk_tree_model_get(model, &iter, DT_STYLES_COL_FULLNAME, &name, -1);
- const GList *list = dt_view_get_images_to_act_on(TRUE, TRUE, FALSE);
+ GList *list = dt_act_on_get_images(TRUE, TRUE, FALSE);
if(name)
{
dt_styles_apply_to_list(name, list, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d->duplicate)));
g_free(name);
}
+ g_list_free(list);
}
// get list of style names from selection
@@ -262,19 +263,21 @@ static void apply_clicked(GtkWidget *w, gpointer user_data)
if(style_names == NULL) return;
- const GList *list = dt_view_get_images_to_act_on(TRUE, TRUE, FALSE);
+ GList *list = dt_act_on_get_images(TRUE, TRUE, FALSE);
if(list) dt_multiple_styles_apply_to_list(style_names, list, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d->duplicate)));
g_list_free_full(style_names, g_free);
+ g_list_free(list);
}
static void create_clicked(GtkWidget *w, gpointer user_data)
{
dt_lib_styles_t *d = (dt_lib_styles_t *)user_data;
- const GList *list = dt_view_get_images_to_act_on(TRUE, TRUE, FALSE);
+ GList *list = dt_act_on_get_images(TRUE, TRUE, FALSE);
dt_styles_create_from_list(list);
+ g_list_free(list);
_gui_styles_update_view(d);
}
@@ -717,8 +720,9 @@ static gboolean entry_activated(GtkEntry *entry, gpointer user_data)
const gchar *name = gtk_entry_get_text(d->entry);
if(name)
{
- const GList *imgs = dt_view_get_images_to_act_on(TRUE, TRUE, FALSE);
+ GList *imgs = dt_act_on_get_images(TRUE, TRUE, FALSE);
dt_styles_apply_to_list(name, imgs, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d->duplicate)));
+ g_list_free(imgs);
}
return FALSE;
@@ -743,8 +747,7 @@ static void _update(dt_lib_module_t *self)
dt_lib_cancel_postponed_update(self);
dt_lib_styles_t *d = (dt_lib_styles_t *)self->data;
- const GList *imgs = dt_view_get_images_to_act_on(TRUE, FALSE, FALSE);
- const gboolean has_act_on = imgs != NULL;
+ const gboolean has_act_on = (dt_act_on_get_images_nb(TRUE, FALSE) > 0);
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(d->tree));
const gint sel_styles_cnt = gtk_tree_selection_count_selected_rows(selection);
diff --git a/src/libs/tagging.c b/src/libs/tagging.c
index e72715aa5570..1298a0ec27cd 100644
--- a/src/libs/tagging.c
+++ b/src/libs/tagging.c
@@ -152,8 +152,7 @@ static void _update_atdetach_buttons(dt_lib_module_t *self)
dt_lib_cancel_postponed_update(self);
dt_lib_tagging_t *d = (dt_lib_tagging_t *)self->data;
- const GList *imgs = dt_view_get_images_to_act_on(FALSE, FALSE, FALSE);
- const gboolean has_act_on = imgs != NULL;
+ const gboolean has_act_on = (dt_act_on_get_images_nb(FALSE, FALSE) > 0);
const gint dict_tags_sel_cnt =
gtk_tree_selection_count_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(d->dictionary_view)));
@@ -983,8 +982,9 @@ int set_params(dt_lib_module_t *self, const void *params, int size)
}
g_strfreev(tokens);
GList *tags_r = dt_tag_get_tags(-1, TRUE);
- const GList *imgs = dt_view_get_images_to_act_on(FALSE, FALSE, FALSE);
+ GList *imgs = dt_act_on_get_images(FALSE, FALSE, FALSE);
dt_tag_set_tags(tags, imgs, TRUE, TRUE, TRUE);
+ g_list_free(imgs);
gboolean change = FALSE;
for(GList *tag = tags; tag; tag = g_list_next(tag))
{
@@ -1073,7 +1073,7 @@ static void _detach_selected_tag(GtkTreeView *view, dt_lib_module_t *self, dt_li
gtk_tree_model_get(model, &iter, DT_LIB_TAGGING_COL_ID, &tagid, -1);
if(tagid <= 0) return;
- const GList *imgs = dt_view_get_images_to_act_on(FALSE, TRUE, FALSE);
+ GList *imgs = dt_act_on_get_images(FALSE, TRUE, FALSE);
if(!imgs) return;
GList *affected_images = dt_tag_get_images_from_list(imgs, tagid);
@@ -1118,6 +1118,7 @@ static void _detach_selected_tag(GtkTreeView *view, dt_lib_module_t *self, dt_li
}
g_list_free(affected_images);
}
+ g_list_free(imgs);
}
static void _attach_button_clicked(GtkButton *button, dt_lib_module_t *self)
@@ -1272,9 +1273,10 @@ static void _new_button_clicked(GtkButton *button, dt_lib_module_t *self)
const gchar *tag = gtk_entry_get_text(d->entry);
if(!tag || tag[0] == '\0') return;
- const GList *imgs = dt_view_get_images_to_act_on(FALSE, TRUE, FALSE);
+ GList *imgs = dt_act_on_get_images(FALSE, TRUE, FALSE);
const gboolean res = dt_tag_attach_string_list(tag, imgs, TRUE);
if(res) dt_image_synch_xmps(imgs);
+ g_list_free(imgs);
/** record last tag used */
g_free(d->last_tag);
@@ -3203,9 +3205,10 @@ static gboolean _lib_tagging_tag_redo(GtkAccelGroup *accel_group, GObject *accel
if(d->last_tag)
{
- const GList *imgs = dt_view_get_images_to_act_on(FALSE, TRUE, FALSE);
+ GList *imgs = dt_act_on_get_images(FALSE, TRUE, FALSE);
const gboolean res = dt_tag_attach_string_list(d->last_tag, imgs, TRUE);
if(res) dt_image_synch_xmps(imgs);
+ g_list_free(imgs);
_init_treeview(self, 0);
_init_treeview(self, 1);
if(res) _raise_signal_tag_changed(self);
@@ -3223,7 +3226,7 @@ static gboolean _lib_tagging_tag_show(GtkAccelGroup *accel_group, GObject *accel
return TRUE; // doesn't work properly with tree treeview
}
- d->floating_tag_imgs = g_list_copy((GList *)dt_view_get_images_to_act_on(FALSE, TRUE, FALSE));
+ d->floating_tag_imgs = dt_act_on_get_images(FALSE, TRUE, FALSE);
gint x, y;
gint px, py, w, h;
GtkWidget *window = dt_ui_main_window(darktable.gui->ui);
diff --git a/src/libs/tools/colorlabels.c b/src/libs/tools/colorlabels.c
index b0725507a494..6e760db5095d 100644
--- a/src/libs/tools/colorlabels.c
+++ b/src/libs/tools/colorlabels.c
@@ -102,10 +102,10 @@ void gui_cleanup(dt_lib_module_t *self)
static void _lib_colorlabels_button_clicked_callback(GtkWidget *w, gpointer user_data)
{
- const GList *imgs = dt_view_get_images_to_act_on(FALSE, TRUE, FALSE);
+ GList *imgs = dt_act_on_get_images(FALSE, TRUE, FALSE);
dt_colorlabels_toggle_label_on_list(imgs, GPOINTER_TO_INT(user_data), TRUE);
dt_collection_update_query(darktable.collection, DT_COLLECTION_CHANGE_RELOAD, DT_COLLECTION_PROP_COLORLABEL,
- g_list_copy((GList *)imgs));
+ imgs);
}
// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
diff --git a/src/libs/tools/ratings.c b/src/libs/tools/ratings.c
index d0a1cf7c8b24..825351aa0bfe 100644
--- a/src/libs/tools/ratings.c
+++ b/src/libs/tools/ratings.c
@@ -191,10 +191,9 @@ static gboolean _lib_ratings_button_press_callback(GtkWidget *widget, GdkEventBu
dt_lib_ratings_t *d = (dt_lib_ratings_t *)self->data;
if(d->current > 0)
{
- const GList *imgs = dt_view_get_images_to_act_on(FALSE, TRUE, FALSE);
+ GList *imgs = dt_act_on_get_images(FALSE, TRUE, FALSE);
dt_ratings_apply_on_list(imgs, d->current, TRUE);
- dt_collection_update_query(darktable.collection, DT_COLLECTION_CHANGE_RELOAD, DT_COLLECTION_PROP_RATING,
- g_list_copy((GList *)imgs));
+ dt_collection_update_query(darktable.collection, DT_COLLECTION_CHANGE_RELOAD, DT_COLLECTION_PROP_RATING, imgs);
dt_control_queue_redraw_center();
}
diff --git a/src/lua/gui.c b/src/lua/gui.c
index 545c2cc6c7ac..0764a7024737 100644
--- a/src/lua/gui.c
+++ b/src/lua/gui.c
@@ -82,12 +82,14 @@ static int _act_on_cb(lua_State *L)
{
lua_newtable(L);
int table_index = 1;
- for(const GList *image = dt_view_get_images_to_act_on(FALSE, TRUE, TRUE); image; image = g_list_next(image))
+ GList *l = dt_act_on_get_images(FALSE, TRUE, TRUE);
+ for(const GList *image = l; image; image = g_list_next(image))
{
luaA_push(L, dt_lua_image_t, &image->data);
lua_seti(L, -2, table_index);
table_index++;
}
+ g_list_free(l);
return 1;
}
diff --git a/src/views/darkroom.c b/src/views/darkroom.c
index 8a55d5f7b833..26a413dcbc53 100644
--- a/src/views/darkroom.c
+++ b/src/views/darkroom.c
@@ -813,7 +813,7 @@ void reset(dt_view_t *self)
int try_enter(dt_view_t *self)
{
- int32_t imgid = dt_view_get_image_to_act_on();
+ int32_t imgid = dt_act_on_get_main_image();
if(imgid < 0)
{
diff --git a/src/views/map.c b/src/views/map.c
index 5108e8948e9f..f994dc55fd59 100644
--- a/src/views/map.c
+++ b/src/views/map.c
@@ -1364,7 +1364,7 @@ static void _view_map_changed_callback_delayed(gpointer user_data)
dt_show_times(&start, "[map] dbscan calculation");
// set the clusters
- const GList *sel_imgs = dt_view_get_images_to_act_on(FALSE, FALSE, FALSE);
+ GList *sel_imgs = dt_act_on_get_images(FALSE, FALSE, FALSE);
int group = -1;
for(i = 0; i< img_count; i++)
{
@@ -1417,6 +1417,7 @@ static void _view_map_changed_callback_delayed(gpointer user_data)
lib->images = g_slist_prepend(lib->images, entry);
}
}
+ g_list_free(sel_imgs);
}
needs_redraw = _view_map_draw_images(self);
diff --git a/src/views/print.c b/src/views/print.c
index 5065b2b722db..4525279ac92b 100644
--- a/src/views/print.c
+++ b/src/views/print.c
@@ -316,7 +316,7 @@ int try_enter(dt_view_t *self)
// now check that there is at least one selected image
- const int imgid = dt_view_get_image_to_act_on();
+ const int imgid = dt_act_on_get_main_image();
if(imgid < 0)
{
diff --git a/src/views/slideshow.c b/src/views/slideshow.c
index d45cfa8c90ab..3d18091d95d1 100644
--- a/src/views/slideshow.c
+++ b/src/views/slideshow.c
@@ -418,7 +418,7 @@ void enter(dt_view_t *self)
}
// if one selected start with it, otherwise start at the current lighttable offset
- const int imgid = dt_view_get_image_to_act_on();
+ const int imgid = dt_act_on_get_main_image();
gint selrank = -1;
if(imgid > 0)
diff --git a/src/views/view.c b/src/views/view.c
index d31dc8442717..1e6eceba6720 100644
--- a/src/views/view.c
+++ b/src/views/view.c
@@ -644,353 +644,6 @@ void dt_view_set_scrollbar(dt_view_t *view, float hpos, float hlower, float hsiz
dt_ui_update_scrollbars(darktable.gui->ui);
}
-static int _images_to_act_on_find_custom(gconstpointer a, gconstpointer b)
-{
- return (GPOINTER_TO_INT(a) != GPOINTER_TO_INT(b));
-}
-static void _images_to_act_on_insert_in_list(GList **list, const int imgid, gboolean only_visible)
-{
- if(only_visible)
- {
- if(!g_list_find_custom(*list, GINT_TO_POINTER(imgid), _images_to_act_on_find_custom))
- *list = g_list_append(*list, GINT_TO_POINTER(imgid));
- return;
- }
-
- const dt_image_t *image = dt_image_cache_get(darktable.image_cache, imgid, 'r');
- if(image)
- {
- const int img_group_id = image->group_id;
- dt_image_cache_read_release(darktable.image_cache, image);
-
- if(!darktable.gui
- || !darktable.gui->grouping
- || darktable.gui->expanded_group_id == img_group_id
- || !dt_selection_get_collection(darktable.selection))
- {
- if(!g_list_find_custom(*list, GINT_TO_POINTER(imgid), _images_to_act_on_find_custom))
- *list = g_list_append(*list, GINT_TO_POINTER(imgid));
- }
- else
- {
- sqlite3_stmt *stmt;
- gchar *query = g_strdup_printf(
- "SELECT id"
- " FROM main.images"
- " WHERE group_id = %d AND id IN (%s)",
- img_group_id, dt_collection_get_query_no_group(dt_selection_get_collection(darktable.selection)));
- DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), query, -1, &stmt, NULL);
- while(sqlite3_step(stmt) == SQLITE_ROW)
- {
- const int imgidg = sqlite3_column_int(stmt, 0);
- if(!g_list_find_custom(*list, GINT_TO_POINTER(imgidg), _images_to_act_on_find_custom))
- *list = g_list_append(*list, GINT_TO_POINTER(imgidg));
- }
- sqlite3_finalize(stmt);
- g_free(query);
- }
- }
-}
-
-// get the list of images to act on during global changes (libs, accels)
-const GList *dt_view_get_images_to_act_on(const gboolean only_visible, const gboolean force,
- const gboolean ordered)
-{
- /** Here's how it works
- *
- * mouse over| x | x | x | | |
- * mouse inside table| x | x | | | |
- * mouse inside selection| x | | | | |
- * active images| ? | ? | x | | x |
- * | | | | | |
- * | S | O | O | S | A |
- * S = selection ; O = mouseover ; A = active images
- * the mouse can be outside thumbtable in case of filmstrip + mouse in center widget
- *
- * if only_visible is FALSE, then it will add also not visible images because of grouping
- * force define if we try to use cache or not
- * if ordered is TRUE, we return the list in the gui order. Otherwise the order is undefined (but quicker)
- **/
-
- const int mouseover = dt_control_get_mouse_over_id();
-
- // if possible, we return the cached list
- if(!force
- && darktable.view_manager->act_on.ok
- && darktable.view_manager->act_on.image_over == mouseover
- && darktable.view_manager->act_on.ordered == ordered
- && darktable.view_manager->act_on.inside_table
- == dt_ui_thumbtable(darktable.gui->ui)->mouse_inside
- && g_slist_length(darktable.view_manager->act_on.active_imgs)
- == g_slist_length(darktable.view_manager->active_images))
- {
- // we test active images if mouse outside table
- gboolean ok = TRUE;
- if(!dt_ui_thumbtable(darktable.gui->ui)->mouse_inside
- && darktable.view_manager->act_on.active_imgs)
- {
- GSList *l1 = darktable.view_manager->act_on.active_imgs;
- GSList *l2 = darktable.view_manager->active_images;
- while(l1 && l2)
- {
- if(GPOINTER_TO_INT(l1->data) != GPOINTER_TO_INT(l2->data))
- {
- ok = FALSE;
- break;
- }
- l2 = g_slist_next(l2);
- l1 = g_slist_next(l1);
- }
- }
- if(ok) return darktable.view_manager->act_on.images;
- }
-
- GList *l = NULL;
- gboolean inside_sel = FALSE;
- if(mouseover > 0)
- {
- // column 1,2,3
- if(dt_ui_thumbtable(darktable.gui->ui)->mouse_inside)
- {
- // column 1,2
- sqlite3_stmt *stmt;
- gchar *query = g_strdup_printf("SELECT imgid FROM main.selected_images WHERE imgid=%d", mouseover);
- DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), query, -1, &stmt, NULL);
- if(stmt != NULL && sqlite3_step(stmt) == SQLITE_ROW)
- {
- inside_sel = TRUE;
- sqlite3_finalize(stmt);
- }
- g_free(query);
-
- if(inside_sel)
- {
- // column 1
-
- // first, we try to return cached list if we were already
- // inside sel and the selection has not changed
- if(!force
- && darktable.view_manager->act_on.ok
- && darktable.view_manager->act_on.image_over_inside_sel
- && darktable.view_manager->act_on.inside_table
- && darktable.view_manager->act_on.ordered == ordered)
- {
- return darktable.view_manager->act_on.images;
- }
-
- // we return the list of the selection
- l = dt_selection_get_list(darktable.selection, only_visible, ordered);
- }
- else
- {
- // column 2
- _images_to_act_on_insert_in_list(&l, mouseover, only_visible);
- }
- }
- else
- {
- // column 3
- _images_to_act_on_insert_in_list(&l, mouseover, only_visible);
- // be absolutely sure we have the id in the list (in darkroom,
- // the active image can be out of collection)
- if(!only_visible) _images_to_act_on_insert_in_list(&l, mouseover, TRUE);
- }
- }
- else
- {
- // column 4,5
- if(darktable.view_manager->active_images)
- {
- // column 5
- for(GSList *ll = darktable.view_manager->active_images; ll; ll = g_slist_next(ll))
- {
- const int id = GPOINTER_TO_INT(ll->data);
- _images_to_act_on_insert_in_list(&l, id, only_visible);
- // be absolutely sure we have the id in the list (in darkroom,
- // the active image can be out of collection)
- if(!only_visible) _images_to_act_on_insert_in_list(&l, id, TRUE);
- }
- }
- else
- {
- // column 4
- // we return the list of the selection
- l = dt_selection_get_list(darktable.selection, only_visible, ordered);
- }
- }
-
- // let's register the new list as cached
- darktable.view_manager->act_on.image_over_inside_sel = inside_sel;
- darktable.view_manager->act_on.ordered = ordered;
- darktable.view_manager->act_on.image_over = mouseover;
- g_list_free(darktable.view_manager->act_on.images);
- darktable.view_manager->act_on.images = l;
- g_slist_free(darktable.view_manager->act_on.active_imgs);
- darktable.view_manager->act_on.active_imgs = g_slist_copy(darktable.view_manager->active_images);
- darktable.view_manager->act_on.inside_table = dt_ui_thumbtable(darktable.gui->ui)->mouse_inside;
- darktable.view_manager->act_on.ok = TRUE;
-
- if((darktable.unmuted & DT_DEBUG_ACT_ON) == DT_DEBUG_ACT_ON)
- {
- gchar *tx = dt_util_dstrcat(NULL, "[images to act on] images list : ");
- for(GList *ll = darktable.view_manager->act_on.images; ll; ll = g_list_next(ll))
- tx = dt_util_dstrcat(tx, "%d ", GPOINTER_TO_INT(ll->data));
- dt_print(DT_DEBUG_ACT_ON, "%s\n", tx);
- g_free(tx);
- }
-
- return darktable.view_manager->act_on.images;
-}
-
-// get the query to retrieve images to act on. this is useful to speedup actions if they already use sqlite queries
-gchar *dt_view_get_images_to_act_on_query(const gboolean only_visible)
-{
- /** Here's how it works
- *
- * mouse over| x | x | x | | |
- * mouse inside table| x | x | | | |
- * mouse inside selection| x | | | | |
- * active images| ? | ? | x | | x |
- * | | | | | |
- * | S | O | O | S | A |
- * S = selection ; O = mouseover ; A = active images
- * the mouse can be outside thumbtable in case of filmstrip + mouse in center widget
- *
- * if only_visible is FALSE, then it will add also not visible images because of grouping
- * due to dt_selection_get_list_query limitation, order is always considered as undefined
- **/
-
- const int mouseover = dt_control_get_mouse_over_id();
-
- GList *l = NULL;
- gboolean inside_sel = FALSE;
- if(mouseover > 0)
- {
- // column 1,2,3
- if(dt_ui_thumbtable(darktable.gui->ui)->mouse_inside)
- {
- // column 1,2
- sqlite3_stmt *stmt;
- gchar *query = g_strdup_printf("SELECT imgid FROM main.selected_images WHERE imgid =%d", mouseover);
- DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), query, -1, &stmt, NULL);
- if(stmt != NULL && sqlite3_step(stmt) == SQLITE_ROW)
- {
- inside_sel = TRUE;
- sqlite3_finalize(stmt);
- }
- g_free(query);
-
- if(inside_sel)
- {
- // column 1
- return dt_selection_get_list_query(darktable.selection, only_visible, FALSE);
- }
- else
- {
- // column 2
- _images_to_act_on_insert_in_list(&l, mouseover, only_visible);
- }
- }
- else
- {
- // column 3
- _images_to_act_on_insert_in_list(&l, mouseover, only_visible);
- // be absolutely sure we have the id in the list (in darkroom,
- // the active image can be out of collection)
- if(!only_visible) _images_to_act_on_insert_in_list(&l, mouseover, TRUE);
- }
- }
- else
- {
- // column 4,5
- if(darktable.view_manager->active_images)
- {
- // column 5
- for(GSList *ll = darktable.view_manager->active_images;
- ll;
- ll = g_slist_next(ll))
- {
- const int id = GPOINTER_TO_INT(ll->data);
- _images_to_act_on_insert_in_list(&l, id, only_visible);
- // be absolutely sure we have the id in the list (in darkroom,
- // the active image can be out of collection)
- if(!only_visible) _images_to_act_on_insert_in_list(&l, id, TRUE);
- }
- }
- else
- {
- // column 4
- return dt_selection_get_list_query(darktable.selection, only_visible, FALSE);
- }
- }
-
- // if we don't return the selection, we return the list of imgid separated by comma
- // in the form it can be used inside queries
- gchar *images = NULL;
- for(; l; l = g_list_next(l))
- {
- images = dt_util_dstrcat(images, "%d,", GPOINTER_TO_INT(l->data));
- }
- if(images)
- {
- // remove trailing comma
- images[strlen(images) - 1] = '\0';
- }
- else
- images = g_strdup(" ");
- return images;
-}
-
-// get the main image to act on during global changes (libs, accels)
-int dt_view_get_image_to_act_on()
-{
- /** Here's how it works -- same as for list, except we don't care about mouse inside selection or table
- *
- * mouse over| x | | |
- * active images| ? | | x |
- * | | | |
- * | O | S | A |
- * First image of ...
- * S = selection ; O = mouseover ; A = active images
- **/
-
- int ret = -1;
- const int mouseover = dt_control_get_mouse_over_id();
-
- if(mouseover > 0)
- {
- ret = mouseover;
- }
- else
- {
- if(darktable.view_manager->active_images)
- {
- ret = GPOINTER_TO_INT(darktable.view_manager->active_images->data);
- }
- else
- {
- sqlite3_stmt *stmt;
- DT_DEBUG_SQLITE3_PREPARE_V2
- (dt_database_get(darktable.db),
- "SELECT s.imgid"
- " FROM main.selected_images as s, memory.collected_images as c"
- " WHERE s.imgid=c.imgid"
- " ORDER BY c.rowid LIMIT 1",
- -1, &stmt, NULL);
- if(stmt != NULL && sqlite3_step(stmt) == SQLITE_ROW)
- {
- ret = sqlite3_column_int(stmt, 0);
- }
- if(stmt) sqlite3_finalize(stmt);
- }
- }
-
- if((darktable.unmuted & DT_DEBUG_ACT_ON) == DT_DEBUG_ACT_ON)
- dt_print(DT_DEBUG_ACT_ON, "[images to act on] single image : %d\n", ret);
-
- return ret;
-}
-
dt_view_surface_value_t dt_view_image_get_surface(int imgid, int width, int height, cairo_surface_t **surface,
const gboolean quality)
{
diff --git a/src/views/view.h b/src/views/view.h
index b2768d887b11..b6cffe1b445f 100644
--- a/src/views/view.h
+++ b/src/views/view.h
@@ -18,6 +18,7 @@
#pragma once
+#include "common/act_on.h"
#include "common/action.h"
#include "common/history.h"
#include "common/image.h"
@@ -160,14 +161,6 @@ typedef enum dt_view_image_over_t
DT_VIEW_END = 10, // placeholder for the end of the list
} dt_view_image_over_t;
-// get images to act on for gloabals change (via libs or accels)
-// no need to free the list - done internally
-const GList *dt_view_get_images_to_act_on(const gboolean only_visible, const gboolean force,
- const gboolean ordered);
-gchar *dt_view_get_images_to_act_on_query(const gboolean only_visible);
-// get the main image to act on during global changes (libs, accels)
-int dt_view_get_image_to_act_on();
-
/** returns an uppercase string of file extension **plus** some flag information **/
char* dt_view_extend_modes_str(const char * name, const gboolean is_hdr, const gboolean is_bw, const gboolean is_bw_flow);
/** expose an image and return a cair0_surface. */
@@ -204,16 +197,9 @@ typedef struct dt_view_manager_t
gboolean prevent_refresh;
} accels_window;
- struct
- {
- GList *images;
- gboolean ok;
- int image_over;
- gboolean inside_table;
- GSList *active_imgs;
- gboolean image_over_inside_sel;
- gboolean ordered;
- } act_on;
+ // cached list of images to act on
+ dt_act_on_cache_t act_on_cache_all;
+ dt_act_on_cache_t act_on_cache_visible;
/* reusable db statements
* TODO: reconsider creating a common/database helper API