1 /*
2 This file is part of darktable,
3 Copyright (C) 2019-2020 darktable developers.
4
5 darktable is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 darktable is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with darktable. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "common/history_snapshot.h"
20 #include "common/darktable.h"
21 #include "common/debug.h"
22 #include "common/history.h"
23 #include "control/signal.h"
24
dt_history_snapshot_item_init(void)25 dt_undo_lt_history_t *dt_history_snapshot_item_init(void)
26 {
27 return (dt_undo_lt_history_t *)g_malloc0(sizeof(dt_undo_lt_history_t));
28 }
29
dt_history_snapshot_undo_create(const int32_t imgid,int * snap_id,int * history_end)30 void dt_history_snapshot_undo_create(const int32_t imgid, int *snap_id, int *history_end)
31 {
32 // create history & mask snapshots for imgid, return the snapshot id
33 sqlite3_stmt *stmt;
34 gboolean all_ok = TRUE;
35
36 dt_lock_image(imgid);
37
38 // get current history end
39 *history_end = 0;
40 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
41 "SELECT history_end FROM main.images WHERE id=?1", -1, &stmt, NULL);
42 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
43
44 if (sqlite3_step(stmt) == SQLITE_ROW)
45 *history_end = sqlite3_column_int(stmt, 0);
46 sqlite3_finalize(stmt);
47
48 // get max snapshot
49
50 *snap_id = 0;
51 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
52 "SELECT MAX(id) FROM memory.undo_history WHERE imgid=?1", -1, &stmt, NULL);
53 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
54
55 if (sqlite3_step(stmt) == SQLITE_ROW)
56 *snap_id = sqlite3_column_int(stmt, 0) + 1;
57 sqlite3_finalize(stmt);
58
59 sqlite3_exec(dt_database_get(darktable.db), "BEGIN TRANSACTION", NULL, NULL, NULL);
60
61 if(*history_end == 0)
62 {
63 // insert a dummy undo_histroy to ensure proper snap_id later
64 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
65 "INSERT INTO memory.undo_history"
66 " VALUES (?1, ?2, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)"
67 , -1, &stmt, NULL);
68 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, *snap_id);
69 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid);
70 all_ok = all_ok && (sqlite3_step(stmt) == SQLITE_DONE);
71
72 goto end_create;
73 }
74
75 // copy current state into undo_history
76
77 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
78 "INSERT INTO memory.undo_history"
79 " SELECT ?1, imgid, num, module, operation, op_params, enabled, "
80 " blendop_params, blendop_version, multi_priority, multi_name "
81 " FROM main.history"
82 " WHERE imgid=?2", -1, &stmt, NULL);
83 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, *snap_id);
84 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid);
85 all_ok = all_ok && (sqlite3_step(stmt) == SQLITE_DONE);
86 sqlite3_finalize(stmt);
87
88 // copy current state into undo_masks_history
89
90 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
91 "INSERT INTO memory.undo_masks_history"
92 " SELECT ?1, imgid, num, formid, form, name, version,"
93 " points, points_count, source"
94 " FROM main.masks_history"
95 " WHERE imgid=?2", -1, &stmt, NULL);
96 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, *snap_id);
97 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid);
98 all_ok = all_ok && (sqlite3_step(stmt) == SQLITE_DONE);
99 sqlite3_finalize(stmt);
100
101 // copy the module order
102
103 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
104 "INSERT INTO memory.undo_module_order"
105 " SELECT ?1, imgid, version, iop_list"
106 " FROM main.module_order"
107 " WHERE imgid=?2", -1, &stmt, NULL);
108 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, *snap_id);
109 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid);
110 all_ok = all_ok && (sqlite3_step(stmt) == SQLITE_DONE);
111
112 end_create:
113
114 sqlite3_finalize(stmt);
115
116 if(all_ok)
117 sqlite3_exec(dt_database_get(darktable.db), "COMMIT", NULL, NULL, NULL);
118 else
119 {
120 sqlite3_exec(dt_database_get(darktable.db), "ROLLBACK_TRANSACTION", NULL, NULL, NULL);
121 fprintf(stderr, "[dt_history_snapshot_undo_create] fails to create a snapshot for %d\n", imgid);
122 }
123
124 dt_unlock_image(imgid);
125 }
126
_history_snapshot_undo_restore(const int32_t imgid,const int snap_id,const int history_end)127 static void _history_snapshot_undo_restore(const int32_t imgid, const int snap_id, const int history_end)
128 {
129 // restore the given snapshot for imgid
130 sqlite3_stmt *stmt;
131 gboolean all_ok = TRUE;
132
133 dt_lock_image(imgid);
134
135 sqlite3_exec(dt_database_get(darktable.db), "BEGIN TRANSACTION", NULL, NULL, NULL);
136
137 dt_history_delete_on_image_ext(imgid, FALSE);
138 DT_DEBUG_CONTROL_SIGNAL_RAISE(darktable.signals, DT_SIGNAL_TAG_CHANGED);
139
140 // if no history end it means the image history was discarded, nothing more to restore
141 if(history_end == 0)
142 {
143 goto end_restore;
144 }
145
146 // copy undo_history snapshot back as current history state
147
148 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
149 "INSERT INTO main.history"
150 " SELECT imgid, num, module, operation, op_params, enabled, "
151 " blendop_params, blendop_version, multi_priority, multi_name "
152 " FROM memory.undo_history"
153 " WHERE imgid=?2 AND id=?1", -1, &stmt, NULL);
154 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, snap_id);
155 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid);
156 all_ok &= (sqlite3_step(stmt) == SQLITE_DONE);
157 sqlite3_finalize(stmt);
158
159 // copy undo_masks_history snapshot back as current masks_history state
160
161 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
162 "INSERT INTO main.masks_history"
163 " SELECT imgid, num, formid, form, name, version, "
164 " points, points_count, source"
165 " FROM memory.undo_masks_history"
166 " WHERE imgid=?2 AND id=?1",
167 -1, &stmt, NULL);
168 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, snap_id);
169 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid);
170 all_ok &= (sqlite3_step(stmt) == SQLITE_DONE);
171 sqlite3_finalize(stmt);
172
173 // restore module order
174
175 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
176 "INSERT INTO main.module_order"
177 " SELECT imgid, version, iop_list"
178 " FROM memory.undo_module_order"
179 " WHERE imgid=?2 AND id=?1", -1, &stmt, NULL);
180 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, snap_id);
181 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid);
182 all_ok &= (sqlite3_step(stmt) == SQLITE_DONE);
183 sqlite3_finalize(stmt);
184
185 end_restore:
186
187 // set history end
188
189 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
190 "UPDATE main.images SET history_end=?2 WHERE id=?1", -1, &stmt, NULL);
191 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
192 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, history_end);
193 all_ok &= (sqlite3_step(stmt) == SQLITE_DONE);
194 sqlite3_finalize(stmt);
195
196 if(all_ok)
197 sqlite3_exec(dt_database_get(darktable.db), "COMMIT", NULL, NULL, NULL);
198 else
199 {
200 sqlite3_exec(dt_database_get(darktable.db), "ROLLBACK_TRANSACTION", NULL, NULL, NULL);
201 fprintf(stderr, "[_history_snapshot_undo_restore] fails to restore a snapshot for %d\n", imgid);
202 }
203 dt_unlock_image(imgid);
204
205 dt_history_hash_write_from_history(imgid, DT_HISTORY_HASH_CURRENT);
206 }
207
_clear_undo_snapshot(const int32_t imgid,const int snap_id)208 static void _clear_undo_snapshot(const int32_t imgid, const int snap_id)
209 {
210 sqlite3_stmt *stmt;
211
212 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
213 "DELETE FROM memory.undo_history WHERE id=?1 AND imgid=?2", -1, &stmt, NULL);
214 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, snap_id);
215 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid);
216 sqlite3_step(stmt);
217 sqlite3_finalize(stmt);
218
219 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
220 "DELETE FROM memory.undo_masks_history WHERE id=?1 AND imgid=?2", -1, &stmt, NULL);
221 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, snap_id);
222 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid);
223 sqlite3_step(stmt);
224 sqlite3_finalize(stmt);
225
226 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
227 "DELETE FROM memory.undo_module_order WHERE id=?1 AND imgid=?2", -1, &stmt, NULL);
228 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, snap_id);
229 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid);
230 sqlite3_step(stmt);
231 sqlite3_finalize(stmt);
232 }
233
dt_history_snapshot_undo_lt_history_data_free(gpointer data)234 void dt_history_snapshot_undo_lt_history_data_free(gpointer data)
235 {
236 dt_undo_lt_history_t *hist = (dt_undo_lt_history_t *)data;
237
238 _clear_undo_snapshot(hist->imgid, hist->after);
239
240 // this is the first element in for this image, it corresponds to the initial status, we can safely remove it now
241 if(hist->before == 0)
242 _clear_undo_snapshot(hist->imgid, hist->before);
243
244 g_free(hist);
245 }
246
dt_history_snapshot_undo_pop(gpointer user_data,dt_undo_type_t type,dt_undo_data_t data,dt_undo_action_t action,GList ** imgs)247 void dt_history_snapshot_undo_pop(gpointer user_data, dt_undo_type_t type, dt_undo_data_t data, dt_undo_action_t action, GList **imgs)
248 {
249 if(type == DT_UNDO_LT_HISTORY)
250 {
251 dt_undo_lt_history_t *hist = (dt_undo_lt_history_t *)data;
252
253 if(action == DT_ACTION_UNDO)
254 {
255 _history_snapshot_undo_restore(hist->imgid, hist->before, hist->before_history_end);
256 }
257 else
258 {
259 _history_snapshot_undo_restore(hist->imgid, hist->after, hist->after_history_end);
260 }
261
262 *imgs = g_list_append(*imgs, GINT_TO_POINTER(hist->imgid));
263 }
264 }
265