/* PIKA - Photo and Image Kooker Application * a rebranding of The GNU Image Manipulation Program (created with heckimp) * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio * * Original copyright, applying to most contents (license remains unchanged): * Copyright (C) 2009 Martin Nordholts * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include "libpikabase/pikabase.h" #include "libpikamath/pikamath.h" #include "libpikawidgets/pikawidgets.h" #include "tools/tools-types.h" #include "tools/pikarectangleoptions.h" #include "tools/tool_manager.h" #include "display/pikadisplay.h" #include "display/pikadisplayshell.h" #include "display/pikadisplayshell-callbacks.h" #include "display/pikadisplayshell-scale.h" #include "display/pikadisplayshell-tool-events.h" #include "display/pikadisplayshell-transform.h" #include "display/pikaimagewindow.h" #include "widgets/pikadialogfactory.h" #include "widgets/pikadock.h" #include "widgets/pikadockable.h" #include "widgets/pikadockbook.h" #include "widgets/pikadocked.h" #include "widgets/pikadockwindow.h" #include "widgets/pikahelp-ids.h" #include "widgets/pikasessioninfo.h" #include "widgets/pikatoolbox.h" #include "widgets/pikatooloptionseditor.h" #include "widgets/pikauimanager.h" #include "widgets/pikawidgets-utils.h" #include "core/pika.h" #include "core/pikachannel.h" #include "core/pikacontext.h" #include "core/pikaimage.h" #include "core/pikalayer.h" #include "core/pikatoolinfo.h" #include "core/pikatooloptions.h" #include "pikacoreapp.h" #include "pika-app-test-utils.h" #include "tests.h" #define PIKA_TEST_IMAGE_WIDTH 150 #define PIKA_TEST_IMAGE_HEIGHT 267 /* Put this in the code below when you want the test to pause so you * can do measurements of widgets on the screen for example */ #define PIKA_PAUSE (g_usleep (2 * 1000 * 1000)) #define ADD_TEST(function) \ g_test_add ("/pika-tools/" #function, \ PikaTestFixture, \ pika, \ pika_tools_setup_image, \ function, \ pika_tools_teardown_image); typedef struct { int avoid_sizeof_zero; } PikaTestFixture; static void pika_tools_setup_image (PikaTestFixture *fixture, gconstpointer data); static void pika_tools_teardown_image (PikaTestFixture *fixture, gconstpointer data); static void pika_tools_synthesize_image_click_drag_release (PikaDisplayShell *shell, gdouble start_image_x, gdouble start_image_y, gdouble end_image_x, gdouble end_image_y, gint button, GdkModifierType modifiers); static PikaDisplay * pika_test_get_only_display (Pika *pika); static PikaImage * pika_test_get_only_image (Pika *pika); static PikaDisplayShell * pika_test_get_only_display_shell (Pika *pika); static void pika_tools_setup_image (PikaTestFixture *fixture, gconstpointer data) { Pika *pika = PIKA (data); pika_test_utils_create_image (pika, PIKA_TEST_IMAGE_WIDTH, PIKA_TEST_IMAGE_HEIGHT); pika_test_run_mainloop_until_idle (); } static void pika_tools_teardown_image (PikaTestFixture *fixture, gconstpointer data) { Pika *pika = PIKA (data); g_object_unref (pika_test_get_only_image (pika)); pika_display_close (pika_test_get_only_display (pika)); pika_test_run_mainloop_until_idle (); } /** * pika_tools_set_tool: * @pika: * @tool_id: * @display: * * Makes sure the given tool is the active tool and that the passed * display is the focused tool display. **/ static void pika_tools_set_tool (Pika *pika, const gchar *tool_id, PikaDisplay *display) { /* Activate tool and setup active display for the new tool */ pika_context_set_tool (pika_get_user_context (pika), pika_get_tool_info (pika, tool_id)); tool_manager_focus_display_active (pika, display); } /** * pika_test_get_only_display: * @pika: * * Asserts that there only is one image and display and then * returns the display. * * Returns: The #PikaDisplay. **/ static PikaDisplay * pika_test_get_only_display (Pika *pika) { g_assert (g_list_length (pika_get_image_iter (pika)) == 1); g_assert (g_list_length (pika_get_display_iter (pika)) == 1); return PIKA_DISPLAY (pika_get_display_iter (pika)->data); } /** * pika_test_get_only_display_shell: * @pika: * * Asserts that there only is one image and display shell and then * returns the display shell. * * Returns: The #PikaDisplayShell. **/ static PikaDisplayShell * pika_test_get_only_display_shell (Pika *pika) { return pika_display_get_shell (pika_test_get_only_display (pika)); } /** * pika_test_get_only_image: * @pika: * * Asserts that there is only one image and returns that. * * Returns: The #PikaImage. **/ static PikaImage * pika_test_get_only_image (Pika *pika) { g_assert (g_list_length (pika_get_image_iter (pika)) == 1); g_assert (g_list_length (pika_get_display_iter (pika)) == 1); return PIKA_IMAGE (pika_get_image_iter (pika)->data); } static void pika_test_synthesize_tool_button_event (PikaDisplayShell *shell, gint x, gint y, gint button, gint modifiers, GdkEventType button_event_type) { GdkEvent *event = gdk_event_new (button_event_type); GdkWindow *window = gtk_widget_get_window (GTK_WIDGET (shell->canvas)); GdkDisplay *display = gdk_window_get_display (window); GdkSeat *seat = gdk_display_get_default_seat (display); g_assert (button_event_type == GDK_BUTTON_PRESS || button_event_type == GDK_BUTTON_RELEASE); gdk_event_set_device (event, gdk_seat_get_pointer (seat)); event->button.window = g_object_ref (window); event->button.send_event = TRUE; event->button.time = gtk_get_current_event_time (); event->button.x = x; event->button.y = y; event->button.axes = NULL; event->button.state = 0; event->button.button = button; event->button.x_root = -1; event->button.y_root = -1; pika_display_shell_canvas_tool_events (shell->canvas, event, shell); gdk_event_free (event); } static void pika_test_synthesize_tool_motion_event (PikaDisplayShell *shell, gint x, gint y, gint modifiers) { GdkEvent *event = gdk_event_new (GDK_MOTION_NOTIFY); GdkWindow *window = gtk_widget_get_window (GTK_WIDGET (shell->canvas)); GdkDisplay *display = gdk_window_get_display (window); GdkSeat *seat = gdk_display_get_default_seat (display); gdk_event_set_device (event, gdk_seat_get_pointer (seat)); event->motion.window = g_object_ref (window); event->motion.send_event = TRUE; event->motion.time = gtk_get_current_event_time (); event->motion.x = x; event->motion.y = y; event->motion.axes = NULL; event->motion.state = GDK_BUTTON1_MASK | modifiers; event->motion.is_hint = FALSE; event->motion.x_root = -1; event->motion.y_root = -1; pika_display_shell_canvas_tool_events (shell->canvas, event, shell); gdk_event_free (event); } static void pika_test_synthesize_tool_crossing_event (PikaDisplayShell *shell, gint x, gint y, gint modifiers, GdkEventType crossing_event_type) { GdkEvent *event = gdk_event_new (crossing_event_type); GdkWindow *window = gtk_widget_get_window (GTK_WIDGET (shell->canvas)); GdkDisplay *display = gdk_window_get_display (window); GdkSeat *seat = gdk_display_get_default_seat (display); g_assert (crossing_event_type == GDK_ENTER_NOTIFY || crossing_event_type == GDK_LEAVE_NOTIFY); gdk_event_set_device (event, gdk_seat_get_pointer (seat)); event->crossing.window = g_object_ref (window); event->crossing.send_event = TRUE; event->crossing.subwindow = NULL; event->crossing.time = gtk_get_current_event_time (); event->crossing.x = x; event->crossing.y = y; event->crossing.x_root = -1; event->crossing.y_root = -1; event->crossing.mode = GDK_CROSSING_NORMAL; event->crossing.detail = GDK_NOTIFY_UNKNOWN; event->crossing.focus = TRUE; event->crossing.state = modifiers; pika_display_shell_canvas_tool_events (shell->canvas, event, shell); gdk_event_free (event); } static void pika_tools_synthesize_image_click_drag_release (PikaDisplayShell *shell, gdouble start_image_x, gdouble start_image_y, gdouble end_image_x, gdouble end_image_y, gint button /*1..3*/, GdkModifierType modifiers) { gdouble start_canvas_x = -1.0; gdouble start_canvas_y = -1.0; gdouble middle_canvas_x = -1.0; gdouble middle_canvas_y = -1.0; gdouble end_canvas_x = -1.0; gdouble end_canvas_y = -1.0; /* Transform coordinates */ pika_display_shell_transform_xy_f (shell, start_image_x, start_image_y, &start_canvas_x, &start_canvas_y); pika_display_shell_transform_xy_f (shell, end_image_x, end_image_y, &end_canvas_x, &end_canvas_y); middle_canvas_x = (start_canvas_x + end_canvas_x) / 2; middle_canvas_y = (start_canvas_y + end_canvas_y) / 2; /* Enter notify */ pika_test_synthesize_tool_crossing_event (shell, (int)start_canvas_x, (int)start_canvas_y, modifiers, GDK_ENTER_NOTIFY); /* Button press */ pika_test_synthesize_tool_button_event (shell, (int)start_canvas_x, (int)start_canvas_y, button, modifiers, GDK_BUTTON_PRESS); /* Move events */ pika_test_synthesize_tool_motion_event (shell, (int)start_canvas_x, (int)start_canvas_y, modifiers); pika_test_synthesize_tool_motion_event (shell, (int)middle_canvas_x, (int)middle_canvas_y, modifiers); pika_test_synthesize_tool_motion_event (shell, (int)end_canvas_x, (int)end_canvas_y, modifiers); /* Button release */ pika_test_synthesize_tool_button_event (shell, (int)end_canvas_x, (int)end_canvas_y, button, modifiers, GDK_BUTTON_RELEASE); /* Leave notify */ pika_test_synthesize_tool_crossing_event (shell, (int)start_canvas_x, (int)start_canvas_y, modifiers, GDK_LEAVE_NOTIFY); /* Process them */ pika_test_run_mainloop_until_idle (); } /** * crop_tool_can_crop: * @fixture: * @data: * * Make sure it's possible to crop at all. Regression test for * "Bug 315255 - SIGSEGV, while doing a crop". **/ static void crop_tool_can_crop (PikaTestFixture *fixture, gconstpointer data) { Pika *pika = PIKA (data); PikaImage *image = pika_test_get_only_image (pika); PikaDisplayShell *shell = pika_test_get_only_display_shell (pika); gint cropped_x = 10; gint cropped_y = 10; gint cropped_w = 20; gint cropped_h = 30; /* Fit display and pause and let it stabalize (two idlings seems to * always be enough) */ pika_ui_manager_activate_action (pika_test_utils_get_ui_manager (pika), "view", "view-shrink-wrap"); pika_test_run_mainloop_until_idle (); pika_test_run_mainloop_until_idle (); /* Activate crop tool */ pika_tools_set_tool (pika, "pika-crop-tool", shell->display); /* Do the crop rect */ pika_tools_synthesize_image_click_drag_release (shell, cropped_x, cropped_y, cropped_x + cropped_w, cropped_y + cropped_h, 1 /*button*/, 0 /*modifiers*/); /* Crop */ pika_test_utils_synthesize_key_event (GTK_WIDGET (shell), GDK_KEY_Return); pika_test_run_mainloop_until_idle (); /* Make sure the new image has the expected size */ g_assert_cmpint (cropped_w, ==, pika_image_get_width (image)); g_assert_cmpint (cropped_h, ==, pika_image_get_height (image)); } /** * crop_tool_can_crop: * @fixture: * @data: * * Make sure it's possible to change width of crop rect in tool * options without there being a pending rectangle. Regression test * for "Bug 322396 - Crop dimension entering causes crash". **/ static void crop_set_width_without_pending_rect (PikaTestFixture *fixture, gconstpointer data) { Pika *pika = PIKA (data); PikaDisplay *display = pika_test_get_only_display (pika); PikaToolInfo *tool_info; PikaRectangleOptions *rectangle_options; GtkWidget *size_entry; /* Activate crop tool */ pika_tools_set_tool (pika, "pika-crop-tool", display); /* Get tool options */ tool_info = pika_get_tool_info (pika, "pika-crop-tool"); rectangle_options = PIKA_RECTANGLE_OPTIONS (tool_info->tool_options); /* Find 'Width' or 'Height' GtkTextEntry in tool options */ size_entry = pika_rectangle_options_get_size_entry (rectangle_options); /* Set arbitrary non-0 value */ pika_size_entry_set_value (PIKA_SIZE_ENTRY (size_entry), 0 /*field*/, 42.0 /*lower*/); /* If we don't crash, everything s fine */ } int main(int argc, char **argv) { Pika *pika = NULL; gint result = -1; pika_test_bail_if_no_display (); gtk_test_init (&argc, &argv, NULL); pika_test_utils_setup_menus_path (); /* Start up PIKA */ pika = pika_init_for_gui_testing (TRUE /*show_gui*/); pika_test_run_mainloop_until_idle (); /* Add tests */ ADD_TEST (crop_tool_can_crop); ADD_TEST (crop_set_width_without_pending_rect); /* Run the tests and return status */ g_application_run (pika->app, 0, NULL); result = pika_core_app_get_exit_status (PIKA_CORE_APP (pika->app)); g_application_quit (G_APPLICATION (pika->app)); g_clear_object (&pika->app); return result; }