/* 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) 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 . */ #include "config.h" #include #include #include "libpikabase/pikabase.h" #include "libpikacolor/pikacolor.h" #include "libpikawidgets/pikawidgets.h" #include "display-types.h" #include "config/pikadisplayconfig.h" #include "widgets/pikawidgets-utils.h" #include "pikacanvas.h" #include "pika-intl.h" #define MAX_BATCH_SIZE 32000 enum { PROP_0, PROP_CONFIG }; /* local function prototypes */ static void pika_canvas_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void pika_canvas_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void pika_canvas_unrealize (GtkWidget *widget); static void pika_canvas_style_updated (GtkWidget *widget); static gboolean pika_canvas_focus_in_event (GtkWidget *widget, GdkEventFocus *event); static gboolean pika_canvas_focus_out_event (GtkWidget *widget, GdkEventFocus *event); static gboolean pika_canvas_focus (GtkWidget *widget, GtkDirectionType direction); G_DEFINE_TYPE (PikaCanvas, pika_canvas, PIKA_TYPE_OVERLAY_BOX) #define parent_class pika_canvas_parent_class static void pika_canvas_class_init (PikaCanvasClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); object_class->set_property = pika_canvas_set_property; object_class->get_property = pika_canvas_get_property; widget_class->unrealize = pika_canvas_unrealize; widget_class->style_updated = pika_canvas_style_updated; widget_class->focus_in_event = pika_canvas_focus_in_event; widget_class->focus_out_event = pika_canvas_focus_out_event; widget_class->focus = pika_canvas_focus; g_object_class_install_property (object_class, PROP_CONFIG, g_param_spec_object ("config", NULL, NULL, PIKA_TYPE_DISPLAY_CONFIG, PIKA_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } static void pika_canvas_init (PikaCanvas *canvas) { GtkWidget *widget = GTK_WIDGET (canvas); gtk_widget_set_can_focus (widget, TRUE); gtk_widget_add_events (widget, PIKA_CANVAS_EVENT_MASK); } static void pika_canvas_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { PikaCanvas *canvas = PIKA_CANVAS (object); switch (property_id) { case PROP_CONFIG: canvas->config = g_value_get_object (value); /* don't dup */ break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void pika_canvas_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { PikaCanvas *canvas = PIKA_CANVAS (object); switch (property_id) { case PROP_CONFIG: g_value_set_object (value, canvas->config); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void pika_canvas_unrealize (GtkWidget *widget) { PikaCanvas *canvas = PIKA_CANVAS (widget); g_clear_object (&canvas->layout); GTK_WIDGET_CLASS (parent_class)->unrealize (widget); } static void pika_canvas_style_updated (GtkWidget *widget) { PikaCanvas *canvas = PIKA_CANVAS (widget); g_clear_object (&canvas->layout); GTK_WIDGET_CLASS (parent_class)->style_updated (widget); } static gboolean pika_canvas_focus_in_event (GtkWidget *widget, GdkEventFocus *event) { /* don't allow the default impl to invalidate the whole widget, * we don't draw a focus indicator anyway. */ return FALSE; } static gboolean pika_canvas_focus_out_event (GtkWidget *widget, GdkEventFocus *event) { /* see focus-in-event */ return FALSE; } static gboolean pika_canvas_focus (GtkWidget *widget, GtkDirectionType direction) { GtkWidget *focus = gtk_container_get_focus_child (GTK_CONTAINER (widget)); /* override GtkContainer's focus() implementation which would always * give focus to the canvas because it is focussable. Instead, try * navigating in the focused overlay child first, and use * GtkContainer's default implementation only if that fails (which * happens when focus navigation leaves the overlay child). */ if (focus && gtk_widget_child_focus (focus, direction)) return TRUE; return GTK_WIDGET_CLASS (parent_class)->focus (widget, direction); } /* public functions */ /** * pika_canvas_new: * * Creates a new #PikaCanvas widget. * * The #PikaCanvas widget is a #GtkDrawingArea abstraction. It manages * a set of graphic contexts for drawing on a PIKA display. If you * draw using a #PikaCanvasStyle, #PikaCanvas makes sure that the * associated #GdkGC is created. All drawing on the canvas needs to * happen by means of the #PikaCanvas drawing functions. Besides from * not needing a #GdkGC pointer, the #PikaCanvas drawing functions * look and work like their #GdkDrawable counterparts. #PikaCanvas * gracefully handles attempts to draw on the unrealized widget. * * Returns: a new #PikaCanvas widget **/ GtkWidget * pika_canvas_new (PikaDisplayConfig *config) { g_return_val_if_fail (PIKA_IS_DISPLAY_CONFIG (config), NULL); return g_object_new (PIKA_TYPE_CANVAS, "name", "pika-canvas", "config", config, NULL); } /** * pika_canvas_get_layout: * @canvas: a #PikaCanvas widget * @format: a standard printf() format string. * @Varargs: the parameters to insert into the format string. * * Returns a layout which can be used for * pango_cairo_show_layout(). The layout belongs to the canvas and * should not be freed, not should a pointer to it be kept around * after drawing. * * Returns: a #PangoLayout owned by the canvas. **/ PangoLayout * pika_canvas_get_layout (PikaCanvas *canvas, const gchar *format, ...) { va_list args; gchar *text; if (! canvas->layout) canvas->layout = gtk_widget_create_pango_layout (GTK_WIDGET (canvas), NULL); va_start (args, format); text = g_strdup_vprintf (format, args); va_end (args); pango_layout_set_text (canvas->layout, text, -1); g_free (text); return canvas->layout; } /** * pika_canvas_set_padding: * @canvas: a #PikaCanvas widget * @color: a color in #PikaRGB format * * Sets the background color of the canvas's window. This * is the color the canvas is set to if it is cleared. **/ void pika_canvas_set_padding (PikaCanvas *canvas, PikaCanvasPaddingMode padding_mode, const PikaRGB *padding_color) { g_return_if_fail (PIKA_IS_CANVAS (canvas)); g_return_if_fail (padding_color != NULL); canvas->padding_mode = padding_mode; canvas->padding_color = *padding_color; gtk_widget_queue_draw (GTK_WIDGET (canvas)); }