1 | // Aseprite |
2 | // Copyright (C) 2018-2019 Igara Studio S.A. |
3 | // Copyright (C) 2001-2018 David Capello |
4 | // |
5 | // This program is distributed under the terms of |
6 | // the End-User License Agreement for Aseprite. |
7 | |
8 | #ifdef HAVE_CONFIG_H |
9 | #include "config.h" |
10 | #endif |
11 | |
12 | #include "app/transaction.h" |
13 | |
14 | #include "app/cmd_transaction.h" |
15 | #include "app/context_access.h" |
16 | #include "app/doc.h" |
17 | #include "app/doc_undo.h" |
18 | #include "app/modules/palettes.h" |
19 | #include "doc/sprite.h" |
20 | #include "ui/manager.h" |
21 | #include "ui/system.h" |
22 | |
23 | #define TX_TRACE(...) |
24 | |
25 | namespace app { |
26 | |
27 | using namespace doc; |
28 | |
29 | Transaction::Transaction( |
30 | Context* ctx, |
31 | Doc* doc, |
32 | const std::string& label, |
33 | Modification modification) |
34 | : m_ctx(ctx) |
35 | , m_doc(doc) |
36 | , m_undo(nullptr) |
37 | , m_cmds(nullptr) |
38 | , m_changes(Changes::kNone) |
39 | { |
40 | TX_TRACE("TX: Start <%s> (%s)\n" , |
41 | label.c_str(), |
42 | modification == ModifyDocument ? "modifies document" : |
43 | "doesn't modify document" ); |
44 | |
45 | m_doc->add_observer(this); |
46 | m_undo = m_doc->undoHistory(); |
47 | |
48 | m_cmds = new CmdTransaction(label, |
49 | modification == Modification::ModifyDocument, |
50 | m_undo->savedCounter()); |
51 | |
52 | // Here we are executing an empty CmdTransaction, just to save the |
53 | // SpritePosition. Sub-cmds are executed then one by one, in |
54 | // Transaction::execute() |
55 | m_cmds->execute(m_ctx); |
56 | } |
57 | |
58 | Transaction::~Transaction() |
59 | { |
60 | try { |
61 | // If it isn't committed, we have to rollback all changes. |
62 | if (m_cmds) |
63 | rollback(nullptr); |
64 | } |
65 | catch (...) { |
66 | // Just avoid throwing an exception in the dtor (just in case |
67 | // rollback() failed). |
68 | |
69 | // TODO logging error |
70 | } |
71 | |
72 | m_doc->remove_observer(this); |
73 | } |
74 | |
75 | // Used to set the document range after all the transaction is |
76 | // executed and before the commit. This range is stored in |
77 | // CmdTransaction to recover it on Edit > Redo. |
78 | void Transaction::setNewDocRange(const DocRange& range) |
79 | { |
80 | ASSERT(m_cmds); |
81 | m_cmds->setNewDocRange(range); |
82 | } |
83 | |
84 | void Transaction::commit() |
85 | { |
86 | // This assert can fail when we run scripts in batch mode |
87 | //ui::assert_ui_thread(); |
88 | |
89 | ASSERT(m_cmds); |
90 | TX_TRACE("TX: Commit <%s>\n" , m_cmds->label().c_str()); |
91 | |
92 | m_cmds->updateSpritePositionAfter(); |
93 | #ifdef ENABLE_UI |
94 | const SpritePosition sprPos = m_cmds->spritePositionAfterExecute(); |
95 | #endif |
96 | |
97 | m_undo->add(m_cmds); |
98 | m_cmds = nullptr; |
99 | |
100 | // Process changes |
101 | if (int(m_changes) & int(Changes::kSelection)) { |
102 | m_doc->resetTransformation(); |
103 | m_doc->generateMaskBoundaries(); |
104 | } |
105 | |
106 | #ifdef ENABLE_UI |
107 | if (int(m_changes) & int(Changes::kColorChange)) { |
108 | ASSERT(m_doc); |
109 | ASSERT(m_doc->sprite()); |
110 | |
111 | Palette* pal = m_doc->sprite()->palette(sprPos.frame()); |
112 | ASSERT(pal); |
113 | if (pal) |
114 | set_current_palette(pal, false); |
115 | else |
116 | set_current_palette(nullptr, false); |
117 | |
118 | if (m_ctx->isUIAvailable()) |
119 | ui::Manager::getDefault()->invalidate(); |
120 | } |
121 | #endif |
122 | } |
123 | |
124 | void Transaction::rollbackAndStartAgain() |
125 | { |
126 | auto newCmds = m_cmds->moveToEmptyCopy(); |
127 | rollback(newCmds); |
128 | newCmds->execute(m_ctx); |
129 | } |
130 | |
131 | void Transaction::rollback(CmdTransaction* newCmds) |
132 | { |
133 | ASSERT(m_cmds); |
134 | TX_TRACE("TX: Rollback <%s>\n" , m_cmds->label().c_str()); |
135 | |
136 | m_cmds->undo(); |
137 | |
138 | delete m_cmds; |
139 | m_cmds = newCmds; |
140 | } |
141 | |
142 | void Transaction::execute(Cmd* cmd) |
143 | { |
144 | try { |
145 | cmd->execute(m_ctx); |
146 | } |
147 | catch (...) { |
148 | delete cmd; |
149 | throw; |
150 | } |
151 | |
152 | try { |
153 | m_cmds->add(cmd); |
154 | } |
155 | catch (...) { |
156 | cmd->undo(); |
157 | delete cmd; |
158 | throw; |
159 | } |
160 | } |
161 | |
162 | void Transaction::onSelectionChanged(DocEvent& ev) |
163 | { |
164 | m_changes = Changes(int(m_changes) | int(Changes::kSelection)); |
165 | } |
166 | |
167 | void Transaction::onColorSpaceChanged(DocEvent& ev) |
168 | { |
169 | m_changes = Changes(int(m_changes) | int(Changes::kColorChange)); |
170 | } |
171 | |
172 | void Transaction::onPaletteChanged(DocEvent& ev) |
173 | { |
174 | m_changes = Changes(int(m_changes) | int(Changes::kColorChange)); |
175 | } |
176 | |
177 | } // namespace app |
178 | |