1 | /* GTK - The GIMP Toolkit |
2 | * Copyright (C) 2011 Red Hat, Inc. |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Lesser General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Lesser General Public |
15 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
16 | */ |
17 | |
18 | #include <gtk/gtk.h> |
19 | |
20 | static int serial = 0; |
21 | |
22 | typedef struct { |
23 | int serial; |
24 | int count; |
25 | int start; |
26 | int end; |
27 | char *text; |
28 | char *new_text; |
29 | int position; |
30 | int length; |
31 | } EntryData; |
32 | |
33 | static void |
34 | notify (GtkEditable *editable, GParamSpec *pspec, EntryData *data) |
35 | { |
36 | data->serial = serial++; |
37 | data->count++; |
38 | data->text = gtk_editable_get_chars (editable, start_pos: 0, end_pos: -1); |
39 | gtk_editable_get_selection_bounds (editable, start_pos: &data->start, end_pos: &data->end); |
40 | |
41 | #if 0 |
42 | g_print ("notify::%s\n" , pspec->name); |
43 | g_print ("\ttext: %s\n" , data->text); |
44 | g_print ("\tstart: %d\n" , data->start); |
45 | g_print ("\tend: %d\n" , data->end); |
46 | #endif |
47 | } |
48 | |
49 | static void |
50 | insert_text (GtkEditable *editable, |
51 | const char *new_text, |
52 | int new_text_length, |
53 | int *position, |
54 | EntryData *data) |
55 | { |
56 | data->serial = serial++; |
57 | data->count++; |
58 | data->text = gtk_editable_get_chars (editable, start_pos: 0, end_pos: -1); |
59 | gtk_editable_get_selection_bounds (editable, start_pos: &data->start, end_pos: &data->end); |
60 | data->new_text = g_strdup (str: new_text); |
61 | data->position = *position; |
62 | data->length = new_text_length; |
63 | |
64 | #if 0 |
65 | g_print ("insert-text \"%s\", %d\n" , new_text, *position); |
66 | g_print ("\ttext: %s\n" , data->text); |
67 | g_print ("\tstart: %d\n" , data->start); |
68 | g_print ("\tend: %d\n" , data->end); |
69 | #endif |
70 | } |
71 | |
72 | static void |
73 | delete_text (GtkEditable *editable, |
74 | int start_pos, |
75 | int end_pos, |
76 | EntryData *data) |
77 | { |
78 | data->serial = serial++; |
79 | data->count++; |
80 | data->text = gtk_editable_get_chars (editable, start_pos: 0, end_pos: -1); |
81 | gtk_editable_get_selection_bounds (editable, start_pos: &data->start, end_pos: &data->end); |
82 | data->position = start_pos; |
83 | data->length = end_pos - start_pos; |
84 | |
85 | #if 0 |
86 | g_print ("delete-text %d %d\n" , start_pos, end_pos); |
87 | g_print ("\ttext: %s\n" , data->text); |
88 | g_print ("\tstart: %d\n" , data->start); |
89 | g_print ("\tend: %d\n" , data->end); |
90 | #endif |
91 | } |
92 | |
93 | static void |
94 | changed (GtkEditable *editable, |
95 | EntryData *data) |
96 | { |
97 | data->serial = serial++; |
98 | data->count++; |
99 | data->text = gtk_editable_get_chars (editable, start_pos: 0, end_pos: -1); |
100 | gtk_editable_get_selection_bounds (editable, start_pos: &data->start, end_pos: &data->end); |
101 | |
102 | #if 0 |
103 | g_print ("changed\n" ); |
104 | g_print ("\ttext: %s\n" , data->text); |
105 | g_print ("\tstart: %d\n" , data->start); |
106 | g_print ("\tend: %d\n" , data->end); |
107 | #endif |
108 | } |
109 | |
110 | static void |
111 | test_insert (void) |
112 | { |
113 | GtkWidget *entry; |
114 | int pos; |
115 | EntryData data1; |
116 | EntryData data2; |
117 | EntryData data3; |
118 | EntryData data4; |
119 | EntryData data5; |
120 | EntryData data6; |
121 | |
122 | entry = gtk_entry_new (); |
123 | g_object_ref_sink (entry); |
124 | |
125 | gtk_editable_set_text (GTK_EDITABLE (entry), text: "bar" ); |
126 | gtk_editable_set_position (GTK_EDITABLE (entry), position: -1); |
127 | pos = gtk_editable_get_position (GTK_EDITABLE (entry)); |
128 | g_assert_cmpint (pos, ==, 3); |
129 | |
130 | data1.count = 0; |
131 | data2.count = 0; |
132 | data3.count = 0; |
133 | data4.count = 0; |
134 | data5.count = 0; |
135 | data6.count = 0; |
136 | g_signal_connect (entry, "notify::cursor-position" , |
137 | G_CALLBACK (notify), &data1); |
138 | g_signal_connect (entry, "notify::selection-bound" , |
139 | G_CALLBACK (notify), &data2); |
140 | g_signal_connect (entry, "notify::text" , |
141 | G_CALLBACK (notify), &data3); |
142 | g_signal_connect (entry, "insert-text" , |
143 | G_CALLBACK (insert_text), &data4); |
144 | g_signal_connect (entry, "delete-text" , |
145 | G_CALLBACK (delete_text), &data5); |
146 | g_signal_connect (entry, "changed" , |
147 | G_CALLBACK (changed), &data6); |
148 | |
149 | pos = 0; |
150 | gtk_editable_insert_text (GTK_EDITABLE (entry), text: "foo" , length: -1, position: &pos); |
151 | g_assert_cmpint (pos, ==, 3); |
152 | |
153 | pos = gtk_editable_get_position (GTK_EDITABLE (entry)); |
154 | g_assert_cmpint (pos, ==, 6); |
155 | |
156 | /* Check that notification for ::text, ::cursor-position and |
157 | * ::selection-bound happens in a consistent state after the |
158 | * change. |
159 | */ |
160 | g_assert_cmpint (data1.count, ==, 1); |
161 | g_assert_cmpint (data1.start, ==, 6); |
162 | g_assert_cmpint (data1.end, ==, 6); |
163 | g_assert_cmpstr (data1.text, ==, "foobar" ); |
164 | g_free (mem: data1.text); |
165 | |
166 | g_assert_cmpint (data2.count, ==, 1); |
167 | g_assert_cmpint (data2.start, ==, 6); |
168 | g_assert_cmpint (data2.end, ==, 6); |
169 | g_assert_cmpstr (data2.text, ==, "foobar" ); |
170 | g_free (mem: data2.text); |
171 | |
172 | g_assert_cmpint (data3.count, ==, 1); |
173 | g_assert_cmpint (data3.start, ==, 6); |
174 | g_assert_cmpint (data3.end, ==, 6); |
175 | g_assert_cmpstr (data3.text, ==, "foobar" ); |
176 | g_free (mem: data3.text); |
177 | |
178 | /* Check that ::insert-text sees the state _before_ the insertion */ |
179 | g_assert_cmpint (data4.count, ==, 1); |
180 | g_assert_cmpint (data4.start, ==, 3); |
181 | g_assert_cmpint (data4.end, ==, 3); |
182 | g_assert_cmpstr (data4.text, ==, "bar" ); |
183 | g_assert_cmpint (data4.position, ==, 0); |
184 | g_assert_cmpint (data4.length, ==, 3); |
185 | g_assert_cmpstr (data4.new_text, ==, "foo" ); |
186 | g_free (mem: data4.text); |
187 | g_free (mem: data4.new_text); |
188 | |
189 | /* no deletion here */ |
190 | g_assert_cmpint (data5.count, ==, 0); |
191 | |
192 | /* Check that ::changed sees the post-change state */ |
193 | g_assert_cmpint (data6.count, ==, 1); |
194 | g_assert_cmpint (data6.start, ==, 6); |
195 | g_assert_cmpint (data6.end, ==, 6); |
196 | g_assert_cmpstr (data6.text, ==, "foobar" ); |
197 | g_free (mem: data6.text); |
198 | |
199 | /* Now check ordering: ::insert-text comes before ::notify */ |
200 | g_assert_cmpint (data4.serial, <, data1.serial); |
201 | g_assert_cmpint (data4.serial, <, data2.serial); |
202 | g_assert_cmpint (data4.serial, <, data3.serial); |
203 | |
204 | /* ... and ::changed comes after ::notify */ |
205 | g_assert_cmpint (data6.serial, >, data1.serial); |
206 | g_assert_cmpint (data6.serial, >, data2.serial); |
207 | g_assert_cmpint (data6.serial, >, data3.serial); |
208 | |
209 | g_object_unref (object: entry); |
210 | } |
211 | |
212 | static void |
213 | test_delete (void) |
214 | { |
215 | GtkWidget *entry; |
216 | int pos; |
217 | EntryData data1; |
218 | EntryData data2; |
219 | EntryData data3; |
220 | EntryData data4; |
221 | EntryData data5; |
222 | EntryData data6; |
223 | |
224 | entry = gtk_entry_new (); |
225 | g_object_ref_sink (entry); |
226 | |
227 | gtk_editable_set_text (GTK_EDITABLE (entry), text: "foobar" ); |
228 | gtk_editable_set_position (GTK_EDITABLE (entry), position: -1); |
229 | pos = gtk_editable_get_position (GTK_EDITABLE (entry)); |
230 | g_assert_cmpint (pos, ==, 6); |
231 | |
232 | data1.count = 0; |
233 | data2.count = 0; |
234 | data3.count = 0; |
235 | data4.count = 0; |
236 | data5.count = 0; |
237 | data6.count = 0; |
238 | g_signal_connect (entry, "notify::cursor-position" , |
239 | G_CALLBACK (notify), &data1); |
240 | g_signal_connect (entry, "notify::selection-bound" , |
241 | G_CALLBACK (notify), &data2); |
242 | g_signal_connect (entry, "notify::text" , |
243 | G_CALLBACK (notify), &data3); |
244 | g_signal_connect (entry, "insert-text" , |
245 | G_CALLBACK (insert_text), &data4); |
246 | g_signal_connect (entry, "delete-text" , |
247 | G_CALLBACK (delete_text), &data5); |
248 | g_signal_connect (entry, "changed" , |
249 | G_CALLBACK (changed), &data6); |
250 | |
251 | gtk_editable_delete_text (GTK_EDITABLE (entry), start_pos: 0, end_pos: 3); |
252 | |
253 | pos = gtk_editable_get_position (GTK_EDITABLE (entry)); |
254 | g_assert_cmpint (pos, ==, 3); |
255 | |
256 | /* Check that notification for ::text, ::cursor-position and |
257 | * ::selection-bound happens in a consistent state after the |
258 | * change. |
259 | */ |
260 | g_assert_cmpint (data1.count, ==, 1); |
261 | g_assert_cmpint (data1.start, ==, 3); |
262 | g_assert_cmpint (data1.end, ==, 3); |
263 | g_assert_cmpstr (data1.text, ==, "bar" ); |
264 | g_free (mem: data1.text); |
265 | |
266 | g_assert_cmpint (data2.count, ==, 1); |
267 | g_assert_cmpint (data2.start, ==, 3); |
268 | g_assert_cmpint (data2.end, ==, 3); |
269 | g_assert_cmpstr (data2.text, ==, "bar" ); |
270 | g_free (mem: data2.text); |
271 | |
272 | g_assert_cmpint (data3.count, ==, 1); |
273 | g_assert_cmpint (data3.start, ==, 3); |
274 | g_assert_cmpint (data3.end, ==, 3); |
275 | g_assert_cmpstr (data3.text, ==, "bar" ); |
276 | g_free (mem: data3.text); |
277 | |
278 | /* no insertion here */ |
279 | g_assert_cmpint (data4.count, ==, 0); |
280 | |
281 | /* Check that ::delete-text sees the state _before_ the insertion */ |
282 | g_assert_cmpint (data5.count, ==, 1); |
283 | g_assert_cmpint (data5.start, ==, 6); |
284 | g_assert_cmpint (data5.end, ==, 6); |
285 | g_assert_cmpstr (data5.text, ==, "foobar" ); |
286 | g_assert_cmpint (data5.position, ==, 0); |
287 | g_assert_cmpint (data5.length, ==, 3); |
288 | g_free (mem: data5.text); |
289 | |
290 | /* Check that ::changed sees the post-change state */ |
291 | g_assert_cmpint (data6.count, ==, 1); |
292 | g_assert_cmpint (data6.start, ==, 3); |
293 | g_assert_cmpint (data6.end, ==, 3); |
294 | g_assert_cmpstr (data6.text, ==, "bar" ); |
295 | g_free (mem: data6.text); |
296 | |
297 | /* Now check ordering: ::delete-text comes before ::notify */ |
298 | g_assert_cmpint (data5.serial, <, data1.serial); |
299 | g_assert_cmpint (data5.serial, <, data2.serial); |
300 | g_assert_cmpint (data5.serial, <, data3.serial); |
301 | |
302 | /* ... and ::changed comes after ::notify */ |
303 | g_assert_cmpint (data6.serial, >, data1.serial); |
304 | g_assert_cmpint (data6.serial, >, data2.serial); |
305 | g_assert_cmpint (data6.serial, >, data3.serial); |
306 | g_object_unref (object: entry); |
307 | } |
308 | |
309 | int |
310 | main (int argc, |
311 | char *argv[]) |
312 | { |
313 | gtk_test_init (argcp: &argc, argvp: &argv); |
314 | |
315 | g_test_add_func (testpath: "/entry/delete" , test_func: test_delete); |
316 | g_test_add_func (testpath: "/entry/insert" , test_func: test_insert); |
317 | |
318 | return g_test_run(); |
319 | } |
320 | |