1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; coding: utf-8 -*-
2 *
3 * This file tests the GtkImageToolSelector class.
4 **/
5 #include <src/gtkimagetoolselector.h>
6 #include <assert.h>
7 #include "testlib/testlib.h"
8
9 static GtkImageView *view = NULL;
10 // Use two global variables to avoid castings.
11 static GtkIImageTool *tool = NULL;
12 static GtkImageToolSelector *selector = NULL;
13
14 static void
setup()15 setup ()
16 {
17 view = GTK_IMAGE_VIEW (gtk_image_view_new ());
18 g_object_ref (view);
19 gtk_object_sink (GTK_OBJECT (view));
20 fake_realize (GTK_WIDGET (view));
21
22 tool = gtk_image_tool_selector_new (view);
23 selector = GTK_IMAGE_TOOL_SELECTOR (tool);
24 gtk_image_view_set_tool (view, tool);
25 }
26
27 static void
teardown()28 teardown ()
29 {
30 g_object_unref (selector);
31 gtk_widget_destroy (GTK_WIDGET (view));
32 g_object_unref (view);
33 }
34
35 /**
36 * test_null_image_view:
37 *
38 * Ensure that an error is printed if the view argument to the
39 * constructor is NULL.
40 **/
41 static void
test_null_image_view()42 test_null_image_view ()
43 {
44 printf ("test_null_image_view (one error message expected)\n");
45 GtkIImageTool *tool = gtk_image_tool_selector_new (NULL);
46 assert (!tool);
47 }
48
49 /**
50 * test_default_selection:
51 *
52 * Ensure that the default selection rectangle is (0, 0), (0, 0).
53 **/
54 static void
test_default_selection()55 test_default_selection ()
56 {
57 printf ("test_default_selection\n");
58 setup ();
59 GdkRectangle sel;
60 gtk_image_tool_selector_get_selection (selector, &sel);
61 assert (gdk_rectangle_eq (sel, (GdkRectangle){0, 0, 0, 0}));
62 teardown ();
63 }
64
65 /**
66 * test_tool_ref_count:
67 *
68 * Ensure that the tool has the correct number of references.
69 **/
70 static void
test_tool_ref_count()71 test_tool_ref_count ()
72 {
73 printf ("test_tool_ref_count\n");
74 setup ();
75
76 // The selector has an implied reference and the view
77 assert (G_OBJECT (selector)->ref_count == 2);
78
79 teardown ();
80 }
81
82 /**
83 * test_get_set_selection:
84 *
85 * Ensure that getting and setting the selection rectangle works when
86 * the view shows a pixbuf.
87 **/
88 static void
test_get_set_selection()89 test_get_set_selection ()
90 {
91 printf ("test_get_set_selection\n");
92 setup ();
93 GdkPixbuf *pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 50, 50);
94 gtk_image_view_set_pixbuf (view, pixbuf, FALSE);
95 GdkRectangle sel = {10, 11, 12, 13};
96 gtk_image_tool_selector_set_selection (selector, &sel);
97 GdkRectangle ret;
98 gtk_image_tool_selector_get_selection (selector, &ret);
99 assert (gdk_rectangle_eq (ret, (GdkRectangle){10, 11, 12, 13}));
100 g_object_unref (pixbuf);
101 teardown();
102 }
103
104 /**
105 * test_set_selection_no_pixbuf:
106 *
107 * Ensure that setting the selection when there is no pixbuf in the
108 * view works. The selection should not be modified.
109 **/
110 static void
test_set_selection_no_pixbuf()111 test_set_selection_no_pixbuf ()
112 {
113 printf ("test_set_selection_no_pixbuf\n");
114 setup ();
115 GdkRectangle sel = {10, 11, 12, 13};
116 gtk_image_view_set_pixbuf (view, NULL, FALSE);
117 gtk_image_tool_selector_set_selection (selector, &sel);
118
119 gtk_image_tool_selector_get_selection (selector, &sel);
120 assert (sel.x == 0 && sel.y == 0 && sel.width == 0 && sel.height == 0);
121 teardown ();
122 }
123
124 /**
125 * test_set_selection_to_big:
126 *
127 * Ensure that setting a to big selection rectangle leaves the
128 * selection rectangle untouched.
129 **/
130 static void
test_set_selection_to_big()131 test_set_selection_to_big ()
132 {
133 printf ("test_selection_to_big\n");
134 setup ();
135 GdkPixbuf *pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 20, 20);
136 gtk_image_view_set_pixbuf (view, pixbuf, TRUE);
137
138 GdkRectangle good_sel = {0, 0, 10, 10};
139 gtk_image_tool_selector_set_selection (selector, &good_sel);
140
141 GdkRectangle bad_sel = {0, 0, 50, 50};
142 gtk_image_tool_selector_set_selection (selector, &bad_sel);
143
144 GdkRectangle curr_sel;
145 gtk_image_tool_selector_get_selection (selector, &curr_sel);
146 assert (gdk_rectangle_eq (curr_sel, (GdkRectangle){0, 0, 10, 10}));
147
148 g_object_unref (pixbuf);
149 teardown ();
150 }
151
152 /**
153 * test_set_selection_outside_pixbuf:
154 *
155 * Ensure that a selection rectangle outside the pixbuf is moved back
156 * in.
157 **/
158 static void
test_set_selection_outside_pixbuf()159 test_set_selection_outside_pixbuf ()
160 {
161 printf ("test_set_selection_outside_pixbuf\n");
162 setup ();
163 GdkPixbuf *pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 20, 20);
164 gtk_image_view_set_pixbuf (view, pixbuf, TRUE);
165
166 GdkRectangle good_sel = {0, 0, 10, 10};
167 gtk_image_tool_selector_set_selection (selector, &good_sel);
168
169 GdkRectangle bad_sel = {15, 15, 10, 10};
170 gtk_image_tool_selector_set_selection (selector, &bad_sel);
171
172 GdkRectangle curr_sel;
173 gtk_image_tool_selector_get_selection (selector, &curr_sel);
174 assert (gdk_rectangle_eq (curr_sel, (GdkRectangle){10, 10, 10, 10}));
175
176 g_object_unref (pixbuf);
177 teardown ();
178 }
179
180 /**
181 * test_motion_notify:
182 *
183 * Ensure that the selector handles motion notifies.
184 **/
185 static void
test_motion_notify()186 test_motion_notify ()
187 {
188 printf ("test_lookup_cursors\n");
189 setup ();
190 GdkEventMotion ev = {.x = 10, .y = 10};
191 assert (!gtk_iimage_tool_motion_notify (tool, &ev));
192 teardown();
193 }
194
195 /**
196 * test_motion_notify_in_selection:
197 *
198 * Ensure that the selector handles a motion notify occuring inside
199 * the selection.
200 **/
201 static void
test_motion_notify_in_selection()202 test_motion_notify_in_selection ()
203 {
204 printf ("test_motion_notify_in_selection\n");
205 setup ();
206
207 // Fake an allocation
208 GTK_WIDGET (view)->allocation = (GtkAllocation){0, 0, 40, 40};
209
210 GdkPixbuf *pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 20, 20);
211 gtk_image_view_set_pixbuf (view, pixbuf, TRUE);
212
213 GdkRectangle sel_rect = {0, 0, 20, 20};
214 gtk_image_tool_selector_set_selection (selector, &sel_rect);
215
216 // Put the pointer inside the selection area.
217 GdkEventMotion ev = {.x = 20, .y = 20};
218 assert (!gtk_iimage_tool_motion_notify (tool, &ev));
219 g_object_unref (pixbuf);
220 teardown ();
221 }
222
223 /**
224 * test_set_selection_on_unrealized_view:
225 *
226 * The objective of this test is to verify that setting the selection
227 * on a selector tool with an unrealized view works as expected.
228 **/
229 static void
test_set_selection_on_unrealized_view()230 test_set_selection_on_unrealized_view ()
231 {
232 printf ("test_set_selection_on_unrealized_view\n");
233 GtkImageView *view2 = GTK_IMAGE_VIEW (gtk_image_view_new ());
234 g_object_ref (view2);
235 gtk_object_sink (GTK_OBJECT (view2));
236 tool = gtk_image_tool_selector_new (view2);
237 selector = GTK_IMAGE_TOOL_SELECTOR (tool);
238
239 gtk_image_view_set_tool (view2, tool);
240 GdkPixbuf *pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 20, 20);
241 gtk_image_view_set_pixbuf (view2, pixbuf, TRUE);
242
243 GdkRectangle sel = {10, 10, 10, 10};
244
245 gtk_image_tool_selector_set_selection (selector, &sel);
246
247 g_object_unref (pixbuf);
248 g_object_unref (selector);
249 gtk_widget_destroy (GTK_WIDGET (view2));
250 g_object_unref (view2);
251 }
252
253 /**
254 * test_motion_notify_keeps_accuracy:
255 *
256 * Motion notify of #GtkImageToolSelector must remember the initial
257 * zoom space position of the mouse pointer. Because the geometry of
258 * the selection rectangle is stored in image space coordinates, it
259 * means that multiple motion notify events that defines drags that
260 * are shorter must be recorded somehow.
261 *
262 * The easiest way to accomplish that is to remember the position
263 * where the drag began.
264 **/
265 static void
test_motion_notify_keeps_accuracy()266 test_motion_notify_keeps_accuracy ()
267 {
268 printf ("test_motion_notify_keeps_accuracy\n");
269 setup ();
270
271 GTK_WIDGET (view)->allocation = (GtkAllocation){0, 0, 500, 500};
272
273 GdkPixbuf *pixbuf =
274 gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 100, 100);
275 gtk_image_view_set_pixbuf (view, pixbuf, TRUE);
276 gtk_image_view_set_zoom (view, 5.0);
277
278 // Set the initial selection
279 GdkRectangle sel;
280 sel = (GdkRectangle){10, 10, 50, 50};
281 gtk_image_tool_selector_set_selection (selector, &sel);
282
283 // Fake drag the selection some
284 GtkWidget *widget = GTK_WIDGET (view);
285 GtkWidgetClass *wid_class = GTK_WIDGET_GET_CLASS (widget);
286 GdkEventMotion motion_ev;
287
288 // First, fake a button click in the selection at widget
289 // coordinates (100, 100) which is inside the selection.
290 GdkEventButton button_ev = {.button = 1,
291 .window = GTK_WIDGET (view)->window,
292 .x = 100, .y = 100};
293 wid_class->button_press_event (widget, &button_ev);
294
295 // Then a motion event at (103, 105)
296 motion_ev = (GdkEventMotion){.x = 103, .y = 105};
297 wid_class->motion_notify_event (widget, &motion_ev);
298
299 // Since 100 // 5 == 103 // 5, the selection should not move in
300 // the x direction. But should move one pixel downwards in the y
301 // direction.
302 gtk_image_tool_selector_get_selection (selector, &sel);
303 assert (sel.x == 10 && sel.y == 11);
304
305 // The another mtoin event at (106, 110)
306 motion_ev = (GdkEventMotion){.x = 106, .y = 110};
307 wid_class->motion_notify_event (widget, &motion_ev);
308
309 // 6 // 5 = 1 and 10 // 5 = 2, so the selection should now have
310 // moved one pixel to the right and two pixels downwards from its
311 // original position.
312 gtk_image_tool_selector_get_selection (selector, &sel);
313 assert (sel.x == 11 && sel.y == 12);
314
315 g_object_unref (pixbuf);
316 teardown ();
317 }
318
319 /**
320 * test_button_press_before_realize:
321 *
322 * Ensure that the selector can handle button press events even before
323 * its view has been realized.
324 **/
325 static void
test_button_press_before_realize()326 test_button_press_before_realize ()
327 {
328 printf ("test_button_press_before_realize\n");
329 setup ();
330 GTK_WIDGET (view)->allocation = (GtkAllocation){0, 0, 10, 10};
331 GdkPixbuf *pixbuf =
332 gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 10, 10);
333 gtk_image_view_set_pixbuf (view, pixbuf, TRUE);
334 GdkEventButton ev = {.button = 1,
335 .window = GTK_WIDGET (view)->window,
336 .x = 5, .y = 5};
337 gtk_iimage_tool_button_press (tool, &ev);
338 g_object_unref (pixbuf);
339 teardown ();
340 }
341
342 /**
343 * test_cursor_at_point_inside_selection:
344 *
345 * Ensure that the correct cursor is returned when
346 * gtk_iimage_tool_cursor_at_point() is called with a position inside
347 * the selection.
348 **/
349 static void
test_cursor_at_point_inside_selection()350 test_cursor_at_point_inside_selection ()
351 {
352 printf ("test_cursor_at_point_inside_selection\n");
353 setup ();
354 GTK_WIDGET (view)->allocation = (GtkAllocation){0, 0, 50, 50};
355 GdkPixbuf *pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 90, 90);
356 gtk_image_view_set_pixbuf (view, pixbuf, FALSE);
357
358 gtk_image_view_set_zoom (view, 1.0);
359 GdkRectangle sel = {0, 0, 90, 90};
360 gtk_image_tool_selector_set_selection (selector, &sel);
361 GdkCursor *cursor = gtk_iimage_tool_cursor_at_point (tool, 0, 0);
362 assert (cursor);
363
364 g_object_unref (pixbuf);
365 teardown ();
366 }
367
368 /**
369 * test_cursor_at_point_on_null_pixbuf:
370 *
371 * Ensure that the cursor returned is %NULL if there is no pixbuf in
372 * the view.
373 **/
374 static void
test_cursor_at_point_on_null_pixbuf()375 test_cursor_at_point_on_null_pixbuf ()
376 {
377 printf ("test_cursor_at_point_on_null_pixbuf\n");
378 setup ();
379 GdkCursor *cursor = gtk_iimage_tool_cursor_at_point (tool, 10, 10);
380 assert (!cursor);
381 teardown ();
382 }
383
384 /**
385 * test_cursor_outside_widget:
386 *
387 * Ensure that the cursor returned is %NULL when the coordinate is
388 * outside the widget.
389 **/
390 static void
test_cursor_outside_widget()391 test_cursor_outside_widget ()
392 {
393 printf ("test_cursor_outside_widget\n");
394 setup ();
395 GTK_WIDGET (view)->allocation = (GtkAllocation){0, 0, 50, 50};
396 GdkPixbuf *pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 90, 90);
397 gtk_image_view_set_pixbuf (view, pixbuf, FALSE);
398
399 int coords[] = {
400 250, 250,
401 250, 25,
402 -10, -10
403 };
404 for (int n = 0; n < G_N_ELEMENTS(coords); n += 2)
405 {
406 int x = coords[n];
407 int y = coords[n + 1];
408 GdkCursor *cursor = gtk_iimage_tool_cursor_at_point (tool, x, y);
409 assert (!cursor);
410 }
411 g_object_unref (pixbuf);
412 teardown ();
413 }
414
415 /**
416 * test_selection_after_pixbuf_change:
417 *
418 * Ensure that the selection becomes (0,0)-[0,0] when the pixbuf is
419 * changed if reset_fit is %TRUE.
420 **/
421 static void
test_selection_after_pixbuf_change()422 test_selection_after_pixbuf_change ()
423 {
424 printf ("test_selection_after_pixbuf_change\n");
425 setup ();
426 GdkRectangle rect;
427
428 GdkPixbuf *pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 90, 90);
429 gtk_image_view_set_pixbuf (view, pixbuf, TRUE);
430 gtk_image_tool_selector_get_selection (selector, &rect);
431 assert (gdk_rectangle_eq (rect, (GdkRectangle){0, 0, 0, 0}));
432
433 rect = (GdkRectangle){1, 2, 3, 4};
434 gtk_image_tool_selector_set_selection (selector, &rect);
435 gtk_image_view_set_pixbuf (view, NULL, TRUE);
436 gtk_image_tool_selector_get_selection (selector, &rect);
437 assert (gdk_rectangle_eq (rect, (GdkRectangle){0, 0, 0, 0}));
438
439 g_object_unref (pixbuf);
440 teardown ();
441 }
442
443 int
main(int argc,char * argv[])444 main (int argc,
445 char *argv[])
446 {
447 gtk_init (&argc, &argv);
448 test_null_image_view ();
449 test_default_selection ();
450 test_tool_ref_count ();
451 test_get_set_selection ();
452 test_set_selection_no_pixbuf ();
453 test_set_selection_to_big ();
454 test_set_selection_outside_pixbuf ();
455 test_motion_notify ();
456 test_motion_notify_in_selection ();
457 test_set_selection_on_unrealized_view ();
458 test_motion_notify_keeps_accuracy ();
459 test_button_press_before_realize ();
460 test_cursor_at_point_inside_selection ();
461 test_cursor_at_point_on_null_pixbuf ();
462 test_cursor_outside_widget ();
463 test_selection_after_pixbuf_change ();
464 printf ("16 tests passed.\n");
465 }
466