Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
gui/actions/image.c
Go to the documentation of this file.
1/*
2 This file is part of the Ansel project.
3 Copyright (C) 2023, 2025 Aurélien PIERRE.
4 Copyright (C) 2023 Luca Zulberti.
5
6 Ansel is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 Ansel is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Ansel. If not, see <http://www.gnu.org/licenses/>.
18*/
19#include "gui/actions/menu.h"
21#include "common/selection.h"
22#include "common/act_on.h"
24#include "common/image.h"
25#include "system/macros.h"
26#include "common/grouping.h"
28#include "metadata/ratings.h"
29#include "control/redraw.h"
30#include "common/collection.h"
31
32static gboolean rotate_counterclockwise_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
33{
35 return TRUE;
36}
37
38static gboolean rotate_clockwise_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
39{
41 return TRUE;
42}
43
44static gboolean flip_horizontally_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
45{
47 return TRUE;
48}
49
50static gboolean flip_vertically_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
51{
53 return TRUE;
54}
55
56static gboolean reset_rotation_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
57{
59 return TRUE;
60}
61
64static gboolean group_images_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
65{
66 GList *imgs = NULL;
67 int32_t new_group_id = UNKNOWN_IMAGE;
68
70
71 // The new group leader was "the first image in the selection", which meant the first row of
72 // "SELECT imgid FROM main.selected_images" -- and since imgid IS that table's INTEGER PRIMARY
73 // KEY, an unordered scan hands them back ascending, so the leader was the LOWEST id. The
74 // in-memory selection is not kept sorted (ids are appended as they are picked), so take the
75 // minimum explicitly rather than the first element.
76 for(GList *l = selected; l; l = g_list_next(l))
77 {
78 const int32_t id = GPOINTER_TO_INT(l->data);
79 if(new_group_id == UNKNOWN_IMAGE || id < new_group_id) new_group_id = id;
80 }
81
82 for(GList *l = selected; l; l = g_list_next(l))
83 {
84 const int32_t id = GPOINTER_TO_INT(l->data);
85 dt_grouping_add_to_group(new_group_id, id);
86 imgs = g_list_prepend(imgs, GINT_TO_POINTER(id));
87 }
88 g_list_free(selected); // shallow copy of the selection's own list
89
91 return TRUE;
92}
93
95static gboolean ungroup_images_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
96{
97 GList *imgs = NULL;
98
100 for(GList *l = selected; l; l = g_list_next(l))
101 {
102 const int id = GPOINTER_TO_INT(l->data);
103 const int new_group_id = dt_grouping_remove_from_group(id);
104 if(new_group_id != -1)
105 {
106 // new_group_id == -1 if image to be ungrouped was a single image and no change to any group was made
107 imgs = g_list_prepend(imgs, GINT_TO_POINTER(id));
108 }
109 }
110 g_list_free(selected); // shallow copy of the selection's own list
111
112 if(!IS_NULL_PTR(imgs))
113 {
115 g_list_reverse(imgs));
117 }
118 return TRUE;
119}
120
121/* Those operations are dangerous, don't allow them in darkroom aka outside of selection */
122
123static gboolean _colorlabels_callback(int color)
124{
125 GList *imgs = dt_act_on_get_images();
127 g_list_free(imgs);
128 return TRUE;
129}
130
131static gboolean _rating_callback(int value)
132{
133 GList *imgs = dt_act_on_get_images();
135 g_list_free(imgs);
136 return TRUE;
137}
138
139static gboolean red_label_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
140{
142 return TRUE;
143}
144
145static gboolean yellow_label_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
146{
148 return TRUE;
149}
150
151static gboolean green_label_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
152{
154 return TRUE;
155}
156
157static gboolean blue_label_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
158{
160 return TRUE;
161}
162
163static gboolean magenta_label_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
164{
166 return TRUE;
167}
168
169static gboolean reset_label_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
170{
172 return TRUE;
173}
174
175static gboolean rating_one_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
176{
178 return TRUE;
179}
180
181static gboolean rating_two_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
182{
184 return TRUE;
185}
186
187static gboolean rating_three_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
188{
190 return TRUE;
191}
192
193static gboolean rating_four_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
194{
196 return TRUE;
197}
198
199static gboolean rating_five_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
200{
202 return TRUE;
203}
204
205static gboolean rating_reset_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
206{
208 return TRUE;
209}
210
211static gboolean rating_reject_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
212{
214 return TRUE;
215}
216
217/* Orientation has a module in darkroom, don't support it there */
219{
220 return has_active_images() && _is_lighttable();
221}
222
224
225void append_image(GtkWidget **menus, GList **lists, const dt_menus_t index)
226{
227 /* Orientation: quarter turns and mirrors, all writing the same EXIF orientation */
228 add_top_submenu_entry(menus, lists, _("Rotate and flip"), index);
229 GtkWidget *parent = get_last_widget(lists);
230
231 add_sub_sub_menu_entry(menus, parent, lists, _("90\302\260 counter-clockwise"), index, NULL,
233
234 add_sub_sub_menu_entry(menus, parent, lists, _("90\302\260 clockwise"), index, NULL,
236
238
239 add_sub_sub_menu_entry(menus, parent, lists, _("Flip horizontally"), index, NULL,
241
242 add_sub_sub_menu_entry(menus, parent, lists, _("Flip vertically"), index, NULL,
244
246
247 /* Resets the mirrors as well as the rotation: it restores ORIENTATION_NULL, which hands the
248 image back to whatever its EXIF says. Hence "orientation" rather than "rotation". */
249 add_sub_sub_menu_entry(menus, parent, lists, _("Reset orientation"), index, NULL,
251
252 /* Color labels */
253 add_top_submenu_entry(menus, lists, _("Color labels"), index);
254 parent = get_last_widget(lists);
255
256 add_sub_sub_menu_entry(menus, parent, lists, _("<span foreground='#BB2222'>\342\254\244</span> Red"), index, NULL,
257 red_label_callback, NULL, NULL, has_active_images, GDK_KEY_F1, 0);
258
259 add_sub_sub_menu_entry(menus, parent, lists, _("<span foreground='#BBBB22'>\342\254\244</span> Yellow"), index, NULL,
260 yellow_label_callback, NULL, NULL, has_active_images, GDK_KEY_F2, 0);
261
262 add_sub_sub_menu_entry(menus, parent, lists, _("<span foreground='#22BB22'>\342\254\244</span> Green"), index, NULL,
263 green_label_callback, NULL, NULL, has_active_images, GDK_KEY_F3, 0);
264
265 add_sub_sub_menu_entry(menus, parent, lists, _("<span foreground='#2222BB'>\342\254\244</span> Blue"), index, NULL,
266 blue_label_callback, NULL, NULL, has_active_images, GDK_KEY_F4, 0);
267
268 add_sub_sub_menu_entry(menus, parent, lists, _("<span foreground='#BB22BB'>\342\254\244</span> Purple"), index, NULL,
269 magenta_label_callback, NULL, NULL, has_active_images, GDK_KEY_F5, 0);
270
272
273 add_sub_sub_menu_entry(menus, parent, lists, _("<span foreground='#BBBBBB'>\342\254\244</span> Clear labels"), index, NULL,
274 reset_label_callback, NULL, NULL, has_active_images, GDK_KEY_F6, 0);
275
276 /* Ratings */
277 add_top_submenu_entry(menus, lists, _("Ratings"), index);
278 parent = get_last_widget(lists);
279
280 add_sub_sub_menu_entry(menus, parent, lists, _("Reject"), index, NULL,
281 rating_reject_callback, NULL, NULL, has_active_images, GDK_KEY_r, 0);
282
283 add_sub_sub_menu_entry(menus, parent, lists, _("\342\230\205"), index, NULL,
284 rating_one_callback, NULL, NULL, has_active_images, GDK_KEY_1, 0);
285
286 add_sub_sub_menu_entry(menus, parent, lists, _("\342\230\205\342\230\205"), index, NULL,
287 rating_two_callback, NULL, NULL, has_active_images, GDK_KEY_2, 0);
288
289 add_sub_sub_menu_entry(menus, parent, lists, _("\342\230\205\342\230\205\342\230\205"), index, NULL,
290 rating_three_callback, NULL, NULL, has_active_images, GDK_KEY_3, 0);
291
292 add_sub_sub_menu_entry(menus, parent, lists, _("\342\230\205\342\230\205\342\230\205\342\230\205"), index, NULL,
293 rating_four_callback, NULL, NULL, has_active_images, GDK_KEY_4, 0);
294
295 add_sub_sub_menu_entry(menus, parent, lists, _("\342\230\205\342\230\205\342\230\205\342\230\205\342\230\205"), index, NULL,
296 rating_five_callback, NULL, NULL, has_active_images, GDK_KEY_5, 0);
297
299
300 add_sub_sub_menu_entry(menus, parent, lists, _("Clear rating"), index, NULL,
301 rating_reset_callback, NULL, NULL, has_active_images, GDK_KEY_0, 0);
302
303 add_menu_separator(menus[index]);
304
305 /* Reload EXIF */
306 add_sub_menu_entry(menus, lists, _("Reload EXIF from file"), index, NULL, GET_ACCEL_WRAPPER(dt_control_refresh_exif)
307 , NULL, NULL,
308 has_active_images, 0, 0);
309
310 add_menu_separator(menus[index]);
311
312 /* Group/Ungroup */
313 add_sub_menu_entry(menus, lists, _("Group images"), index, NULL, group_images_callback, NULL, NULL,
315
316 add_sub_menu_entry(menus, lists, _("Ungroup images"), index, NULL, ungroup_images_callback, NULL, NULL,
317 has_active_images, GDK_KEY_g, DT_PRIMARY_MASK | GDK_SHIFT_MASK);
318}
Handle default and user-set shortcuts (accelerators)
#define DT_PRIMARY_MASK
GList * dt_act_on_get_images()
Definition act_on.c:38
#define TRUE
Definition ashift_lsd.c:162
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
void dt_collection_update_query(const dt_collection_t *collection, dt_collection_change_t query_change, dt_collection_properties_t changed_property, GList *list)
Definition collection.c:837
dt_collection_t * dt_collection_get_global(void)
Definition collection.c:138
@ DT_COLLECTION_PROP_GROUPING
Definition collection.h:143
@ DT_COLLECTION_CHANGE_RELOAD
Definition collection.h:164
void dt_colorlabels_toggle_label_on_list(GList *list, const int color, const gboolean undo_on)
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
void dt_control_queue_redraw_center()
Request a redraw of the centre view.
Definition control.c:924
void dt_control_refresh_exif()
void dt_control_flip_images(const dt_image_transform_t transform)
GHashTable * selected
set of checked row labels, mirrored to conf on every change
GdkRGBA color[]
Definition geotagging.c:541
int dt_grouping_remove_from_group(const int32_t image_id)
Definition grouping.c:58
void dt_grouping_add_to_group(const int32_t group_id, const int32_t image_id)
Definition grouping.c:47
static gboolean green_label_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
static gboolean group_images_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
static gboolean rating_four_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
static gboolean rating_reset_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
static gboolean _colorlabels_callback(int color)
static gboolean reset_rotation_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
gboolean _can_be_reoriented()
static gboolean flip_vertically_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
void append_image(GtkWidget **menus, GList **lists, const dt_menus_t index)
static gboolean _rating_callback(int value)
static gboolean blue_label_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
static gboolean rotate_clockwise_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
static gboolean reset_label_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
static gboolean rating_two_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
static gboolean yellow_label_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
static gboolean ungroup_images_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
static gboolean rating_one_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
static gboolean magenta_label_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
static gboolean rating_reject_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
static gboolean rotate_counterclockwise_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
static gboolean flip_horizontally_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
static gboolean red_label_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
static gboolean rating_five_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
static gboolean rating_three_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
#define UNKNOWN_IMAGE
Definition image.h:78
@ DT_IMAGE_TRANSFORM_FLIP_VERTICALLY
mirror top <-> bottom, as displayed
Definition image.h:250
@ DT_IMAGE_TRANSFORM_ROTATE_CW
quarter turn clockwise
Definition image.h:246
@ DT_IMAGE_TRANSFORM_RESET
back to ORIENTATION_NULL, i.e. what EXIF says
Definition image.h:248
@ DT_IMAGE_TRANSFORM_FLIP_HORIZONTALLY
mirror left <-> right, as displayed
Definition image.h:249
@ DT_IMAGE_TRANSFORM_ROTATE_CCW
quarter turn counter-clockwise
Definition image.h:247
#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
void add_sub_sub_menu_entry(GtkWidget **menus, GtkWidget *parent, GList **lists, const gchar *label, const dt_menus_t index, void *data, gboolean(*action_callback)(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data), gboolean(*checked_callback)(GtkWidget *widget), gboolean(*active_callback)(GtkWidget *widget), gboolean(*sensitive_callback)(GtkWidget *widget), guint key_val, GdkModifierType mods)
Definition menu.c:607
gboolean _is_lighttable()
Definition menu.c:664
GtkWidget * get_last_widget(GList **list)
Definition menu.c:641
void add_sub_menu_entry(GtkWidget **menus, GList **lists, const gchar *label, const dt_menus_t index, void *data, gboolean(*action_callback)(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data), gboolean(*checked_callback)(GtkWidget *widget), gboolean(*active_callback)(GtkWidget *widget), gboolean(*sensitive_callback)(GtkWidget *widget), guint key_val, GdkModifierType mods)
Definition menu.c:565
void add_sub_menu_separator(GtkWidget *parent)
Definition menu.c:628
void add_top_submenu_entry(GtkWidget **menus, GList **lists, const gchar *label, const dt_menus_t index)
Definition menu.c:538
void add_menu_separator(GtkWidget *menu)
Definition menu.c:621
gboolean has_active_images()
Definition menu.c:659
#define GET_ACCEL_WRAPPER(cb)
Definition menu.h:239
#define MAKE_ACCEL_WRAPPER(cb)
Definition menu.h:227
dt_menus_t
Definition menu.h:42
void dt_ratings_apply_on_list(GList *img, const int rating, const gboolean undo_on)
Definition ratings.c:196
Asking the interface to repaint itself.
struct dt_selection_t * dt_selection_get_global(void)
Definition selection.c:80
GList * dt_selection_get_list(struct dt_selection_t *selection)
Definition selection.c:185
static const dt_aligned_pixel_simd_t value
Definition simd.h:144