Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
ratings.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2011 Henrik Andersson.
4 Copyright (C) 2011, 2013-2014 johannes hanika.
5 Copyright (C) 2012 Richard Wonka.
6 Copyright (C) 2012-2014, 2016 Tobias Ellinghaus.
7 Copyright (C) 2013 José Carlos García Sogo.
8 Copyright (C) 2013 parafin.
9 Copyright (C) 2013, 2019-2021 Pascal Obry.
10 Copyright (C) 2014, 2016 Roman Lebedev.
11 Copyright (C) 2016 itinerarium.
12 Copyright (C) 2018 Rick Yorgason.
13 Copyright (C) 2019-2020 Heiko Bauke.
14 Copyright (C) 2019-2020 Philippe Weyland.
15 Copyright (C) 2020-2021 Aldric Renaudin.
16 Copyright (C) 2020 Chris Elston.
17 Copyright (C) 2021 Diederik Ter Rahe.
18 Copyright (C) 2021 Hubert Kowalski.
19 Copyright (C) 2021 Ralf Brown.
20 Copyright (C) 2022 Martin Bařinka.
21 Copyright (C) 2022 solarer.
22 Copyright (C) 2023, 2025 Aurélien PIERRE.
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#include "common/collection.h"
38#include "common/glib_utils.h"
39#include "caches/image_cache.h"
40#include "metadata/ratings.h"
41#include "metadata/notify.h"
42#include "common/undo.h"
43#include "common/grouping.h"
44
45
46#define DT_RATINGS_UPGRADE -1
47#define DT_RATINGS_DOWNGRADE -2
48#define DT_RATINGS_REJECT -3
49#define DT_RATINGS_UNREJECT -4
50
51typedef struct dt_undo_ratings_t
52{
53 int32_t imgid;
54 int before;
55 int after;
57
58char *dt_ratings_get_name(const int rating)
59{
60 switch(rating)
61 {
62 case 0:
63 return _("empty");
64 case 1:
65 return _("1 star");
66 case 2:
67 return _("2 stars");
68 case 3:
69 return _("3 stars");
70 case 4:
71 return _("4 stars");
72 case 5:
73 return _("5 stars");
74 case 6:
75 return _("rejected");
76 default:
77 return _("unknown/invalid");
78 }
79}
80
81int dt_ratings_get(const int32_t imgid)
82{
83 int stars = 0;
84 dt_image_t *image = dt_image_cache_get(imgid, 'r');
85 if(image)
86 {
87 if(image->flags & DT_IMAGE_REJECTED)
88 stars = DT_VIEW_REJECT;
89 else
90 stars = DT_VIEW_RATINGS_MASK & image->flags;
92 }
93 return stars;
94}
95
96static void _ratings_apply_to_image(const int32_t imgid, const int rating)
97{
98 int new_rating = rating;
99 dt_image_t *image = dt_image_cache_get(imgid, 'w');
100
101 if(image)
102 {
103 // apply or remove rejection
104 if(new_rating == DT_RATINGS_REJECT)
105 image->flags |= DT_IMAGE_REJECTED;
106 else if(new_rating == DT_RATINGS_UNREJECT)
107 image->flags &= ~DT_IMAGE_REJECTED;
108 else
109 {
110 image->flags = (image->flags & ~(DT_IMAGE_REJECTED | DT_VIEW_RATINGS_MASK))
111 | (DT_VIEW_RATINGS_MASK & new_rating);
112 }
113 // synch through:
115 }
116 else
117 {
119 }
120}
121
122static void _pop_undo(gpointer user_data, dt_undo_type_t type, dt_undo_data_t data, dt_undo_action_t action, GList **imgs)
123{
124 if(type == DT_UNDO_RATINGS)
125 {
126 for(GList *list = (GList *)data; list; list = g_list_next(list))
127 {
129 _ratings_apply_to_image(ratings->imgid, (action == DT_ACTION_UNDO) ? ratings->before : ratings->after);
130 *imgs = g_list_prepend(*imgs, GINT_TO_POINTER(ratings->imgid));
131 }
133 }
134}
135
136static void _ratings_undo_data_free(gpointer data)
137{
138 GList *l = (GList *)data;
139 g_list_free(l);
140 l = NULL;
141}
142
143// wrapper that does some precalculation to deal with toggle effects and rating increase/decrease
144static void _ratings_apply(GList *imgs, const int rating, GList **undo, const gboolean undo_on)
145{
146 // REJECTION and SINGLE_STAR rating can have a toggle effect
147 // but we only toggle off if ALL images have that rating
148 // so we need to check every image first
149 gboolean toggle = FALSE;
150
151 if(rating == DT_VIEW_REJECT)
152 {
153 toggle = TRUE;
154 for(const GList *images = g_list_first(imgs); images; images = g_list_next(images))
155 {
156 if(dt_ratings_get(GPOINTER_TO_INT(images->data)) != DT_VIEW_REJECT)
157 {
158 toggle = FALSE;
159 break;
160 }
161 }
162 }
163
164 for(const GList *images = g_list_first(imgs); images; images = g_list_next(images))
165 {
166 const int32_t image_id = GPOINTER_TO_INT(images->data);
167 const int old_rating = dt_ratings_get(image_id);
168 if(undo_on)
169 {
170 dt_undo_ratings_t *undoratings = (dt_undo_ratings_t *)malloc(sizeof(dt_undo_ratings_t));
171 undoratings->imgid = image_id;
172 undoratings->before = old_rating;
173 undoratings->after = rating;
174 *undo = g_list_append(*undo, undoratings);
175 }
176
177 int new_rating = rating;
178 // do not 'DT_RATINGS_UPGRADE' or 'DT_RATINGS_UPGRADE' if image was rejected
179 if(old_rating == DT_VIEW_REJECT && rating < DT_VIEW_DESERT)
180 new_rating = DT_VIEW_REJECT;
181 else if(rating == DT_RATINGS_UPGRADE)
182 new_rating = MIN(DT_VIEW_STAR_5, old_rating + 1);
183 else if(rating == DT_RATINGS_DOWNGRADE)
184 new_rating = MAX(DT_VIEW_DESERT, old_rating - 1);
185 else if(rating == DT_VIEW_STAR_1 && toggle)
186 new_rating = DT_VIEW_DESERT;
187 else if(rating == DT_VIEW_REJECT && toggle)
188 new_rating = DT_RATINGS_UNREJECT;
189 else if(rating == DT_VIEW_REJECT && !toggle)
190 new_rating = DT_RATINGS_REJECT;
191
192 _ratings_apply_to_image(image_id, new_rating);
193 }
194}
195
196void dt_ratings_apply_on_list(GList *img, const int rating, const gboolean undo_on)
197{
198 if(img)
199 {
200 GList *undo = NULL;
202
203 _ratings_apply(img, rating, &undo, undo_on);
204
205 if(undo_on)
206 {
209 }
211 dt_metadata_notify(DT_METADATA_NOTICE_TOAST, _("Rating set to %s for %i image(s)"),
212 dt_ratings_get_name(rating), g_list_length(img));
213 }
214}
215
216void dt_ratings_apply_on_image(const int32_t imgid, const int rating, const gboolean single_star_toggle,
217 const gboolean undo_on, const gboolean group_on)
218{
219 GList *imgs = NULL;
220 int new_rating = rating;
221
222 if(imgid > 0) imgs = g_list_prepend(imgs, GINT_TO_POINTER(imgid));
223
224 if(imgs)
225 {
226 GList *undo = NULL;
228 if(group_on) dt_grouping_add_grouped_images(&imgs);
229
230 if(!g_list_shorter_than(imgs, 2)) // pop up a toast if rating multiple images at once
231 {
232 const guint count = g_list_length(imgs);
233 if(new_rating == DT_VIEW_REJECT)
235 ngettext("rejecting %d image", "rejecting %d images", count), count);
236 else
238 ngettext("applying rating %d to %d image", "applying rating %d to %d images", count),
239 new_rating, count);
240 }
241
242 _ratings_apply(imgs, new_rating, &undo, undo_on);
243
244 if(undo_on)
245 {
248 }
249 g_list_free(imgs);
250 imgs = NULL;
251 }
252 else
253 dt_metadata_notify(DT_METADATA_NOTICE_MESSAGE, _("no images selected to apply rating"));
254}
255
256// clang-format off
257// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
258// vim: shiftwidth=2 expandtab tabstop=2 cindent
259// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
260// 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
const dt_collection_filter_flag_t ratings[7]
Definition filter.c:455
static gboolean g_list_shorter_than(const GList *list, unsigned len)
Definition glib_utils.h:34
void dt_grouping_add_grouped_images(GList **images)
Definition grouping.c:148
@ DT_IMAGE_REJECTED
Definition image.h:113
@ DT_VIEW_REJECT
Definition image.h:355
@ DT_VIEW_STAR_5
Definition image.h:354
@ DT_VIEW_STAR_1
Definition image.h:350
@ DT_VIEW_DESERT
Definition image.h:349
void dt_image_cache_write_release(dt_image_t *img, dt_image_cache_write_mode_t mode)
dt_image_t * dt_image_cache_get(const int32_t imgid, char mode)
void dt_image_cache_read_release(const dt_image_t *img)
@ DT_IMAGE_CACHE_RELAXED
Definition image_cache.h:50
@ DT_IMAGE_CACHE_SAFE
Definition image_cache.h:48
_lib_location_type_t type
Definition location.c:1
void dt_metadata_notify(const dt_metadata_notice_t kind, const char *format,...)
Everything this module says to the outside world: messages for the user, and the fact that the tag vo...
@ DT_METADATA_NOTICE_TOAST
@ DT_METADATA_NOTICE_MESSAGE
#define DT_RATINGS_UPGRADE
Definition ratings.c:46
static void _pop_undo(gpointer user_data, dt_undo_type_t type, dt_undo_data_t data, dt_undo_action_t action, GList **imgs)
Definition ratings.c:122
#define DT_RATINGS_DOWNGRADE
Definition ratings.c:47
int dt_ratings_get(const int32_t imgid)
Definition ratings.c:81
void dt_ratings_apply_on_image(const int32_t imgid, const int rating, const gboolean single_star_toggle, const gboolean undo_on, const gboolean group_on)
Definition ratings.c:216
static void _ratings_apply(GList *imgs, const int rating, GList **undo, const gboolean undo_on)
Definition ratings.c:144
static void _ratings_apply_to_image(const int32_t imgid, const int rating)
Definition ratings.c:96
static void _ratings_undo_data_free(gpointer data)
Definition ratings.c:136
#define DT_RATINGS_UNREJECT
Definition ratings.c:49
#define DT_RATINGS_REJECT
Definition ratings.c:48
void dt_ratings_apply_on_list(GList *img, const int rating, const gboolean undo_on)
Definition ratings.c:196
char * dt_ratings_get_name(const int rating)
Definition ratings.c:58
#define DT_VIEW_RATINGS_MASK
Definition ratings.h:42
int32_t flags
Definition image.h:401
int32_t imgid
Definition ratings.c:53
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29
void dt_undo_end_group(dt_undo_t *self)
Definition undo.c:149
void dt_undo_start_group(dt_undo_t *self, dt_undo_type_t type)
Definition undo.c:134
void dt_undo_record(dt_undo_t *self, gpointer user_data, dt_undo_type_t type, dt_undo_data_t data, void(*undo)(gpointer user_data, dt_undo_type_t type, dt_undo_data_t item, dt_undo_action_t action, GList **imgs), void(*free_data)(gpointer data))
Definition undo.c:163
dt_undo_type_t
Definition undo.h:40
@ DT_UNDO_RATINGS
Definition undo.h:45
dt_undo_t * dt_undo_get_global(void)
Definition darktable.c:611
void * dt_undo_data_t
Definition undo.h:70
dt_undo_action_t
Definition undo.h:65
@ DT_ACTION_UNDO
Definition undo.h:66