1 /*
2     Copyright © 2020, Inochi2D Project
3     Distributed under the 2-Clause BSD License, see LICENSE file.
4     
5     Authors: Luna Nielsen
6 */
7 module creator.core.actionstack;
8 import creator.core.settings;
9 import creator.actions;
10 import inochi2d;
11 
12 private {
13     Action[] actions;
14     size_t actionPointer;
15     size_t actionIndex;
16     size_t maxUndoHistory;
17 }
18 
19 /**
20     Initialize actions system
21 */
22 void incActionInit() {
23     maxUndoHistory = incSettingsGet!size_t("MaxUndoHistory", 100);
24 }
25 
26 /**
27     Pushes a new action to the stack
28 */
29 void incActionPush(Action action) {
30     
31     // Chop away entries outside undo history
32     if (actions.length > incActionGetUndoHistoryLength()) {
33         size_t toChop = actions.length-incActionGetUndoHistoryLength();
34         actions = actions[toChop..$];
35     }
36 
37     if (incActionTop() !is null && incActionTop().canMerge(action)) {
38         incActionTop().merge(action);
39         incActionNotifyTopChanged();
40     } else {
41         // Add to the history
42         actions = actions[0..actionPointer]~action;
43         actionPointer++;
44     }
45 }
46 
47 /**
48     Steps back in the action stack
49 */
50 void incActionUndo() {
51     actionPointer--;
52     if (cast(ptrdiff_t)actionPointer < 0) {
53         actionPointer = 0;
54         return;
55     }
56     actions[actionPointer].rollback();
57 }
58 
59 /**
60     Steps forward in the action stack
61 */
62 void incActionRedo() {
63     if (actionPointer >= actions.length) {
64         actionPointer = actions.length-1;
65         return;
66     }
67     actions[actionPointer].redo();
68     actionPointer++;
69 }
70 
71 /**
72     Gets whether undo is possible
73 */
74 bool incActionCanUndo() {
75     return actionPointer > 0;
76 }
77 
78 /**
79     Gets whether redo is possible
80 */
81 bool incActionCanRedo() {
82     return actionPointer < actions.length;
83 }
84 
85 /**
86     Gets the action history
87 */
88 Action[] incActionHistory() {
89     return actions;
90 }
91 
92 /**
93     Index of the current action
94 */
95 size_t incActionIndex() {
96     return actionPointer;
97 }
98 
99 /**
100     Gets the "top" action
101 */
102 Action incActionTop() {
103     return actionPointer > 0 && actionPointer <= actions.length ? actions[actionPointer-1] : null;
104 }
105 
106 /**
107     Notify that the top action has changed
108 */
109 void incActionNotifyTopChanged() {
110     actions.length = actionPointer;
111 }
112 
113 /**
114     Sets max undo history length
115 */
116 void incActionSetUndoHistoryLength(size_t length) {
117     length = clamp(length, 0, 1000);
118     maxUndoHistory = length;
119     incSettingsSet("MaxUndoHistory", maxUndoHistory);
120 }
121 
122 /**
123     Gets max undo history
124 */
125 size_t incActionGetUndoHistoryLength() {
126     return maxUndoHistory;
127 }
128 
129 /**
130     Sets the action index
131     Indexes start at 1, 0 is reserved for the INTIAL index
132 */
133 void incActionSetIndex(size_t index) {
134     if (index > actions.length) {
135         index = actions.length;
136     }
137 
138     if (index == 0) {
139 
140         // Undo till we can't anymore
141         while (incActionCanUndo()) incActionUndo();
142     }
143 
144     if (index < actionPointer) {
145         while (index < actionPointer) incActionUndo();
146     } else if (cast(ptrdiff_t)index > cast(ptrdiff_t)actionPointer) {
147         while (cast(ptrdiff_t)index > cast(ptrdiff_t)actionPointer) incActionRedo();
148     }
149 }
150 
151 /**
152     Clears action history
153 */
154 void incActionClearHistory() {
155     actions.length = 0;
156     actionPointer = 0;
157 }