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