Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
selection.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2011-2012 Henrik Andersson.
4 Copyright (C) 2012 James C. McPherson.
5 Copyright (C) 2012 johannes hanika.
6 Copyright (C) 2012 Richard Wonka.
7 Copyright (C) 2012-2014, 2016 Tobias Ellinghaus.
8 Copyright (C) 2013 Jérémy Rosen.
9 Copyright (C) 2013, 2018-2021 Pascal Obry.
10 Copyright (C) 2013 Simon Spannagel.
11 Copyright (C) 2014-2016 Roman Lebedev.
12 Copyright (C) 2018 Rick Yorgason.
13 Copyright (C) 2019 Edgardo Hoszowski.
14 Copyright (C) 2019 Rikard Öxler.
15 Copyright (C) 2020-2021 Aldric Renaudin.
16 Copyright (C) 2020 Hubert Kowalski.
17 Copyright (C) 2020, 2022 Philippe Weyland.
18 Copyright (C) 2021 Ralf Brown.
19 Copyright (C) 2021 solarer.
20 Copyright (C) 2022-2023, 2025-2026 Aurélien PIERRE.
21 Copyright (C) 2022 Martin Bařinka.
22 Copyright (C) 2023 Luca Zulberti.
23
24 darktable is free software: you can redistribute it and/or modify
25 it under the terms of the GNU General Public License as published by
26 the Free Software Foundation, either version 3 of the License, or
27 (at your option) any later version.
28
29 darktable is distributed in the hope that it will be useful,
30 but WITHOUT ANY WARRANTY; without even the implied warranty of
31 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 GNU General Public License for more details.
33
34 You should have received a copy of the GNU General Public License
35 along with darktable. If not, see <http://www.gnu.org/licenses/>.
36*/
37
38#include "common/utility.h"
40#include "common/collection.h"
41#include "common/selection.h"
42#include "system/macros.h"
43#include "system/mem_alloc.h"
44#include "common/image.h"
45#include "control/signal.h"
46
47
48typedef struct dt_selection_t
49{
50 /* length of selection. 0 means no selection, -1 means it needs to be updated */
51 uint32_t length;
52
53 /* this stores the last single clicked image id indicating
54 the start of a selection range */
56
57 /* GList of ids of all images in selection */
58 GList *ids;
59
60 /* TRUE while a selection is parked in memory.selected_backup by dt_selection_push(),
61 waiting for the matching dt_selection_pop(). This lived on dt_gui_gtk_t, which is why a
62 layer-1 module reached for dt_gui_get_global() to read its own state -- it is not GUI
63 state, and no GUI code ever touched it. */
64 gboolean stacked;
66
67
68// Signal the GUI that selection got changed and trigger a selected images counter update
69
70/* The application-wide selection.
71 *
72 * Owned HERE, not by darktable_t, and created by the GUI bootstrap (dt_gui_gtk_init()):
73 * a selection is a lighttable concept, and ansel-cli has none -- instantiating it in
74 * dt_init() made every CLI run reload the user's selection from the database for nothing.
75 * In a GUI-less process the accessor returns NULL, and every public entry point below
76 * treats that as "the module is not up" and degrades to a no-op / neutral result, so the
77 * code paths shared with the CLI (tag attach, image removal) need no if(darktable.gui). */
79
84
89
95
101
102
104{
105 if(IS_NULL_PTR(selection)) return -1;
106 return selection->last_single_id;
107}
108
109
110static void _reset_ids_list(dt_selection_t *selection)
111{
112 g_list_free(g_steal_pointer(&selection->ids));
113 selection->ids = NULL;
114 selection->length = 0;
115 selection->last_single_id = -1;
116}
117
118static void _update_last_ids(dt_selection_t *selection)
119{
120 GList *last = g_list_last(selection->ids);
121 if(last)
122 selection->last_single_id = GPOINTER_TO_INT(last->data);
123 else
124 selection->last_single_id = -1;
125}
126
127// Drop selected imgids that are not in the current collection
128// WARNING: that doesn't take care of visible/unvisible image group members in GUI
133
134// Unroll DB imgids to GList
136{
137 // Don't reverse the GList: the query orders SQL rows descending and the repository
138 // prepends, so what comes back is already ascending.
140}
141
143{
144 if(IS_NULL_PTR(selection)) return;
145 _reset_ids_list(selection);
146 selection->ids = _selection_database_to_glist(selection);
147 selection->length = g_list_length(selection->ids);
148 _update_last_ids(selection);
149}
150
151/* On collection change events, ensure the selection is only a subset of the current collection,
152 * aka it doesn't contain dangling imgids that can't be found in current collection
153 */
154static void _selection_update_collection(gpointer instance, dt_collection_change_t query_change,
155 dt_collection_properties_t changed_property, gpointer imgs, uint32_t next,
156 dt_selection_t *selection)
157{
158 _clean_missing_ids(selection);
160 _update_gui();
161}
162
163
164static void _remove_id_link(dt_selection_t *selection, int32_t imgid)
165{
166 GList *link = g_list_find(selection->ids, GINT_TO_POINTER(imgid));
167 if(link)
168 {
169 selection->ids = g_list_delete_link(selection->ids, link);
170 --selection->length;
171 }
172 _update_last_ids(selection);
173}
174
175static void _add_id_link(dt_selection_t *selection, int32_t imgid)
176{
177 if(!g_list_find(selection->ids, GINT_TO_POINTER(imgid)))
178 {
179 selection->ids = g_list_append(selection->ids, GINT_TO_POINTER(imgid));
180 ++selection->length;
181 }
182 selection->last_single_id = imgid;
183}
184
185GList *dt_selection_get_list(struct dt_selection_t *selection)
186{
187 if(IS_NULL_PTR(selection)) return NULL;
188 if(IS_NULL_PTR(selection->ids)) return NULL;
189
190 return g_list_copy(selection->ids);
191}
192
194{
195 if(IS_NULL_PTR(selection) || !selection->ids) return 0;
196
197 return selection->length;
198}
199
200static void _selection_select(dt_selection_t *selection, int32_t imgid)
201{
202 if(imgid < 0) return;
203
205}
206
207static void _selection_deselect(dt_selection_t *selection, int32_t imgid)
208{
209 if(imgid < 0) return;
210
212}
213
215{
216 if(IS_NULL_PTR(selection)) return;
217 // Backup current selection
218 if(!selection->stacked)
219 {
221 selection->stacked = TRUE;
222
223 // Commit from DB to GList of imgids
225 }
226
227 _update_gui();
228}
229
231{
232 if(IS_NULL_PTR(selection)) return;
233 // Restore current selection
234 if(selection->stacked)
235 {
237 selection->stacked = FALSE;
238
239 // Commit from DB to GList of imgids
241 }
242
243 _update_gui();
244}
245
247{
248 dt_selection_t *selection = g_malloc0(sizeof(dt_selection_t));
249
250 /* populate our local cache */
252
253 /* setup signal handler for collection update to sanitize selection imgids */
255 G_CALLBACK(_selection_update_collection), (gpointer)selection);
256
257 return selection;
258}
259
261{
262 if(IS_NULL_PTR(selection)) return;
264 (gpointer)selection);
265 g_list_free(selection->ids);
266 selection->ids = NULL;
268 dt_free(selection);
269}
270
272{
273 if(IS_NULL_PTR(selection)) return;
275 _reset_ids_list(selection);
276 _update_gui();
277}
278
279void dt_selection_select(dt_selection_t *selection, int32_t imgid)
280{
281 if(IS_NULL_PTR(selection)) return;
282 if(imgid == UNKNOWN_IMAGE) return;
283 _selection_select(selection, imgid);
284 _add_id_link(selection, imgid);
285 _update_gui();
286}
287
288void dt_selection_deselect(dt_selection_t *selection, int32_t imgid)
289{
290 if(IS_NULL_PTR(selection)) return;
291 if(imgid == UNKNOWN_IMAGE) return;
292 _selection_deselect(selection, imgid);
293 _remove_id_link(selection, imgid);
294 _update_gui();
295}
296
297void dt_selection_select_single(dt_selection_t *selection, int32_t imgid)
298{
299 if(IS_NULL_PTR(selection)) return;
300 if(imgid == UNKNOWN_IMAGE) return;
301 dt_selection_clear(selection);
302 dt_selection_select(selection, imgid);
303}
304
305void dt_selection_toggle(dt_selection_t *selection, int32_t imgid)
306{
307 if(IS_NULL_PTR(selection)) return;
308 if(imgid == UNKNOWN_IMAGE) return;
309
310 if(g_list_find(selection->ids, GINT_TO_POINTER(imgid)))
311 dt_selection_deselect(selection, imgid);
312 else
313 dt_selection_select(selection, imgid);
314}
315
316static int32_t _list_iterate(struct dt_selection_t *selection, GList **list, int *count, const gboolean add)
317{
318 *count += 1;
319 int32_t imgid = GPOINTER_TO_INT((*list)->data);
320
321 if(add)
322 _add_id_link(selection, imgid);
323 else
324 _remove_id_link(selection, imgid);
325
326 *list = g_list_next(*list);
327 return imgid;
328}
329
330void dt_selection_select_list(struct dt_selection_t *selection, const GList *const l)
331{
332 if(IS_NULL_PTR(selection)) return;
333 if(IS_NULL_PTR(l)) return;
334 GList *list = (GList *)l;
335
336 // Send SQL queries by batches of 400 imgids for performance
337 while(list)
338 {
339 int count = 0;
340 gchar *ids = g_strdup("");
341 while(list && count < 400)
342 {
343 int32_t imgid = _list_iterate(selection, &list, &count, TRUE);
344 ids = dt_util_dstrcat(ids, (ids[0] != '\0') ? ", (%i)" : "(%i)", imgid);
345 }
347 // its sibling below has always freed this; this one never did
348 dt_free(ids);
349 }
350
351 _update_gui();
352}
353
354void dt_selection_deselect_list(struct dt_selection_t *selection, const GList *const l)
355{
356 if(IS_NULL_PTR(selection)) return;
357 if(IS_NULL_PTR(l)) return;
358 GList *list = (GList *)l;
359
360 // Send SQL queries by batches of 400 imgids for performance
361 while(list)
362 {
363 int count = 0;
364 gchar *ids = g_strdup("");
365 while(list && count < 400)
366 {
367 int32_t imgid = _list_iterate(selection, &list, &count, FALSE);
368 ids = dt_util_dstrcat(ids, (ids[0] != '\0') ? ", %i" : "%i", imgid);
369 }
371 dt_free(ids);
372 }
373
374 _update_gui();
375}
376
378{
379 if(IS_NULL_PTR(selection)) return NULL;
380 // There is no selection even after init, abort
381 if(IS_NULL_PTR(selection->ids)) return NULL;
382
383 gchar **ids = g_malloc0_n(selection->length + 1, 9 * sizeof(char *));
384 uint32_t i = 0;
385
386 // Build the array of uint32_tegers as charaters
387 for(GList *id = g_list_first(selection->ids); id; id = g_list_next(id))
388 {
389 ids[i] = g_strdup_printf("%i", GPOINTER_TO_INT(id->data));
390 i++;
391 }
392
393 // ids needs to be null-terminated for strjoinv
394 ids[i] = NULL;
395
396 // Concatenate with blank comas within
397 gchar *result = g_strjoinv(",", ids);
398
399 g_strfreev(ids);
400
401 return result;
402}
403
404gboolean dt_selection_is_id_selected(struct dt_selection_t *selection, int32_t imgid)
405{
406 if(IS_NULL_PTR(selection)) return FALSE;
407 if(IS_NULL_PTR(selection) || !selection->ids) return FALSE;
408 return (g_list_find(selection->ids, GINT_TO_POINTER(imgid)) != NULL);
409}
410
411// clang-format off
412// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
413// vim: shiftwidth=2 expandtab tabstop=2 cindent
414// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
415// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
dt_collection_t * dt_collection_get_global(void)
Definition collection.c:138
void dt_collection_hint_message(const dt_collection_t *collection)
Definition collection.c:912
dt_collection_properties_t
Definition collection.h:120
dt_collection_change_t
Definition collection.h:160
#define UNKNOWN_IMAGE
Definition image.h:78
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
static GList * _selection_database_to_glist(dt_selection_t *selection)
Definition selection.c:135
int32_t dt_selection_get_first_id(struct dt_selection_t *selection)
Definition selection.c:103
void dt_selection_deselect_list(struct dt_selection_t *selection, const GList *const l)
Definition selection.c:354
void dt_selection_cleanup_global(void)
Definition selection.c:90
struct dt_selection_t * dt_selection_get_global(void)
Definition selection.c:80
void dt_selection_reload_from_database_real(dt_selection_t *selection)
Definition selection.c:142
void dt_selection_push(dt_selection_t *selection)
Definition selection.c:214
static void _update_gui()
Definition selection.c:96
gchar * dt_selection_ids_to_string(struct dt_selection_t *selection)
Definition selection.c:377
static void _remove_id_link(dt_selection_t *selection, int32_t imgid)
Definition selection.c:164
static void _selection_update_collection(gpointer instance, dt_collection_change_t query_change, dt_collection_properties_t changed_property, gpointer imgs, uint32_t next, dt_selection_t *selection)
Definition selection.c:154
int dt_selection_get_length(struct dt_selection_t *selection)
Definition selection.c:193
static void _update_last_ids(dt_selection_t *selection)
Definition selection.c:118
void dt_selection_init_global(void)
Definition selection.c:85
static void _selection_deselect(dt_selection_t *selection, int32_t imgid)
Definition selection.c:207
void dt_selection_free(dt_selection_t *selection)
Definition selection.c:260
static int32_t _list_iterate(struct dt_selection_t *selection, GList **list, int *count, const gboolean add)
Definition selection.c:316
static void _selection_select(dt_selection_t *selection, int32_t imgid)
Definition selection.c:200
GList * dt_selection_get_list(struct dt_selection_t *selection)
Definition selection.c:185
gboolean dt_selection_is_id_selected(struct dt_selection_t *selection, int32_t imgid)
Definition selection.c:404
dt_selection_t * dt_selection_new()
Definition selection.c:246
void dt_selection_select_list(struct dt_selection_t *selection, const GList *const l)
Definition selection.c:330
static dt_selection_t * _selection_global
Definition selection.c:78
static void _add_id_link(dt_selection_t *selection, int32_t imgid)
Definition selection.c:175
static void _clean_missing_ids(dt_selection_t *selection)
Definition selection.c:129
static void _reset_ids_list(dt_selection_t *selection)
Definition selection.c:110
void dt_selection_clear(dt_selection_t *selection)
Definition selection.c:271
void dt_selection_deselect(dt_selection_t *selection, int32_t imgid)
Definition selection.c:288
void dt_selection_select(dt_selection_t *selection, int32_t imgid)
Definition selection.c:279
void dt_selection_select_single(dt_selection_t *selection, int32_t imgid)
Definition selection.c:297
void dt_selection_pop(dt_selection_t *selection)
Definition selection.c:230
void dt_selection_toggle(dt_selection_t *selection, int32_t imgid)
Definition selection.c:305
#define dt_selection_reload_from_database(selection)
Definition selection.h:123
void dt_selection_repository_select(const int32_t imgid)
Add imgid to the selection. Selecting an already-selected image does nothing.
void dt_selection_repository_clear(void)
Empty the selection.
void dt_selection_repository_cleanup(void)
Finalise whatever this repository still caches – today, nothing. See dt_colorlabel_repository_cleanup...
void dt_selection_repository_pop(void)
Copy memory.selected_backup back over the selection.
void dt_selection_repository_deselect_list(const char *ids)
Remove every id in ids from the selection.
void dt_selection_repository_deselect(const int32_t imgid)
Remove imgid from the selection.
void dt_selection_repository_drop_uncollected(void)
Drop selected images that are no longer in the current collection.
GList * dt_selection_repository_get_all(void)
Every selected image id, GINT_TO_POINTER, ASCENDING. Free with g_list_free().
void dt_selection_repository_push(void)
Copy the selection into memory.selected_backup, replacing what was there.
void dt_selection_repository_select_list(const char *ids)
Add every id in ids to the selection.
main.selected_images, and the memory.selected_backup it is pushed onto.
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
Definition signal.h:407
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:386
struct dt_control_signal_t * dt_control_signal_get_global(void)
Definition darktable.c:616
@ DT_SIGNAL_SELECTION_CHANGED
This signal is raised when the selection is changed no param, no returned value.
Definition signal.h:130
@ DT_SIGNAL_COLLECTION_CHANGED
This signal is raised when collection changed. To avoid leaking the list, dt_collection_t is connecte...
Definition signal.h:125
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:396
uint32_t length
Definition selection.c:51
gboolean stacked
Definition selection.c:64
int32_t last_single_id
Definition selection.c:55
GList * ids
Definition selection.c:58
gchar * dt_util_dstrcat(gchar *str, const gchar *format,...)
Definition utility.c:99