72 text->value = NULL; |
82 text->value = NULL; |
73 } |
83 } |
74 text->value = NULL; |
84 text->value = NULL; |
75 XmTextSetString(text->obj, str); |
85 XmTextSetString(text->obj, str); |
76 } |
86 } |
|
87 |
|
88 UiUndoMgr* ui_create_undomgr() { |
|
89 UiUndoMgr *mgr = malloc(sizeof(UiUndoMgr)); |
|
90 mgr->begin = NULL; |
|
91 mgr->cur = NULL; |
|
92 mgr->length = 0; |
|
93 mgr->event = 1; |
|
94 return mgr; |
|
95 } |
|
96 |
|
97 void ui_text_modify_callback(Widget widget, UiText *value, XtPointer data) { |
|
98 XmTextVerifyCallbackStruct *txv = (XmTextVerifyCallbackStruct*)data; |
|
99 int type = txv->text->length > 0 ? UI_TEXTBUF_INSERT : UI_TEXTBUF_DELETE; |
|
100 UiUndoMgr *mgr = value->undomgr; |
|
101 if(!mgr->event) { |
|
102 return; |
|
103 } |
|
104 |
|
105 char *text = txv->text->ptr; |
|
106 int length = txv->text->length; |
|
107 |
|
108 if(mgr->cur) { |
|
109 UcxList *elm = mgr->cur->next; |
|
110 if(elm) { |
|
111 mgr->cur->next = NULL; |
|
112 while(elm) { |
|
113 elm->prev = NULL; |
|
114 UcxList *next = elm->next; |
|
115 ui_free_textbuf_op(elm->data); |
|
116 free(elm); |
|
117 elm = next; |
|
118 } |
|
119 } |
|
120 |
|
121 if(type == UI_TEXTBUF_INSERT) { |
|
122 UiTextBufOp *last_op = mgr->cur->data; |
|
123 if( |
|
124 last_op->type == UI_TEXTBUF_INSERT && |
|
125 ui_check_insertstr(last_op->text, last_op->len, text, length) == 0) |
|
126 { |
|
127 // append text to last op |
|
128 int ln = last_op->len; |
|
129 char *newtext = malloc(ln + length + 1); |
|
130 memcpy(newtext, last_op->text, ln); |
|
131 memcpy(newtext+ln, text, length); |
|
132 newtext[ln+length] = '\0'; |
|
133 |
|
134 last_op->text = newtext; |
|
135 last_op->len = ln + length; |
|
136 last_op->end += length; |
|
137 |
|
138 return; |
|
139 } |
|
140 } |
|
141 } |
|
142 |
|
143 char *str; |
|
144 if(type == UI_TEXTBUF_INSERT) { |
|
145 str = malloc(length + 1); |
|
146 memcpy(str, text, length); |
|
147 str[length] = 0; |
|
148 } else { |
|
149 length = txv->endPos - txv->startPos; |
|
150 str = malloc(length + 1); |
|
151 XmTextGetSubstring(value->obj, txv->startPos, length, length+1, str); |
|
152 } |
|
153 |
|
154 UiTextBufOp *op = malloc(sizeof(UiTextBufOp)); |
|
155 op->type = type; |
|
156 op->start = txv->startPos; |
|
157 op->end = txv->endPos + 1; |
|
158 op->len = length; |
|
159 op->text = str; |
|
160 |
|
161 UcxList *elm = ucx_list_append(NULL, op); |
|
162 mgr->cur = elm; |
|
163 mgr->begin = ucx_list_concat(mgr->begin, elm); |
|
164 } |
|
165 |
|
166 int ui_check_insertstr(char *oldstr, int oldlen, char *newstr, int newlen) { |
|
167 // return 1 if oldstr + newstr are one word |
|
168 |
|
169 int has_space = 0; |
|
170 for(int i=0;i<oldlen;i++) { |
|
171 if(oldstr[i] < 33) { |
|
172 has_space = 1; |
|
173 break; |
|
174 } |
|
175 } |
|
176 |
|
177 for(int i=0;i<newlen;i++) { |
|
178 if(has_space && newstr[i] > 32) { |
|
179 return 1; |
|
180 } |
|
181 } |
|
182 |
|
183 return 0; |
|
184 } |
|
185 |
|
186 void ui_free_textbuf_op(UiTextBufOp *op) { |
|
187 if(op->text) { |
|
188 free(op->text); |
|
189 } |
|
190 free(op); |
|
191 } |
|
192 |
|
193 |
|
194 void ui_text_undo(UiText *value) { |
|
195 UiUndoMgr *mgr = value->undomgr; |
|
196 |
|
197 if(mgr->cur) { |
|
198 UiTextBufOp *op = mgr->cur->data; |
|
199 mgr->event = 0; |
|
200 switch(op->type) { |
|
201 case UI_TEXTBUF_INSERT: { |
|
202 XmTextReplace(value->obj, op->start, op->end, ""); |
|
203 break; |
|
204 } |
|
205 case UI_TEXTBUF_DELETE: { |
|
206 XmTextInsert(value->obj, op->start, op->text); |
|
207 break; |
|
208 } |
|
209 } |
|
210 mgr->event = 1; |
|
211 mgr->cur = mgr->cur->prev; |
|
212 } |
|
213 } |
|
214 |
|
215 void ui_text_redo(UiText *value) { |
|
216 UiUndoMgr *mgr = value->undomgr; |
|
217 |
|
218 UcxList *elm = NULL; |
|
219 if(mgr->cur) { |
|
220 if(mgr->cur->next) { |
|
221 elm = mgr->cur->next; |
|
222 } |
|
223 } else if(mgr->begin) { |
|
224 elm = mgr->begin; |
|
225 } |
|
226 |
|
227 if(elm) { |
|
228 UiTextBufOp *op = elm->data; |
|
229 mgr->event = 0; |
|
230 switch(op->type) { |
|
231 case UI_TEXTBUF_INSERT: { |
|
232 XmTextInsert(value->obj, op->start, op->text); |
|
233 break; |
|
234 } |
|
235 case UI_TEXTBUF_DELETE: { |
|
236 XmTextReplace(value->obj, op->start, op->end, ""); |
|
237 break; |
|
238 } |
|
239 } |
|
240 mgr->event = 1; |
|
241 mgr->cur = elm; |
|
242 } |
|
243 } |