1// Aseprite
2// Copyright (C) 2019-2020 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/cmd_transaction.h"
13
14#include "app/context.h"
15#include "app/site.h"
16
17#ifdef ENABLE_UI
18#include "app/app.h"
19#include "app/ui/timeline/timeline.h"
20#endif
21
22namespace app {
23
24CmdTransaction::CmdTransaction(const std::string& label,
25 bool changeSavedState,
26 int* savedCounter)
27 : m_ranges(nullptr)
28 , m_label(label)
29 , m_changeSavedState(changeSavedState)
30 , m_savedCounter(savedCounter)
31{
32}
33
34CmdTransaction* CmdTransaction::moveToEmptyCopy()
35{
36 CmdTransaction* copy = new CmdTransaction(m_label,
37 m_changeSavedState,
38 m_savedCounter);
39 copy->m_spritePositionBefore = m_spritePositionBefore;
40 copy->m_spritePositionAfter = m_spritePositionAfter;
41 if (m_ranges) {
42 copy->m_ranges.reset(new Ranges);
43 copy->m_ranges->m_before = std::move(m_ranges->m_before);
44 copy->m_ranges->m_after = std::move(m_ranges->m_after);
45 }
46 return copy;
47}
48
49void CmdTransaction::setNewDocRange(const DocRange& range)
50{
51#ifdef ENABLE_UI
52 if (m_ranges)
53 range.write(m_ranges->m_after);
54#endif
55}
56
57void CmdTransaction::updateSpritePositionAfter()
58{
59 m_spritePositionAfter = calcSpritePosition();
60
61 // We cannot capture m_ranges->m_after from the Timeline here
62 // because the document range in the Timeline is updated after the
63 // commit/command (on Timeline::onAfterCommandExecution).
64 //
65 // So m_ranges->m_after is captured explicitly in
66 // setNewDocRange().
67}
68
69std::istream* CmdTransaction::documentRangeBeforeExecute() const
70{
71 if (m_ranges && m_ranges->m_before.tellp() > 0) {
72 m_ranges->m_before.seekg(0);
73 return &m_ranges->m_before;
74 }
75 else
76 return nullptr;
77}
78
79std::istream* CmdTransaction::documentRangeAfterExecute() const
80{
81 if (m_ranges && m_ranges->m_after.tellp() > 0) {
82 m_ranges->m_after.seekg(0);
83 return &m_ranges->m_after;
84 }
85 else
86 return nullptr;
87}
88
89void CmdTransaction::onExecute()
90{
91 // Save the current site and doc range
92 m_spritePositionBefore = calcSpritePosition();
93#ifdef ENABLE_UI
94 if (isDocRangeEnabled()) {
95 m_ranges.reset(new Ranges);
96 calcDocRange().write(m_ranges->m_before);
97 }
98#endif
99
100 // Execute the sequence of "cmds"
101 CmdSequence::onExecute();
102
103 if (m_changeSavedState)
104 ++(*m_savedCounter);
105}
106
107void CmdTransaction::onUndo()
108{
109 CmdSequence::onUndo();
110
111 if (m_changeSavedState)
112 --(*m_savedCounter);
113}
114
115void CmdTransaction::onRedo()
116{
117 CmdSequence::onRedo();
118
119 if (m_changeSavedState)
120 ++(*m_savedCounter);
121}
122
123std::string CmdTransaction::onLabel() const
124{
125 return m_label;
126}
127
128size_t CmdTransaction::onMemSize() const
129{
130 size_t size = CmdSequence::onMemSize();
131 if (m_ranges) {
132 size += (m_ranges->m_before.tellp() +
133 m_ranges->m_after.tellp());
134 }
135 return size;
136}
137
138SpritePosition CmdTransaction::calcSpritePosition() const
139{
140 Site site = context()->activeSite();
141 return SpritePosition(site.layer(), site.frame());
142}
143
144bool CmdTransaction::isDocRangeEnabled() const
145{
146#ifdef ENABLE_UI
147 if (App::instance()) {
148 Timeline* timeline = App::instance()->timeline();
149 if (timeline && timeline->range().enabled())
150 return true;
151 }
152#endif
153 return false;
154}
155
156DocRange CmdTransaction::calcDocRange() const
157{
158#ifdef ENABLE_UI
159 // TODO We cannot use Context::activeSite() because it losts
160 // important information about the DocRange() (type and
161 // flags).
162 if (App::instance()) {
163 Timeline* timeline = App::instance()->timeline();
164 if (timeline)
165 return timeline->range();
166 }
167#endif
168 return DocRange();
169}
170
171} // namespace app
172