499 lines
18 KiB
C
499 lines
18 KiB
C
/* 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 <martinn@src.gnome.org>
|
|
*
|
|
* 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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <gegl.h>
|
|
#include <gtk/gtk.h>
|
|
#include <gdk/gdkkeysyms.h>
|
|
|
|
#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;
|
|
}
|