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
notify(GtkEditable * editable,GParamSpec * pspec,EntryData * data)34 notify (GtkEditable *editable, GParamSpec *pspec, EntryData *data)
35 {
36 data->serial = serial++;
37 data->count++;
38 data->text = gtk_editable_get_chars (editable, 0, -1);
39 gtk_editable_get_selection_bounds (editable, &data->start, &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
insert_text(GtkEditable * editable,const char * new_text,int new_text_length,int * position,EntryData * data)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, 0, -1);
59 gtk_editable_get_selection_bounds (editable, &data->start, &data->end);
60 data->new_text = g_strdup (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
delete_text(GtkEditable * editable,int start_pos,int end_pos,EntryData * data)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, 0, -1);
81 gtk_editable_get_selection_bounds (editable, &data->start, &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
changed(GtkEditable * editable,EntryData * data)94 changed (GtkEditable *editable,
95 EntryData *data)
96 {
97 data->serial = serial++;
98 data->count++;
99 data->text = gtk_editable_get_chars (editable, 0, -1);
100 gtk_editable_get_selection_bounds (editable, &data->start, &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
test_insert(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), "bar");
126 gtk_editable_set_position (GTK_EDITABLE (entry), -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), "foo", -1, &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 (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 (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 (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 (data4.text);
187 g_free (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 (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 (entry);
210 }
211
212 static void
test_delete(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), "foobar");
228 gtk_editable_set_position (GTK_EDITABLE (entry), -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), 0, 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 (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 (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 (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 (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 (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 (entry);
307 }
308
309 int
main(int argc,char * argv[])310 main (int argc,
311 char *argv[])
312 {
313 gtk_test_init (&argc, &argv);
314
315 g_test_add_func ("/entry/delete", test_delete);
316 g_test_add_func ("/entry/insert", test_insert);
317
318 return g_test_run();
319 }
320