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
20static int serial = 0;
21
22typedef 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
33static void
34notify (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
49static void
50insert_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
72static void
73delete_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
93static void
94changed (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
110static void
111test_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
212static void
213test_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
309int
310main (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

source code of gtk/testsuite/gtk/entry.c