1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * Copyright (C) 2020 Blender Foundation.
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup edinterface
22 *
23 * Undo stack to use for UI widgets that manage their own editing state.
24 */
25
26 #include <string.h>
27
28 #include "BLI_listbase.h"
29
30 #include "DNA_listBase.h"
31
32 #include "MEM_guardedalloc.h"
33
34 #include "interface_intern.h"
35
36 /* -------------------------------------------------------------------- */
37 /** \name Text Field Undo Stack
38 * \{ */
39
40 typedef struct uiUndoStack_Text_State {
41 struct uiUndoStack_Text_State *next, *prev;
42 int cursor_index;
43 char text[0];
44 } uiUndoStack_Text_State;
45
46 typedef struct uiUndoStack_Text {
47 ListBase states;
48 uiUndoStack_Text_State *current;
49 } uiUndoStack_Text;
50
ui_textedit_undo_impl(uiUndoStack_Text * stack,int * r_cursor_index)51 static const char *ui_textedit_undo_impl(uiUndoStack_Text *stack, int *r_cursor_index)
52 {
53 /* Don't undo if no data has been pushed yet. */
54 if (stack->current == NULL) {
55 return NULL;
56 }
57
58 /* Travel backwards in the stack and copy information to the caller. */
59 if (stack->current->prev != NULL) {
60 stack->current = stack->current->prev;
61
62 *r_cursor_index = stack->current->cursor_index;
63 return stack->current->text;
64 }
65 return NULL;
66 }
67
ui_textedit_redo_impl(uiUndoStack_Text * stack,int * r_cursor_index)68 static const char *ui_textedit_redo_impl(uiUndoStack_Text *stack, int *r_cursor_index)
69 {
70 /* Don't redo if no data has been pushed yet. */
71 if (stack->current == NULL) {
72 return NULL;
73 }
74
75 /* Only redo if new data has not been entered since the last undo. */
76 if (stack->current->next) {
77 stack->current = stack->current->next;
78
79 *r_cursor_index = stack->current->cursor_index;
80 return stack->current->text;
81 }
82 return NULL;
83 }
84
ui_textedit_undo(uiUndoStack_Text * stack,int direction,int * r_cursor_index)85 const char *ui_textedit_undo(uiUndoStack_Text *stack, int direction, int *r_cursor_index)
86 {
87 BLI_assert(ELEM(direction, -1, 1));
88 if (direction < 0) {
89 return ui_textedit_undo_impl(stack, r_cursor_index);
90 }
91 return ui_textedit_redo_impl(stack, r_cursor_index);
92 }
93
94 /**
95 * Push the information in the arguments to a new state in the undo stack.
96 *
97 * \note Currently the total length of the undo stack is not limited.
98 */
ui_textedit_undo_push(uiUndoStack_Text * stack,const char * text,int cursor_index)99 void ui_textedit_undo_push(uiUndoStack_Text *stack, const char *text, int cursor_index)
100 {
101 /* Clear all redo actions from the current state. */
102 if (stack->current != NULL) {
103 while (stack->current->next) {
104 uiUndoStack_Text_State *state = stack->current->next;
105 BLI_remlink(&stack->states, state);
106 MEM_freeN(state);
107 }
108 }
109
110 /* Create the new state */
111 const int text_size = strlen(text) + 1;
112 stack->current = MEM_mallocN(sizeof(uiUndoStack_Text_State) + text_size, __func__);
113 stack->current->cursor_index = cursor_index;
114 memcpy(stack->current->text, text, text_size);
115 BLI_addtail(&stack->states, stack->current);
116 }
117 /**
118 * Start the undo stack.
119 *
120 * \note The current state should be pushed immediately after calling this.
121 */
ui_textedit_undo_stack_create(void)122 uiUndoStack_Text *ui_textedit_undo_stack_create(void)
123 {
124 uiUndoStack_Text *stack = MEM_mallocN(sizeof(uiUndoStack_Text), __func__);
125 stack->current = NULL;
126 BLI_listbase_clear(&stack->states);
127
128 return stack;
129 }
130
ui_textedit_undo_stack_destroy(uiUndoStack_Text * stack)131 void ui_textedit_undo_stack_destroy(uiUndoStack_Text *stack)
132 {
133 BLI_freelistN(&stack->states);
134 MEM_freeN(stack);
135 }
136
137 /** \} */
138