1// Aseprite
2// Copyright (C) 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#ifndef APP_TRANSACTION_H_INCLUDED
9#define APP_TRANSACTION_H_INCLUDED
10#pragma once
11
12#include "app/cmd_transaction.h"
13#include "app/doc_observer.h"
14
15#include <string>
16
17namespace app {
18
19 class Cmd;
20 class Context;
21 class DocRange;
22 class DocUndo;
23
24 enum Modification {
25 ModifyDocument, // This item changes the "saved status" of the document.
26 DoesntModifyDocument // This item doesn't modify the document.
27 };
28
29 // High-level class to group a set of commands to modify the
30 // document atomically, with enough information to rollback the
31 // whole operation if something fails (e.g. an exceptions is thrown)
32 // in the middle of the procedure.
33 //
34 // This class is a DocObserver because it listen and accumulates the
35 // changes in the Doc (m_changes), and when the transaction ends, it
36 // processes those changes as UI updates (so widgets are
37 // invalidated/updated correctly to show the new Doc state).
38 //
39 // You have to wrap every call to an transaction with a
40 // ContextWriter. The preferred usage is as follows:
41 //
42 // {
43 // ContextWriter writer(context);
44 // Transaction transaction(context, "My big operation");
45 // ...
46 // transaction.commit();
47 // }
48 //
49 class Transaction : public DocObserver {
50 public:
51 // Starts a undoable sequence of operations in a transaction that
52 // can be committed or rollbacked. All the operations will be
53 // grouped in the sprite's undo as an atomic operation.
54 Transaction(
55 Context* ctx,
56 Doc* doc,
57 const std::string& label,
58 Modification mod = ModifyDocument);
59 virtual ~Transaction();
60
61 // Can be used to change the new document range resulting from
62 // executing this transaction. This range can be used then in
63 // undo/redo operations to restore the Timeline selection/range.
64 void setNewDocRange(const DocRange& range);
65
66 // This must be called to commit all the changes, so the undo will
67 // be finally added in the sprite.
68 //
69 // If you don't use this routine, all the changes will be discarded
70 // (if the sprite's undo was enabled when the Transaction was
71 // created).
72 //
73 // WARNING: This must be called from the main UI thread, because
74 // it will generate a DocUndo::add() which triggers a
75 // DocUndoObserver::onAddUndoState() notification, which
76 // updates the Undo History window UI.
77 void commit();
78
79 // Discard everything that was added so far. We can start
80 // executing new Cmds again.
81 void rollbackAndStartAgain();
82
83 // Executes the given command and tries to add it to the container
84 // of executed commands (m_cmds).
85 //
86 // If some of these operations fails, the given "cmd" will be
87 // deleted anyway.
88 //
89 // TODO In the future we should refactor this using unique
90 // pointers-like structure only
91 void execute(Cmd* cmd);
92
93 CmdTransaction* cmds() { return m_cmds; }
94
95 private:
96 // List of changes during the execution of this transaction
97 enum class Changes {
98 kNone = 0,
99 // The selection has changed so we have to re-generate the
100 // boundary segments.
101 kSelection = 1,
102 // The color palette or color space has changed.
103 kColorChange = 2
104 };
105
106 void rollback(CmdTransaction* newCmds);
107
108 // DocObserver impl
109 void onSelectionChanged(DocEvent& ev) override;
110 void onColorSpaceChanged(DocEvent& ev) override;
111 void onPaletteChanged(DocEvent& ev) override;
112
113 Context* m_ctx;
114 Doc* m_doc;
115 DocUndo* m_undo;
116 CmdTransaction* m_cmds;
117 Changes m_changes;
118 };
119
120} // namespace app
121
122#endif
123