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