summaryrefslogtreecommitdiffstats
path: root/app/widgets/gimpcursor.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/widgets/gimpcursor.c')
-rw-r--r--app/widgets/gimpcursor.c526
1 files changed, 526 insertions, 0 deletions
diff --git a/app/widgets/gimpcursor.c b/app/widgets/gimpcursor.c
new file mode 100644
index 0000000..7509f48
--- /dev/null
+++ b/app/widgets/gimpcursor.c
@@ -0,0 +1,526 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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 "config.h"
+
+#include <gtk/gtk.h>
+
+#include "widgets-types.h"
+
+#include "gimpcursor.h"
+#include "gimpwidgets-utils.h"
+
+#include "cursors/gimp-tool-cursors.c"
+
+
+#define cursor_default_hot_x 10
+#define cursor_default_hot_y 10
+
+#define cursor_mouse_hot_x 3
+#define cursor_mouse_hot_y 2
+#define cursor_crosshair_hot_x 15
+#define cursor_crosshair_hot_y 15
+#define cursor_zoom_hot_x 8
+#define cursor_zoom_hot_y 8
+#define cursor_color_picker_hot_x 1
+#define cursor_color_picker_hot_y 30
+
+
+typedef struct _GimpCursor GimpCursor;
+
+struct _GimpCursor
+{
+ const gchar *resource_name;
+ const gint hot_x;
+ const gint hot_y;
+
+ GdkPixbuf *pixbuf;
+ GdkPixbuf *pixbuf_x2;
+};
+
+
+static GimpCursor gimp_cursors[] =
+{
+ /* these have to match up with enum GimpCursorType in widgets-enums.h */
+
+ {
+ "cursor-none",
+ cursor_default_hot_x, cursor_default_hot_y
+ },
+ {
+ "cursor-mouse",
+ cursor_mouse_hot_x, cursor_mouse_hot_y
+ },
+ {
+ "cursor-crosshair",
+ cursor_crosshair_hot_x, cursor_crosshair_hot_y
+ },
+ {
+ "cursor-crosshair-small",
+ cursor_default_hot_x, cursor_default_hot_y
+ },
+ {
+ "cursor-bad",
+ cursor_default_hot_x, cursor_default_hot_y
+ },
+ {
+ "cursor-move",
+ cursor_default_hot_x, cursor_default_hot_y
+ },
+ {
+ "cursor-zoom",
+ cursor_zoom_hot_x, cursor_zoom_hot_y
+ },
+ {
+ "cursor-color-picker",
+ cursor_color_picker_hot_x, cursor_color_picker_hot_y
+ },
+ {
+ "cursor-corner-top",
+ cursor_default_hot_x, cursor_default_hot_y
+ },
+ {
+ "cursor-corner-top-right",
+ cursor_default_hot_x, cursor_default_hot_y
+ },
+ {
+ "cursor-corner-right",
+ cursor_default_hot_x, cursor_default_hot_y
+ },
+ {
+ "cursor-corner-bottom-right",
+ cursor_default_hot_x, cursor_default_hot_y
+ },
+ {
+ "cursor-corner-bottom",
+ cursor_default_hot_x, cursor_default_hot_y
+ },
+ {
+ "cursor-corner-bottom-left",
+ cursor_default_hot_x, cursor_default_hot_y
+ },
+ {
+ "cursor-corner-left",
+ cursor_default_hot_x, cursor_default_hot_y
+ },
+ {
+ "cursor-corner-top-left",
+ cursor_default_hot_x, cursor_default_hot_y
+ },
+ {
+ "cursor-side-top",
+ cursor_default_hot_x, cursor_default_hot_y
+ },
+ {
+ "cursor-side-top-right",
+ cursor_default_hot_x, cursor_default_hot_y
+ },
+ {
+ "cursor-side-right",
+ cursor_default_hot_x, cursor_default_hot_y
+ },
+ {
+ "cursor-side-bottom-right",
+ cursor_default_hot_x, cursor_default_hot_y
+ },
+ {
+ "cursor-side-bottom",
+ cursor_default_hot_x, cursor_default_hot_y
+ },
+ {
+ "cursor-side-bottom-left",
+ cursor_default_hot_x, cursor_default_hot_y
+ },
+ {
+ "cursor-side-left",
+ cursor_default_hot_x, cursor_default_hot_y
+ },
+ {
+ "cursor-side-top-left",
+ cursor_default_hot_x, cursor_default_hot_y
+ }
+};
+
+static GimpCursor gimp_tool_cursors[] =
+{
+ /* these have to match up with enum GimpToolCursorType in widgets-enums.h */
+
+ { NULL },
+ { "tool-rect-select" },
+ { "tool-ellipse-select" },
+ { "tool-free-select" },
+ { "tool-polygon-select" },
+ { "tool-fuzzy-select" },
+ { "tool-paths" },
+ { "tool-paths-anchor" },
+ { "tool-paths-control" },
+ { "tool-paths-segment" },
+ { "tool-iscissors" },
+ { "tool-move" },
+ { "tool-zoom" },
+ { "tool-crop" },
+ { "tool-resize" },
+ { "tool-rotate" },
+ { "tool-shear" },
+ { "tool-perspective" },
+ { "tool-transform-3d-camera" },
+ { "tool-flip-horizontal" },
+ { "tool-flip-vertical" },
+ { "tool-text" },
+ { "tool-color-picker" },
+ { "tool-bucket-fill" },
+ { "tool-gradient" },
+ { "tool-pencil" },
+ { "tool-paintbrush" },
+ { "tool-airbrush" },
+ { "tool-ink" },
+ { "tool-clone" },
+ { "tool-heal" },
+ { "tool-eraser" },
+ { "tool-smudge" },
+ { "tool-blur" },
+ { "tool-dodge" },
+ { "tool-burn" },
+ { "tool-measure" },
+ { "tool-warp" },
+ { "tool-hand" }
+};
+
+static GimpCursor gimp_cursor_modifiers[] =
+{
+ /* these have to match up with enum GimpCursorModifier in widgets-enums.h */
+
+ { NULL },
+ { "modifier-bad" },
+ { "modifier-plus" },
+ { "modifier-minus" },
+ { "modifier-intersect" },
+ { "modifier-move" },
+ { "modifier-resize" },
+ { "modifier-rotate" },
+ { "modifier-zoom" },
+ { "modifier-control" },
+ { "modifier-anchor" },
+ { "modifier-foreground" },
+ { "modifier-background" },
+ { "modifier-pattern" },
+ { "modifier-join" },
+ { "modifier-select" }
+};
+
+
+static const GdkPixbuf *
+get_cursor_pixbuf (GimpCursor *cursor,
+ gint scale_factor)
+{
+ gchar *resource_path;
+ GError *error = NULL;
+
+ if (! cursor->pixbuf)
+ {
+ resource_path = g_strconcat ("/org/gimp/tool-cursors/",
+ cursor->resource_name,
+ ".png", NULL);
+
+ cursor->pixbuf = gdk_pixbuf_new_from_resource (resource_path, &error);
+
+ if (! cursor->pixbuf)
+ {
+ g_critical ("Failed to create cursor image '%s': %s",
+ resource_path, error->message);
+ g_clear_error (&error);
+ }
+
+ g_free (resource_path);
+ }
+
+ if (scale_factor == 2 && ! cursor->pixbuf_x2)
+ {
+ resource_path = g_strconcat ("/org/gimp/tool-cursors/",
+ cursor->resource_name,
+ "-x2.png", NULL);
+
+ cursor->pixbuf_x2 = gdk_pixbuf_new_from_resource (resource_path, &error);
+
+ if (! cursor->pixbuf_x2)
+ {
+ /* no critical here until we actually have the cursor files */
+ g_printerr ("Failed to create scaled cursor image '%s' "
+ "falling back to upscaling default cursor: %s\n",
+ resource_path, error->message);
+ g_clear_error (&error);
+
+ if (cursor->pixbuf)
+ {
+ gint width = gdk_pixbuf_get_width (cursor->pixbuf);
+ gint height = gdk_pixbuf_get_height (cursor->pixbuf);
+
+ cursor->pixbuf_x2 = gdk_pixbuf_scale_simple (cursor->pixbuf,
+ width * 2,
+ height * 2,
+ GDK_INTERP_NEAREST);
+ }
+ }
+
+ g_free (resource_path);
+ }
+
+ if (scale_factor == 2)
+ return cursor->pixbuf_x2;
+ else
+ return cursor->pixbuf;
+}
+
+GdkCursor *
+gimp_cursor_new (GdkWindow *window,
+ GimpHandedness cursor_handedness,
+ GimpCursorType cursor_type,
+ GimpToolCursorType tool_cursor,
+ GimpCursorModifier modifier)
+{
+ GdkDisplay *display;
+ GimpCursor *bmcursor = NULL;
+ GimpCursor *bmmodifier = NULL;
+ GimpCursor *bmtool = NULL;
+ GdkCursor *cursor;
+ GdkPixbuf *pixbuf;
+ gint scale_factor;
+ gint hot_x;
+ gint hot_y;
+
+ g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
+ g_return_val_if_fail (cursor_type < GIMP_CURSOR_LAST, NULL);
+
+ display = gdk_window_get_display (window);
+
+ if (cursor_type <= (GimpCursorType) GDK_LAST_CURSOR)
+ return gdk_cursor_new_for_display (display, (GdkCursorType) cursor_type);
+
+ g_return_val_if_fail (cursor_type >= GIMP_CURSOR_NONE, NULL);
+
+ /* disallow the small tool cursor with some cursors
+ */
+ if (cursor_type <= GIMP_CURSOR_NONE ||
+ cursor_type == GIMP_CURSOR_CROSSHAIR ||
+ cursor_type == GIMP_CURSOR_ZOOM ||
+ cursor_type == GIMP_CURSOR_COLOR_PICKER ||
+ cursor_type >= GIMP_CURSOR_LAST)
+ {
+ tool_cursor = GIMP_TOOL_CURSOR_NONE;
+ }
+
+ /* don't allow anything with the empty cursor
+ */
+ if (cursor_type == GIMP_CURSOR_NONE)
+ {
+ tool_cursor = GIMP_TOOL_CURSOR_NONE;
+ modifier = GIMP_CURSOR_MODIFIER_NONE;
+ }
+
+ /* some more sanity checks
+ */
+ if (cursor_type == GIMP_CURSOR_MOVE &&
+ modifier == GIMP_CURSOR_MODIFIER_MOVE)
+ {
+ modifier = GIMP_CURSOR_MODIFIER_NONE;
+ }
+
+ /* when cursor is "corner" or "side" sides must be exchanged for
+ * left-hand-mice-flipping of pixbuf below
+ */
+
+ if (cursor_handedness == GIMP_HANDEDNESS_LEFT)
+ {
+ switch (cursor_type)
+ {
+ case GIMP_CURSOR_CORNER_TOP_LEFT:
+ cursor_type = GIMP_CURSOR_CORNER_TOP_RIGHT; break;
+
+ case GIMP_CURSOR_CORNER_TOP_RIGHT:
+ cursor_type = GIMP_CURSOR_CORNER_TOP_LEFT; break;
+
+ case GIMP_CURSOR_CORNER_LEFT:
+ cursor_type = GIMP_CURSOR_CORNER_RIGHT; break;
+
+ case GIMP_CURSOR_CORNER_RIGHT:
+ cursor_type = GIMP_CURSOR_CORNER_LEFT; break;
+
+ case GIMP_CURSOR_CORNER_BOTTOM_LEFT:
+ cursor_type = GIMP_CURSOR_CORNER_BOTTOM_RIGHT; break;
+
+ case GIMP_CURSOR_CORNER_BOTTOM_RIGHT:
+ cursor_type = GIMP_CURSOR_CORNER_BOTTOM_LEFT; break;
+
+ case GIMP_CURSOR_SIDE_TOP_LEFT:
+ cursor_type = GIMP_CURSOR_SIDE_TOP_RIGHT; break;
+
+ case GIMP_CURSOR_SIDE_TOP_RIGHT:
+ cursor_type = GIMP_CURSOR_SIDE_TOP_LEFT; break;
+
+ case GIMP_CURSOR_SIDE_LEFT:
+ cursor_type = GIMP_CURSOR_SIDE_RIGHT; break;
+
+ case GIMP_CURSOR_SIDE_RIGHT:
+ cursor_type = GIMP_CURSOR_SIDE_LEFT; break;
+
+ case GIMP_CURSOR_SIDE_BOTTOM_LEFT:
+ cursor_type = GIMP_CURSOR_SIDE_BOTTOM_RIGHT; break;
+
+ case GIMP_CURSOR_SIDE_BOTTOM_RIGHT:
+ cursor_type = GIMP_CURSOR_SIDE_BOTTOM_LEFT; break;
+
+ default:
+ break;
+ }
+ }
+
+ /* prepare the main cursor */
+
+ cursor_type -= GIMP_CURSOR_NONE;
+ bmcursor = &gimp_cursors[cursor_type];
+
+ /* prepare the tool cursor */
+
+ if (tool_cursor > GIMP_TOOL_CURSOR_NONE &&
+ tool_cursor < GIMP_TOOL_CURSOR_LAST)
+ {
+ bmtool = &gimp_tool_cursors[tool_cursor];
+ }
+
+ /* prepare the cursor modifier */
+
+ if (modifier > GIMP_CURSOR_MODIFIER_NONE &&
+ modifier < GIMP_CURSOR_MODIFIER_LAST)
+ {
+ bmmodifier = &gimp_cursor_modifiers[modifier];
+ }
+
+ scale_factor = 1;
+
+ /* guess HiDPI */
+ {
+ GdkScreen *screen = gdk_window_get_screen (window);
+ gdouble xres, yres;
+
+ gimp_get_monitor_resolution (screen,
+ gdk_screen_get_monitor_at_window (screen, window),
+ &xres, &yres);
+
+ if ((xres + yres) / 2.0 > 250.0)
+ scale_factor = 2;
+ }
+
+ pixbuf = gdk_pixbuf_copy (get_cursor_pixbuf (bmcursor, scale_factor));
+
+ if (bmmodifier || bmtool)
+ {
+ gint width = gdk_pixbuf_get_width (pixbuf);
+ gint height = gdk_pixbuf_get_height (pixbuf);
+
+ if (bmmodifier)
+ gdk_pixbuf_composite (get_cursor_pixbuf (bmmodifier, scale_factor),
+ pixbuf,
+ 0, 0, width, height,
+ 0.0, 0.0, 1.0, 1.0,
+ GDK_INTERP_NEAREST, 200);
+
+ if (bmtool)
+ gdk_pixbuf_composite (get_cursor_pixbuf (bmtool, scale_factor),
+ pixbuf,
+ 0, 0, width, height,
+ 0.0, 0.0, 1.0, 1.0,
+ GDK_INTERP_NEAREST, 200);
+ }
+
+ hot_x = bmcursor->hot_x;
+ hot_y = bmcursor->hot_y;
+
+ /* flip the cursor if mouse setting is left-handed */
+
+ if (cursor_handedness == GIMP_HANDEDNESS_LEFT)
+ {
+ GdkPixbuf *flipped = gdk_pixbuf_flip (pixbuf, TRUE);
+ gint width = gdk_pixbuf_get_width (flipped);
+
+ g_object_unref (pixbuf);
+ pixbuf = flipped;
+
+ hot_x = (width - 1) - hot_x;
+ }
+
+ cursor = gdk_cursor_new_from_pixbuf (display, pixbuf,
+ hot_x * scale_factor,
+ hot_y * scale_factor);
+
+ g_object_unref (pixbuf);
+
+ return cursor;
+}
+
+void
+gimp_cursor_set (GtkWidget *widget,
+ GimpHandedness cursor_handedness,
+ GimpCursorType cursor_type,
+ GimpToolCursorType tool_cursor,
+ GimpCursorModifier modifier)
+{
+ GdkWindow *window;
+ GdkCursor *cursor;
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (gtk_widget_get_realized (widget));
+
+ window = gtk_widget_get_window (widget);
+
+ cursor = gimp_cursor_new (window,
+ cursor_handedness,
+ cursor_type,
+ tool_cursor,
+ modifier);
+ gdk_window_set_cursor (window, cursor);
+ gdk_cursor_unref (cursor);
+
+ gdk_display_flush (gdk_window_get_display (window));
+}
+
+GimpCursorType
+gimp_cursor_rotate (GimpCursorType cursor,
+ gdouble angle)
+{
+ if (cursor >= GIMP_CURSOR_CORNER_TOP &&
+ cursor <= GIMP_CURSOR_SIDE_TOP_LEFT)
+ {
+ gint offset = (gint) (angle / 45 + 0.5);
+
+ if (cursor < GIMP_CURSOR_SIDE_TOP)
+ {
+ cursor += offset;
+
+ if (cursor > GIMP_CURSOR_CORNER_TOP_LEFT)
+ cursor -= 8;
+ }
+ else
+ {
+ cursor += offset;
+
+ if (cursor > GIMP_CURSOR_SIDE_TOP_LEFT)
+ cursor -= 8;
+ }
+ }
+
+ return cursor;
+}