1 | #include "gtktexthistoryprivate.h" |
2 | |
3 | #if 0 |
4 | # define DEBUG_COMMANDS |
5 | #endif |
6 | |
7 | typedef struct |
8 | { |
9 | GtkTextHistory *history; |
10 | GString *buf; |
11 | struct { |
12 | int insert; |
13 | int bound; |
14 | } selection; |
15 | guint can_redo : 1; |
16 | guint can_undo : 1; |
17 | guint is_modified : 1; |
18 | } Text; |
19 | |
20 | enum { |
21 | IGNORE = 0, |
22 | SET = 1, |
23 | UNSET = 2, |
24 | }; |
25 | |
26 | enum { |
27 | IGNORE_SELECT = 0, |
28 | DO_SELECT = 1, |
29 | }; |
30 | |
31 | enum { |
32 | INSERT = 1, |
33 | INSERT_SEQ, |
34 | BACKSPACE, |
35 | DELETE_KEY, |
36 | UNDO, |
37 | REDO, |
38 | BEGIN_IRREVERSIBLE, |
39 | END_IRREVERSIBLE, |
40 | BEGIN_USER, |
41 | END_USER, |
42 | MODIFIED, |
43 | UNMODIFIED, |
44 | SELECT, |
45 | CHECK_SELECT, |
46 | SET_MAX_UNDO, |
47 | }; |
48 | |
49 | typedef struct |
50 | { |
51 | int kind; |
52 | int location; |
53 | int end_location; |
54 | const char *text; |
55 | const char *expected; |
56 | int can_undo; |
57 | int can_redo; |
58 | int is_modified; |
59 | int select; |
60 | } Command; |
61 | |
62 | static void |
63 | do_change_state (gpointer funcs_data, |
64 | gboolean is_modified, |
65 | gboolean can_undo, |
66 | gboolean can_redo) |
67 | { |
68 | Text *text = funcs_data; |
69 | |
70 | text->is_modified = is_modified; |
71 | text->can_undo = can_undo; |
72 | text->can_redo = can_redo; |
73 | } |
74 | |
75 | static void |
76 | do_insert (gpointer funcs_data, |
77 | guint begin, |
78 | guint end, |
79 | const char *text, |
80 | guint len) |
81 | { |
82 | Text *t = funcs_data; |
83 | |
84 | #ifdef DEBUG_COMMANDS |
85 | g_printerr ("Insert Into '%s' (begin=%u end=%u text=%s)\n" , |
86 | t->buf->str, begin, end, text); |
87 | #endif |
88 | |
89 | g_string_insert_len (string: t->buf, pos: begin, val: text, len); |
90 | } |
91 | |
92 | static void |
93 | do_delete (gpointer funcs_data, |
94 | guint begin, |
95 | guint end, |
96 | const char *expected_text, |
97 | guint len) |
98 | { |
99 | Text *t = funcs_data; |
100 | |
101 | #ifdef DEBUG_COMMANDS |
102 | g_printerr ("Delete(begin=%u end=%u expected_text=%s)\n" , begin, end, expected_text); |
103 | #endif |
104 | |
105 | if (end < begin) |
106 | { |
107 | guint tmp = end; |
108 | end = begin; |
109 | begin = tmp; |
110 | } |
111 | |
112 | g_assert_cmpint (memcmp (t->buf->str + begin, expected_text, len), ==, 0); |
113 | |
114 | if (end >= t->buf->len) |
115 | { |
116 | t->buf->len = begin; |
117 | t->buf->str[begin] = 0; |
118 | return; |
119 | } |
120 | |
121 | memmove (dest: t->buf->str + begin, |
122 | src: t->buf->str + end, |
123 | n: t->buf->len - end); |
124 | g_string_truncate (string: t->buf, len: t->buf->len - (end - begin)); |
125 | } |
126 | |
127 | static void |
128 | do_select (gpointer funcs_data, |
129 | int selection_insert, |
130 | int selection_bound) |
131 | { |
132 | Text *text = funcs_data; |
133 | |
134 | text->selection.insert = selection_insert; |
135 | text->selection.bound = selection_bound; |
136 | } |
137 | |
138 | static GtkTextHistoryFuncs funcs = { |
139 | do_change_state, |
140 | do_insert, |
141 | do_delete, |
142 | do_select, |
143 | }; |
144 | |
145 | static Text * |
146 | text_new (void) |
147 | { |
148 | Text *text = g_slice_new0 (Text); |
149 | |
150 | text->history = gtk_text_history_new (funcs: &funcs, funcs_data: text); |
151 | text->buf = g_string_new (NULL); |
152 | text->selection.insert = -1; |
153 | text->selection.bound = -1; |
154 | |
155 | return text; |
156 | } |
157 | |
158 | static void |
159 | text_free (Text *text) |
160 | { |
161 | g_object_unref (object: text->history); |
162 | g_string_free (string: text->buf, TRUE); |
163 | g_slice_free (Text, text); |
164 | } |
165 | |
166 | static void |
167 | command_insert (const Command *cmd, |
168 | Text *text) |
169 | { |
170 | do_insert (funcs_data: text, |
171 | begin: cmd->location, |
172 | end: cmd->location + g_utf8_strlen (p: cmd->text, max: -1), |
173 | text: cmd->text, |
174 | len: strlen (s: cmd->text)); |
175 | gtk_text_history_text_inserted (self: text->history, position: cmd->location, text: cmd->text, len: -1); |
176 | } |
177 | |
178 | static void |
179 | command_delete_key (const Command *cmd, |
180 | Text *text) |
181 | { |
182 | do_delete (funcs_data: text, |
183 | begin: cmd->location, |
184 | end: cmd->end_location, |
185 | expected_text: cmd->text, |
186 | len: strlen (s: cmd->text)); |
187 | gtk_text_history_text_deleted (self: text->history, |
188 | begin: cmd->location, |
189 | end: cmd->end_location, |
190 | text: cmd->text, |
191 | ABS (cmd->end_location - cmd->location)); |
192 | } |
193 | |
194 | static void |
195 | command_undo (const Command *cmd, |
196 | Text *text) |
197 | { |
198 | gtk_text_history_undo (self: text->history); |
199 | } |
200 | |
201 | static void |
202 | command_redo (const Command *cmd, |
203 | Text *text) |
204 | { |
205 | gtk_text_history_redo (self: text->history); |
206 | } |
207 | |
208 | static void |
209 | set_selection (Text *text, |
210 | int begin, |
211 | int end) |
212 | { |
213 | gtk_text_history_selection_changed (self: text->history, selection_insert: begin, selection_bound: end); |
214 | } |
215 | |
216 | static void |
217 | run_test (const Command *commands, |
218 | guint n_commands, |
219 | guint max_undo) |
220 | { |
221 | Text *text = text_new (); |
222 | |
223 | if (max_undo) |
224 | gtk_text_history_set_max_undo_levels (self: text->history, max_undo_levels: max_undo); |
225 | |
226 | for (guint i = 0; i < n_commands; i++) |
227 | { |
228 | const Command *cmd = &commands[i]; |
229 | |
230 | #ifdef DEBUG_COMMANDS |
231 | g_printerr ("%d: %d\n" , i, cmd->kind); |
232 | #endif |
233 | |
234 | switch (cmd->kind) |
235 | { |
236 | case INSERT: |
237 | command_insert (cmd, text); |
238 | break; |
239 | |
240 | case INSERT_SEQ: |
241 | for (guint j = 0; cmd->text[j]; j++) |
242 | { |
243 | const char seqtext[2] = { cmd->text[j], 0 }; |
244 | Command seq = { INSERT, cmd->location + j, 1, seqtext, NULL }; |
245 | command_insert (cmd: &seq, text); |
246 | } |
247 | break; |
248 | |
249 | case DELETE_KEY: |
250 | if (cmd->select == DO_SELECT) |
251 | set_selection (text, begin: cmd->location, end: cmd->end_location); |
252 | else if (strlen (s: cmd->text) == 1) |
253 | set_selection (text, begin: cmd->location, end: -1); |
254 | command_delete_key (cmd, text); |
255 | break; |
256 | |
257 | case BACKSPACE: |
258 | if (cmd->select == DO_SELECT) |
259 | set_selection (text, begin: cmd->location, end: cmd->end_location); |
260 | else if (strlen (s: cmd->text) == 1) |
261 | set_selection (text, begin: cmd->end_location, end: -1); |
262 | command_delete_key (cmd, text); |
263 | break; |
264 | |
265 | case UNDO: |
266 | command_undo (cmd, text); |
267 | break; |
268 | |
269 | case REDO: |
270 | command_redo (cmd, text); |
271 | break; |
272 | |
273 | case BEGIN_USER: |
274 | gtk_text_history_begin_user_action (self: text->history); |
275 | break; |
276 | |
277 | case END_USER: |
278 | gtk_text_history_end_user_action (self: text->history); |
279 | break; |
280 | |
281 | case BEGIN_IRREVERSIBLE: |
282 | gtk_text_history_begin_irreversible_action (self: text->history); |
283 | break; |
284 | |
285 | case END_IRREVERSIBLE: |
286 | gtk_text_history_end_irreversible_action (self: text->history); |
287 | break; |
288 | |
289 | case MODIFIED: |
290 | gtk_text_history_modified_changed (self: text->history, TRUE); |
291 | break; |
292 | |
293 | case UNMODIFIED: |
294 | gtk_text_history_modified_changed (self: text->history, FALSE); |
295 | break; |
296 | |
297 | case SELECT: |
298 | gtk_text_history_selection_changed (self: text->history, |
299 | selection_insert: cmd->location, |
300 | selection_bound: cmd->end_location); |
301 | break; |
302 | |
303 | case CHECK_SELECT: |
304 | g_assert_cmpint (text->selection.insert, ==, cmd->location); |
305 | g_assert_cmpint (text->selection.bound, ==, cmd->end_location); |
306 | break; |
307 | |
308 | case SET_MAX_UNDO: |
309 | /* Not ideal use of location, but fine */ |
310 | gtk_text_history_set_max_undo_levels (self: text->history, max_undo_levels: cmd->location); |
311 | break; |
312 | |
313 | default: |
314 | break; |
315 | } |
316 | |
317 | if (cmd->expected) |
318 | g_assert_cmpstr (text->buf->str, ==, cmd->expected); |
319 | |
320 | if (cmd->can_redo == SET) |
321 | g_assert_cmpint (text->can_redo, ==, TRUE); |
322 | else if (cmd->can_redo == UNSET) |
323 | g_assert_cmpint (text->can_redo, ==, FALSE); |
324 | |
325 | if (cmd->can_undo == SET) |
326 | g_assert_cmpint (text->can_undo, ==, TRUE); |
327 | else if (cmd->can_undo == UNSET) |
328 | g_assert_cmpint (text->can_undo, ==, FALSE); |
329 | |
330 | if (cmd->is_modified == SET) |
331 | g_assert_cmpint (text->is_modified, ==, TRUE); |
332 | else if (cmd->is_modified == UNSET) |
333 | g_assert_cmpint (text->is_modified, ==, FALSE); |
334 | } |
335 | |
336 | text_free (text); |
337 | } |
338 | |
339 | static void |
340 | test1 (void) |
341 | { |
342 | static const Command commands[] = { |
343 | { INSERT, 0, -1, "test" , "test" , SET, UNSET }, |
344 | { INSERT, 2, -1, "s" , "tesst" , SET, UNSET }, |
345 | { INSERT, 3, -1, "ss" , "tesssst" , SET, UNSET }, |
346 | { DELETE_KEY, 2, 5, "sss" , "test" , SET, UNSET }, |
347 | { UNDO, -1, -1, NULL, "tesssst" , SET, SET }, |
348 | { REDO, -1, -1, NULL, "test" , SET, UNSET }, |
349 | { UNDO, -1, -1, NULL, "tesssst" , SET, SET }, |
350 | { DELETE_KEY, 0, 7, "tesssst" , "" , SET, UNSET }, |
351 | { INSERT, 0, -1, "z" , "z" , SET, UNSET }, |
352 | { UNDO, -1, -1, NULL, "" , SET, SET }, |
353 | { UNDO, -1, -1, NULL, "tesssst" , SET, SET }, |
354 | { UNDO, -1, -1, NULL, "test" , SET, SET }, |
355 | }; |
356 | |
357 | run_test (commands, G_N_ELEMENTS (commands), max_undo: 0); |
358 | } |
359 | |
360 | static void |
361 | test2 (void) |
362 | { |
363 | static const Command commands[] = { |
364 | { BEGIN_IRREVERSIBLE, -1, -1, NULL, "" , UNSET, UNSET }, |
365 | { INSERT, 0, -1, "this is a test" , "this is a test" , UNSET, UNSET }, |
366 | { END_IRREVERSIBLE, -1, -1, NULL, "this is a test" , UNSET, UNSET }, |
367 | { UNDO, -1, -1, NULL, "this is a test" , UNSET, UNSET }, |
368 | { REDO, -1, -1, NULL, "this is a test" , UNSET, UNSET }, |
369 | { BEGIN_USER, -1, -1, NULL, NULL, UNSET, UNSET }, |
370 | { INSERT, 0, -1, "first" , "firstthis is a test" , UNSET, UNSET }, |
371 | { INSERT, 5, -1, " " , "first this is a test" , UNSET, UNSET }, |
372 | { END_USER, -1, -1, NULL, "first this is a test" , SET, UNSET }, |
373 | { UNDO, -1, -1, NULL, "this is a test" , UNSET, SET }, |
374 | { UNDO, -1, -1, NULL, "this is a test" , UNSET, SET }, |
375 | { REDO, -1, -1, NULL, "first this is a test" , SET, UNSET }, |
376 | { UNDO, -1, -1, NULL, "this is a test" , UNSET, SET }, |
377 | }; |
378 | |
379 | run_test (commands, G_N_ELEMENTS (commands), max_undo: 0); |
380 | } |
381 | |
382 | static void |
383 | test3 (void) |
384 | { |
385 | static const Command commands[] = { |
386 | { INSERT_SEQ, 0, -1, "this is a test of insertions." , "this is a test of insertions." , SET, UNSET }, |
387 | { UNDO, -1, -1, NULL, "this is a test of" , SET, SET }, |
388 | { UNDO, -1, -1, NULL, "this is a test" , SET, SET }, |
389 | { UNDO, -1, -1, NULL, "this is a" , SET, SET }, |
390 | { UNDO, -1, -1, NULL, "this is" , SET, SET }, |
391 | { UNDO, -1, -1, NULL, "this" , SET, SET }, |
392 | { UNDO, -1, -1, NULL, "" , UNSET, SET }, |
393 | { UNDO, -1, -1, NULL, "" , UNSET, SET }, |
394 | { REDO, -1, -1, NULL, "this" , SET, SET }, |
395 | { REDO, -1, -1, NULL, "this is" , SET, SET }, |
396 | { REDO, -1, -1, NULL, "this is a" , SET, SET }, |
397 | { REDO, -1, -1, NULL, "this is a test" , SET, SET }, |
398 | { REDO, -1, -1, NULL, "this is a test of" , SET, SET }, |
399 | { REDO, -1, -1, NULL, "this is a test of insertions." , SET, UNSET }, |
400 | }; |
401 | |
402 | run_test (commands, G_N_ELEMENTS (commands), max_undo: 0); |
403 | } |
404 | |
405 | static void |
406 | test4 (void) |
407 | { |
408 | static const Command commands[] = { |
409 | { INSERT, 0, -1, "initial text" , "initial text" , SET, UNSET }, |
410 | /* Barrier */ |
411 | { BEGIN_IRREVERSIBLE, -1, -1, NULL, NULL, UNSET, UNSET }, |
412 | { END_IRREVERSIBLE, -1, -1, NULL, NULL, UNSET, UNSET }, |
413 | { INSERT, 0, -1, "more text " , "more text initial text" , SET, UNSET }, |
414 | { UNDO, -1, -1, NULL, "initial text" , UNSET, SET }, |
415 | { UNDO, -1, -1, NULL, "initial text" , UNSET, SET }, |
416 | { REDO, -1, -1, NULL, "more text initial text" , SET, UNSET }, |
417 | /* Barrier */ |
418 | { BEGIN_IRREVERSIBLE, UNSET, UNSET }, |
419 | { END_IRREVERSIBLE, UNSET, UNSET }, |
420 | { UNDO, -1, -1, NULL, "more text initial text" , UNSET, UNSET }, |
421 | }; |
422 | |
423 | run_test (commands, G_N_ELEMENTS (commands), max_undo: 0); |
424 | } |
425 | |
426 | static void |
427 | test5 (void) |
428 | { |
429 | static const Command commands[] = { |
430 | { INSERT, 0, -1, "initial text" , "initial text" , SET, UNSET }, |
431 | { DELETE_KEY, 0, 12, "initial text" , "" , SET, UNSET }, |
432 | /* Add empty nested user action (should get ignored) */ |
433 | { BEGIN_USER, -1, -1, NULL, NULL, UNSET, UNSET }, |
434 | { BEGIN_USER, -1, -1, NULL, NULL, UNSET, UNSET }, |
435 | { BEGIN_USER, -1, -1, NULL, NULL, UNSET, UNSET }, |
436 | { END_USER, -1, -1, NULL, NULL, UNSET, UNSET }, |
437 | { END_USER, -1, -1, NULL, NULL, UNSET, UNSET }, |
438 | { END_USER, -1, -1, NULL, NULL, SET, UNSET }, |
439 | { UNDO, -1, -1, NULL, "initial text" }, |
440 | }; |
441 | |
442 | run_test (commands, G_N_ELEMENTS (commands), max_undo: 0); |
443 | } |
444 | |
445 | static void |
446 | test6 (void) |
447 | { |
448 | static const Command commands[] = { |
449 | { INSERT_SEQ, 0, -1, " \t\t this is some text" , " \t\t this is some text" , SET, UNSET }, |
450 | { UNDO, -1, -1, NULL, " \t\t this is some" , SET, SET }, |
451 | { UNDO, -1, -1, NULL, " \t\t this is" , SET, SET }, |
452 | { UNDO, -1, -1, NULL, " \t\t this" , SET, SET }, |
453 | { UNDO, -1, -1, NULL, "" , UNSET, SET }, |
454 | { UNDO, -1, -1, NULL, "" , UNSET, SET }, |
455 | }; |
456 | |
457 | run_test (commands, G_N_ELEMENTS (commands), max_undo: 0); |
458 | } |
459 | |
460 | static void |
461 | test7 (void) |
462 | { |
463 | static const Command commands[] = { |
464 | { MODIFIED, -1, -1, NULL, NULL, UNSET, UNSET, SET }, |
465 | { UNMODIFIED, -1, -1, NULL, NULL, UNSET, UNSET, UNSET }, |
466 | { INSERT, 0, -1, "foo bar" , "foo bar" , SET, UNSET, UNSET }, |
467 | { MODIFIED, -1, -1, NULL, NULL, SET, UNSET, SET }, |
468 | { UNDO, -1, -1, NULL, "" , UNSET, SET, UNSET }, |
469 | { REDO, -1, -1, NULL, "foo bar" , SET, UNSET, SET }, |
470 | { UNDO, -1, -1, NULL, "" , UNSET, SET, UNSET }, |
471 | { REDO, -1, -1, NULL, "foo bar" , SET, UNSET, SET }, |
472 | }; |
473 | |
474 | run_test (commands, G_N_ELEMENTS (commands), max_undo: 0); |
475 | } |
476 | |
477 | static void |
478 | test8 (void) |
479 | { |
480 | static const Command commands[] = { |
481 | { INSERT, 0, -1, "foo bar" , "foo bar" , SET, UNSET, UNSET }, |
482 | { MODIFIED, -1, -1, NULL, NULL, SET, UNSET, SET }, |
483 | { INSERT, 0, -1, "f" , "ffoo bar" , SET, UNSET, SET }, |
484 | { UNMODIFIED, -1, -1, NULL, NULL, SET, UNSET, UNSET }, |
485 | { UNDO, -1, -1, NULL, "foo bar" , SET, SET, SET }, |
486 | { UNDO, -1, -1, NULL, "" , UNSET, SET, SET }, |
487 | { REDO, -1, -1, NULL, "foo bar" , SET, SET, SET }, |
488 | { REDO, -1, -1, NULL, "ffoo bar" , SET, UNSET, UNSET }, |
489 | }; |
490 | |
491 | run_test (commands, G_N_ELEMENTS (commands), max_undo: 0); |
492 | } |
493 | |
494 | static void |
495 | test9 (void) |
496 | { |
497 | static const Command commands[] = { |
498 | { INSERT, 0, -1, "foo bar" , "foo bar" , SET, UNSET, UNSET }, |
499 | { DELETE_KEY, 0, 3, "foo" , " bar" , SET, UNSET, UNSET, DO_SELECT }, |
500 | { DELETE_KEY, 0, 4, " bar" , "" , SET, UNSET, UNSET, DO_SELECT }, |
501 | { UNDO, -1, -1, NULL, " bar" , SET, SET, UNSET }, |
502 | { CHECK_SELECT, 0, 4, NULL, " bar" , SET, SET, UNSET }, |
503 | { UNDO, -1, -1, NULL, "foo bar" , SET, SET, UNSET }, |
504 | { CHECK_SELECT, 0, 3, NULL, "foo bar" , SET, SET, UNSET }, |
505 | { BEGIN_IRREVERSIBLE, -1, -1, NULL, "foo bar" , UNSET, UNSET, UNSET }, |
506 | { END_IRREVERSIBLE, -1, -1, NULL, "foo bar" , UNSET, UNSET, UNSET }, |
507 | }; |
508 | |
509 | run_test (commands, G_N_ELEMENTS (commands), max_undo: 0); |
510 | } |
511 | |
512 | static void |
513 | test10 (void) |
514 | { |
515 | static const Command commands[] = { |
516 | { BEGIN_USER }, { INSERT, 0, -1, "t" , "t" , UNSET, UNSET, UNSET }, { END_USER }, |
517 | { BEGIN_USER }, { INSERT, 1, -1, " " , "t " , UNSET, UNSET, UNSET }, { END_USER }, |
518 | { BEGIN_USER }, { INSERT, 2, -1, "t" , "t t" , UNSET, UNSET, UNSET }, { END_USER }, |
519 | { BEGIN_USER }, { INSERT, 3, -1, "h" , "t th" , UNSET, UNSET, UNSET }, { END_USER }, |
520 | { BEGIN_USER }, { INSERT, 4, -1, "i" , "t thi" , UNSET, UNSET, UNSET }, { END_USER }, |
521 | { BEGIN_USER }, { INSERT, 5, -1, "s" , "t this" , UNSET, UNSET, UNSET }, { END_USER }, |
522 | }; |
523 | |
524 | run_test (commands, G_N_ELEMENTS (commands), max_undo: 0); |
525 | } |
526 | |
527 | static void |
528 | test11 (void) |
529 | { |
530 | /* Test backspace */ |
531 | static const Command commands[] = { |
532 | { INSERT_SEQ, 0, -1, "insert some text" , "insert some text" , SET, UNSET, UNSET }, |
533 | { BACKSPACE, 15, 16, "t" , "insert some tex" , SET, UNSET, UNSET }, |
534 | { BACKSPACE, 14, 15, "x" , "insert some te" , SET, UNSET, UNSET }, |
535 | { BACKSPACE, 13, 14, "e" , "insert some t" , SET, UNSET, UNSET }, |
536 | { BACKSPACE, 12, 13, "t" , "insert some " , SET, UNSET, UNSET }, |
537 | { UNDO, -1, -1, NULL, "insert some text" , SET, SET, UNSET }, |
538 | }; |
539 | |
540 | run_test (commands, G_N_ELEMENTS (commands), max_undo: 0); |
541 | } |
542 | |
543 | static void |
544 | test12 (void) |
545 | { |
546 | static const Command commands[] = { |
547 | { INSERT_SEQ, 0, -1, "this is a test\nmore" , "this is a test\nmore" , SET, UNSET, UNSET }, |
548 | { UNDO, -1, -1, NULL, "this is a test\n" , SET, SET, UNSET }, |
549 | { UNDO, -1, -1, NULL, "this is a test" , SET, SET, UNSET }, |
550 | { UNDO, -1, -1, NULL, "this is a" , SET, SET, UNSET }, |
551 | { UNDO, -1, -1, NULL, "this is" , SET, SET, UNSET }, |
552 | { UNDO, -1, -1, NULL, "this" , SET, SET, UNSET }, |
553 | { UNDO, -1, -1, NULL, "" , UNSET, SET, UNSET }, |
554 | }; |
555 | |
556 | run_test (commands, G_N_ELEMENTS (commands), max_undo: 0); |
557 | } |
558 | |
559 | static void |
560 | test13 (void) |
561 | { |
562 | static const Command commands[] = { |
563 | { INSERT_SEQ, 0, -1, "this is a test\nmore" , "this is a test\nmore" , SET, UNSET, UNSET }, |
564 | { UNDO, -1, -1, NULL, "this is a test\n" , SET, SET, UNSET }, |
565 | { UNDO, -1, -1, NULL, "this is a test" , SET, SET, UNSET }, |
566 | { UNDO, -1, -1, NULL, "this is a" , UNSET, SET, UNSET }, |
567 | { UNDO, -1, -1, NULL, "this is a" , UNSET, SET, UNSET }, |
568 | { SET_MAX_UNDO, 2, -1, NULL, "this is a" , UNSET, SET, UNSET }, |
569 | { REDO, -1, -1, NULL, "this is a test" , SET, SET, UNSET }, |
570 | { REDO, -1, -1, NULL, "this is a test\n" , SET, UNSET, UNSET }, |
571 | { REDO, -1, -1, NULL, "this is a test\n" , SET, UNSET, UNSET }, |
572 | }; |
573 | |
574 | run_test (commands, G_N_ELEMENTS (commands), max_undo: 3); |
575 | } |
576 | |
577 | static void |
578 | test14 (void) |
579 | { |
580 | char *fill = g_strnfill (length: 1024, fill_char: 'x'); |
581 | char *fill_after = g_strnfill (length: 1025, fill_char: 'x'); |
582 | char *fill_after_2 = g_strdup_printf (format: "%s word" , fill_after); |
583 | const Command commands[] = { |
584 | { BEGIN_USER, -1, -1, NULL, NULL, UNSET, UNSET, UNSET }, |
585 | { INSERT, 0, -1, fill, fill, UNSET, UNSET, UNSET }, |
586 | { END_USER, -1, -1, NULL, NULL, SET, UNSET, UNSET }, |
587 | { BEGIN_USER, -1, -1, NULL, NULL, UNSET, UNSET, UNSET }, |
588 | { INSERT, 0, -1, "x" , fill_after, UNSET, UNSET, UNSET }, |
589 | { END_USER, -1, -1, NULL, NULL, SET, UNSET, UNSET }, |
590 | { BEGIN_USER, -1, -1, NULL, NULL, UNSET, UNSET, UNSET }, |
591 | { INSERT_SEQ, strlen(s: fill_after), -1, " word" , fill_after_2, UNSET, UNSET, UNSET }, |
592 | { END_USER, -1, -1, NULL, NULL, SET, UNSET, UNSET }, |
593 | { UNDO, -1, -1, NULL, fill_after, SET, SET, UNSET }, |
594 | { UNDO, -1, -1, NULL, fill, SET, SET, UNSET }, |
595 | { UNDO, -1, -1, NULL, "" , UNSET, SET, UNSET }, |
596 | }; |
597 | |
598 | run_test (commands, G_N_ELEMENTS (commands), max_undo: 0); |
599 | |
600 | g_free (mem: fill); |
601 | g_free (mem: fill_after); |
602 | g_free (mem: fill_after_2); |
603 | } |
604 | |
605 | static void |
606 | test_issue_4276 (void) |
607 | { |
608 | const Command commands[] = { |
609 | { INSERT, 0, -1, "this is some text" , "this is some text" , SET, UNSET, UNSET }, |
610 | { SELECT, 0, 17, NULL, "this is some text" , SET, UNSET, UNSET }, |
611 | { BEGIN_USER, -1, -1, NULL, NULL, UNSET, UNSET, UNSET }, |
612 | { DELETE_KEY, 0, 17, "this is some text" , "" , UNSET, UNSET, UNSET }, |
613 | { INSERT, 0, -1, "z" , "z" , UNSET, UNSET, UNSET }, |
614 | { END_USER, -1, -1, NULL, NULL, SET, UNSET, UNSET }, |
615 | { INSERT, 1, -1, "zzz" , "zzzz" , SET, UNSET, UNSET }, |
616 | { UNDO, -1, -1, NULL, "z" , SET, SET, UNSET }, |
617 | }; |
618 | |
619 | run_test (commands, G_N_ELEMENTS (commands), max_undo: 0); |
620 | } |
621 | |
622 | static void |
623 | test_issue_4575 (void) |
624 | { |
625 | const Command commands[] = { |
626 | { INSERT, 0, -1, "this is some text" , "this is some text" , SET, UNSET, UNSET }, |
627 | { SELECT, 5, 8, NULL, NULL, SET, UNSET, UNSET }, |
628 | { BEGIN_USER, -1, -1, NULL, NULL, UNSET, UNSET, UNSET }, |
629 | { DELETE_KEY, 5, 8, "is " , "this some text" , UNSET, UNSET, UNSET, IGNORE_SELECT }, |
630 | { END_USER, -1, -1, NULL, NULL, SET, UNSET, UNSET }, |
631 | { UNDO, -1, -1, NULL, "this is some text" , SET, SET, UNSET }, |
632 | { CHECK_SELECT, 5, 8, NULL, "this is some text" , SET, SET, UNSET }, |
633 | }; |
634 | |
635 | run_test (commands, G_N_ELEMENTS (commands), max_undo: 0); |
636 | } |
637 | |
638 | int |
639 | main (int argc, |
640 | char *argv[]) |
641 | { |
642 | (g_test_init) (argc: &argc, argv: &argv, NULL); |
643 | |
644 | g_test_add_func (testpath: "/Gtk/TextHistory/test1" , test_func: test1); |
645 | g_test_add_func (testpath: "/Gtk/TextHistory/test2" , test_func: test2); |
646 | g_test_add_func (testpath: "/Gtk/TextHistory/test3" , test_func: test3); |
647 | g_test_add_func (testpath: "/Gtk/TextHistory/test4" , test_func: test4); |
648 | g_test_add_func (testpath: "/Gtk/TextHistory/test5" , test_func: test5); |
649 | g_test_add_func (testpath: "/Gtk/TextHistory/test6" , test_func: test6); |
650 | g_test_add_func (testpath: "/Gtk/TextHistory/test7" , test_func: test7); |
651 | g_test_add_func (testpath: "/Gtk/TextHistory/test8" , test_func: test8); |
652 | g_test_add_func (testpath: "/Gtk/TextHistory/test9" , test_func: test9); |
653 | g_test_add_func (testpath: "/Gtk/TextHistory/test10" , test_func: test10); |
654 | g_test_add_func (testpath: "/Gtk/TextHistory/test11" , test_func: test11); |
655 | g_test_add_func (testpath: "/Gtk/TextHistory/test12" , test_func: test12); |
656 | g_test_add_func (testpath: "/Gtk/TextHistory/test13" , test_func: test13); |
657 | g_test_add_func (testpath: "/Gtk/TextHistory/test14" , test_func: test14); |
658 | g_test_add_func (testpath: "/Gtk/TextHistory/issue_4276" , test_func: test_issue_4276); |
659 | g_test_add_func (testpath: "/Gtk/TextHistory/issue_4575" , test_func: test_issue_4575); |
660 | |
661 | return g_test_run (); |
662 | } |
663 | |