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 (actionPointer+1 > incActionGetUndoHistoryLength()) {
33         size_t toChop = (actionPointer+1)-incActionGetUndoHistoryLength();
34         actions = actions[toChop..$];
35         actionPointer -= toChop;
36     }
37 
38     if (incActionTop() !is null && incActionTop().canMerge(action)) {
39         incActionTop().merge(action);
40         incActionNotifyTopChanged();
41     } else {
42         // Add to the history
43         actions = actions[0..actionPointer]~action;
44         actionPointer++;
45     }
46 }
47 
48 /**
49     Steps back in the action stack
50 */
51 void incActionUndo() {
52     actionPointer--;
53     if (cast(ptrdiff_t)actionPointer < 0) {
54         actionPointer = 0;
55         return;
56     }
57     actions[actionPointer].rollback();
58 }
59 
60 /**
61     Steps forward in the action stack
62 */
63 void incActionRedo() {
64     if (actionPointer >= actions.length) {
65         actionPointer = actions.length;
66         return;
67     }
68     actions[actionPointer].redo();
69     actionPointer++;
70 }
71 
72 /**
73     Gets whether undo is possible
74 */
75 bool incActionCanUndo() {
76     return actionPointer > 0;
77 }
78 
79 /**
80     Gets whether redo is possible
81 */
82 bool incActionCanRedo() {
83     return actionPointer < actions.length;
84 }
85 
86 /**
87     Gets the action history
88 */
89 Action[] incActionHistory() {
90     return actions;
91 }
92 
93 /**
94     Index of the current action
95 */
96 size_t incActionIndex() {
97     return actionPointer;
98 }
99 
100 /**
101     Gets the "top" action
102 */
103 Action incActionTop() {
104     return actionPointer > 0 && actionPointer <= actions.length ? actions[actionPointer-1] : null;
105 }
106 
107 /**
108     Notify that the top action has changed
109 */
110 void incActionNotifyTopChanged() {
111     actions.length = actionPointer;
112 }
113 
114 /**
115     Sets max undo history length
116 */
117 void incActionSetUndoHistoryLength(size_t length) {
118     length = clamp(length, 0, 1000);
119     maxUndoHistory = length;
120     incSettingsSet("MaxUndoHistory", maxUndoHistory);
121 }
122 
123 /**
124     Gets max undo history
125 */
126 size_t incActionGetUndoHistoryLength() {
127     return maxUndoHistory;
128 }
129 
130 /**
131     Sets the action index
132     Indexes start at 1, 0 is reserved for the INTIAL index
133 */
134 void incActionSetIndex(size_t index) {
135     if (index > actions.length) {
136         index = actions.length;
137     }
138 
139     if (index == 0) {
140 
141         // Undo till we can't anymore
142         while (incActionCanUndo()) incActionUndo();
143     }
144 
145     if (index < actionPointer) {
146         while (index < actionPointer) incActionUndo();
147     } else if (cast(ptrdiff_t)index > cast(ptrdiff_t)actionPointer) {
148         while (cast(ptrdiff_t)index > cast(ptrdiff_t)actionPointer) incActionRedo();
149     }
150 }
151 
152 /**
153     Clears action history
154 */
155 void incActionClearHistory() {
156     actions.length = 0;
157     actionPointer = 0;
158 }