/* 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 * * pikatoolwidgetgroup.c * Copyright (C) 2018 Ell * * 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 "display-types.h" #include "core/pikacontainer.h" #include "core/pikalist.h" #include "pikacanvasgroup.h" #include "pikadisplayshell.h" #include "pikatoolwidgetgroup.h" struct _PikaToolWidgetGroupPrivate { PikaContainer *children; PikaToolWidget *focus_widget; PikaToolWidget *hover_widget; gboolean auto_raise; }; /* local function prototypes */ static void pika_tool_widget_group_finalize (GObject *object); static void pika_tool_widget_group_focus_changed (PikaToolWidget *widget); static gint pika_tool_widget_group_button_press (PikaToolWidget *widget, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaButtonPressType press_type); static void pika_tool_widget_group_button_release (PikaToolWidget *widget, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaButtonReleaseType release_type); static void pika_tool_widget_group_motion (PikaToolWidget *widget, const PikaCoords *coords, guint32 time, GdkModifierType state); static PikaHit pika_tool_widget_group_hit (PikaToolWidget *widget, const PikaCoords *coords, GdkModifierType state, gboolean proximity); static void pika_tool_widget_group_hover (PikaToolWidget *widget, const PikaCoords *coords, GdkModifierType state, gboolean proximity); static void pika_tool_widget_group_leave_notify (PikaToolWidget *widget); static gboolean pika_tool_widget_group_key_press (PikaToolWidget *widget, GdkEventKey *kevent); static gboolean pika_tool_widget_group_key_release (PikaToolWidget *widget, GdkEventKey *kevent); static void pika_tool_widget_group_motion_modifier (PikaToolWidget *widget, GdkModifierType key, gboolean press, GdkModifierType state); static void pika_tool_widget_group_hover_modifier (PikaToolWidget *widget, GdkModifierType key, gboolean press, GdkModifierType state); static gboolean pika_tool_widget_group_get_cursor (PikaToolWidget *widget, const PikaCoords *coords, GdkModifierType state, PikaCursorType *cursor, PikaToolCursorType *tool_cursor, PikaCursorModifier *modifier); static void pika_tool_widget_group_children_add (PikaContainer *container, PikaToolWidget *child, PikaToolWidgetGroup *group); static void pika_tool_widget_group_children_remove (PikaContainer *container, PikaToolWidget *child, PikaToolWidgetGroup *group); static void pika_tool_widget_group_children_reorder (PikaContainer *container, PikaToolWidget *child, gint new_index, PikaToolWidgetGroup *group); static void pika_tool_widget_group_child_changed (PikaToolWidget *child, PikaToolWidgetGroup *group); static void pika_tool_widget_group_child_response (PikaToolWidget *child, gint response_id, PikaToolWidgetGroup *group); static void pika_tool_widget_group_child_snap_offsets (PikaToolWidget *child, gint offset_x, gint offset_y, gint width, gint height, PikaToolWidgetGroup *group); static void pika_tool_widget_group_child_status (PikaToolWidget *child, const gchar *status, PikaToolWidgetGroup *group); static void pika_tool_widget_group_child_status_coords (PikaToolWidget *child, const gchar *title, gdouble x, const gchar *separator, gdouble y, const gchar *help, PikaToolWidgetGroup *group); static void pika_tool_widget_group_child_message (PikaToolWidget *child, const gchar *message, PikaToolWidgetGroup *group); static void pika_tool_widget_group_child_focus_changed (PikaToolWidget *child, PikaToolWidgetGroup *group); static PikaToolWidget * pika_tool_widget_group_get_hover_widget (PikaToolWidgetGroup *group, const PikaCoords *coords, GdkModifierType state, gboolean proximity, PikaHit *hit); G_DEFINE_TYPE_WITH_PRIVATE (PikaToolWidgetGroup, pika_tool_widget_group, PIKA_TYPE_TOOL_WIDGET) #define parent_class pika_tool_widget_group_parent_class /* priv functions */ static void pika_tool_widget_group_class_init (PikaToolWidgetGroupClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); PikaToolWidgetClass *widget_class = PIKA_TOOL_WIDGET_CLASS (klass); object_class->finalize = pika_tool_widget_group_finalize; widget_class->focus_changed = pika_tool_widget_group_focus_changed; widget_class->button_press = pika_tool_widget_group_button_press; widget_class->button_release = pika_tool_widget_group_button_release; widget_class->motion = pika_tool_widget_group_motion; widget_class->hit = pika_tool_widget_group_hit; widget_class->hover = pika_tool_widget_group_hover; widget_class->leave_notify = pika_tool_widget_group_leave_notify; widget_class->key_press = pika_tool_widget_group_key_press; widget_class->key_release = pika_tool_widget_group_key_release; widget_class->motion_modifier = pika_tool_widget_group_motion_modifier; widget_class->hover_modifier = pika_tool_widget_group_hover_modifier; widget_class->get_cursor = pika_tool_widget_group_get_cursor; } static void pika_tool_widget_group_init (PikaToolWidgetGroup *group) { PikaToolWidgetGroupPrivate *priv; priv = group->priv = pika_tool_widget_group_get_instance_private (group); priv->children = g_object_new (PIKA_TYPE_LIST, "children-type", PIKA_TYPE_TOOL_WIDGET, "append", TRUE, NULL); g_signal_connect (priv->children, "add", G_CALLBACK (pika_tool_widget_group_children_add), group); g_signal_connect (priv->children, "remove", G_CALLBACK (pika_tool_widget_group_children_remove), group); g_signal_connect (priv->children, "reorder", G_CALLBACK (pika_tool_widget_group_children_reorder), group); pika_container_add_handler (priv->children, "changed", G_CALLBACK (pika_tool_widget_group_child_changed), group); pika_container_add_handler (priv->children, "response", G_CALLBACK (pika_tool_widget_group_child_response), group); pika_container_add_handler (priv->children, "snap-offsets", G_CALLBACK (pika_tool_widget_group_child_snap_offsets), group); pika_container_add_handler (priv->children, "status", G_CALLBACK (pika_tool_widget_group_child_status), group); pika_container_add_handler (priv->children, "status-coords", G_CALLBACK (pika_tool_widget_group_child_status_coords), group); pika_container_add_handler (priv->children, "message", G_CALLBACK (pika_tool_widget_group_child_message), group); pika_container_add_handler (priv->children, "focus-changed", G_CALLBACK (pika_tool_widget_group_child_focus_changed), group); } static void pika_tool_widget_group_finalize (GObject *object) { PikaToolWidgetGroup *group = PIKA_TOOL_WIDGET_GROUP (object); PikaToolWidgetGroupPrivate *priv = group->priv; g_clear_object (&priv->children); G_OBJECT_CLASS (parent_class)->finalize (object); } static gint pika_tool_widget_group_button_press (PikaToolWidget *widget, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaButtonPressType press_type) { PikaToolWidgetGroup *group = PIKA_TOOL_WIDGET_GROUP (widget); PikaToolWidgetGroupPrivate *priv = group->priv; pika_tool_widget_group_hover (widget, coords, state, TRUE); if (priv->focus_widget != priv->hover_widget) { if (priv->hover_widget) pika_tool_widget_set_focus (priv->hover_widget, TRUE); else if (priv->focus_widget) pika_tool_widget_set_focus (priv->focus_widget, FALSE); } if (priv->hover_widget) { if (priv->auto_raise) { pika_container_reorder (priv->children, PIKA_OBJECT (priv->hover_widget), -1); } return pika_tool_widget_button_press (priv->hover_widget, coords, time, state, press_type); } return FALSE; } static void pika_tool_widget_group_focus_changed (PikaToolWidget *widget) { PikaToolWidgetGroup *group = PIKA_TOOL_WIDGET_GROUP (widget); PikaToolWidgetGroupPrivate *priv = group->priv; if (priv->focus_widget) { PikaToolWidget *focus_widget = priv->focus_widget; pika_tool_widget_set_focus (priv->focus_widget, pika_tool_widget_get_focus (widget)); priv->focus_widget = focus_widget; } } static void pika_tool_widget_group_button_release (PikaToolWidget *widget, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaButtonReleaseType release_type) { PikaToolWidgetGroup *group = PIKA_TOOL_WIDGET_GROUP (widget); PikaToolWidgetGroupPrivate *priv = group->priv; if (priv->hover_widget) { pika_tool_widget_button_release (priv->hover_widget, coords, time, state, release_type); } } static void pika_tool_widget_group_motion (PikaToolWidget *widget, const PikaCoords *coords, guint32 time, GdkModifierType state) { PikaToolWidgetGroup *group = PIKA_TOOL_WIDGET_GROUP (widget); PikaToolWidgetGroupPrivate *priv = group->priv; if (priv->hover_widget) pika_tool_widget_motion (priv->hover_widget, coords, time, state); } static PikaHit pika_tool_widget_group_hit (PikaToolWidget *widget, const PikaCoords *coords, GdkModifierType state, gboolean proximity) { PikaToolWidgetGroup *group = PIKA_TOOL_WIDGET_GROUP (widget); PikaHit hit; pika_tool_widget_group_get_hover_widget (group, coords, state, proximity, &hit); return hit; } static void pika_tool_widget_group_hover (PikaToolWidget *widget, const PikaCoords *coords, GdkModifierType state, gboolean proximity) { PikaToolWidgetGroup *group = PIKA_TOOL_WIDGET_GROUP (widget); PikaToolWidgetGroupPrivate *priv = group->priv; PikaToolWidget *hover_widget; hover_widget = pika_tool_widget_group_get_hover_widget (group, coords, state, proximity, NULL); if (priv->hover_widget && priv->hover_widget != hover_widget) pika_tool_widget_leave_notify (priv->hover_widget); priv->hover_widget = hover_widget; if (priv->hover_widget) pika_tool_widget_hover (priv->hover_widget, coords, state, proximity); } static void pika_tool_widget_group_leave_notify (PikaToolWidget *widget) { PikaToolWidgetGroup *group = PIKA_TOOL_WIDGET_GROUP (widget); PikaToolWidgetGroupPrivate *priv = group->priv; if (priv->hover_widget) { pika_tool_widget_leave_notify (priv->hover_widget); priv->hover_widget = NULL; } PIKA_TOOL_WIDGET_CLASS (parent_class)->leave_notify (widget); } static gboolean pika_tool_widget_group_key_press (PikaToolWidget *widget, GdkEventKey *kevent) { PikaToolWidgetGroup *group = PIKA_TOOL_WIDGET_GROUP (widget); PikaToolWidgetGroupPrivate *priv = group->priv; if (priv->focus_widget) return pika_tool_widget_key_press (priv->focus_widget, kevent); return PIKA_TOOL_WIDGET_CLASS (parent_class)->key_press (widget, kevent); } static gboolean pika_tool_widget_group_key_release (PikaToolWidget *widget, GdkEventKey *kevent) { PikaToolWidgetGroup *group = PIKA_TOOL_WIDGET_GROUP (widget); PikaToolWidgetGroupPrivate *priv = group->priv; if (priv->focus_widget) return pika_tool_widget_key_release (priv->focus_widget, kevent); return FALSE; } static void pika_tool_widget_group_motion_modifier (PikaToolWidget *widget, GdkModifierType key, gboolean press, GdkModifierType state) { PikaToolWidgetGroup *group = PIKA_TOOL_WIDGET_GROUP (widget); PikaToolWidgetGroupPrivate *priv = group->priv; if (priv->hover_widget) pika_tool_widget_motion_modifier (priv->hover_widget, key, press, state); } static void pika_tool_widget_group_hover_modifier (PikaToolWidget *widget, GdkModifierType key, gboolean press, GdkModifierType state) { PikaToolWidgetGroup *group = PIKA_TOOL_WIDGET_GROUP (widget); PikaToolWidgetGroupPrivate *priv = group->priv; GList *iter; for (iter = g_queue_peek_head_link (PIKA_LIST (priv->children)->queue); iter; iter = g_list_next (iter)) { pika_tool_widget_hover_modifier (iter->data, key, press, state); } } static gboolean pika_tool_widget_group_get_cursor (PikaToolWidget *widget, const PikaCoords *coords, GdkModifierType state, PikaCursorType *cursor, PikaToolCursorType *tool_cursor, PikaCursorModifier *modifier) { PikaToolWidgetGroup *group = PIKA_TOOL_WIDGET_GROUP (widget); PikaToolWidgetGroupPrivate *priv = group->priv; if (priv->hover_widget) { return pika_tool_widget_get_cursor (priv->hover_widget, coords, state, cursor, tool_cursor, modifier); } return FALSE; } static void pika_tool_widget_group_children_add (PikaContainer *container, PikaToolWidget *child, PikaToolWidgetGroup *group) { PikaToolWidgetGroupPrivate *priv = group->priv; PikaToolWidget *widget = PIKA_TOOL_WIDGET (group); PikaCanvasGroup *canvas_group; canvas_group = PIKA_CANVAS_GROUP (pika_tool_widget_get_item (widget)); pika_canvas_group_add_item (canvas_group, pika_tool_widget_get_item (child)); if (pika_tool_widget_get_focus (child) && priv->focus_widget) { pika_tool_widget_set_focus (priv->focus_widget, FALSE); priv->focus_widget = NULL; } if (! priv->focus_widget) { priv->focus_widget = child; pika_tool_widget_set_focus (child, pika_tool_widget_get_focus (widget)); } pika_tool_widget_changed (widget); } static void pika_tool_widget_group_children_remove (PikaContainer *container, PikaToolWidget *child, PikaToolWidgetGroup *group) { PikaToolWidgetGroupPrivate *priv = group->priv; PikaToolWidget *widget = PIKA_TOOL_WIDGET (group); PikaCanvasGroup *canvas_group; canvas_group = PIKA_CANVAS_GROUP (pika_tool_widget_get_item (widget)); if (priv->focus_widget == child) { pika_tool_widget_set_focus (child, FALSE); priv->focus_widget = NULL; } if (priv->hover_widget == child) { pika_tool_widget_leave_notify (child); priv->hover_widget = NULL; } if (! priv->focus_widget) { priv->focus_widget = PIKA_TOOL_WIDGET (pika_container_get_last_child (container)); if (priv->focus_widget) pika_tool_widget_set_focus (priv->focus_widget, TRUE); } pika_canvas_group_remove_item (canvas_group, pika_tool_widget_get_item (child)); pika_tool_widget_changed (widget); } static void pika_tool_widget_group_children_reorder (PikaContainer *container, PikaToolWidget *child, gint new_index, PikaToolWidgetGroup *group) { PikaToolWidget *widget = PIKA_TOOL_WIDGET (group); PikaCanvasGroup *canvas_group; GList *iter; canvas_group = PIKA_CANVAS_GROUP (pika_tool_widget_get_item (widget)); for (iter = g_queue_peek_head_link (PIKA_LIST (container)->queue); iter; iter = g_list_next (iter)) { PikaCanvasItem *item = pika_tool_widget_get_item (iter->data); pika_canvas_group_remove_item (canvas_group, item); pika_canvas_group_add_item (canvas_group, item); } pika_tool_widget_changed (widget); } static void pika_tool_widget_group_child_changed (PikaToolWidget *child, PikaToolWidgetGroup *group) { PikaToolWidget *widget = PIKA_TOOL_WIDGET (group); pika_tool_widget_changed (widget); } static void pika_tool_widget_group_child_response (PikaToolWidget *child, gint response_id, PikaToolWidgetGroup *group) { PikaToolWidgetGroupPrivate *priv = group->priv; PikaToolWidget *widget = PIKA_TOOL_WIDGET (group); if (priv->focus_widget == child) pika_tool_widget_response (widget, response_id); } static void pika_tool_widget_group_child_snap_offsets (PikaToolWidget *child, gint offset_x, gint offset_y, gint width, gint height, PikaToolWidgetGroup *group) { PikaToolWidgetGroupPrivate *priv = group->priv; PikaToolWidget *widget = PIKA_TOOL_WIDGET (group); if (priv->hover_widget == child) { pika_tool_widget_set_snap_offsets (widget, offset_x, offset_y, width, height); } } static void pika_tool_widget_group_child_status (PikaToolWidget *child, const gchar *status, PikaToolWidgetGroup *group) { PikaToolWidgetGroupPrivate *priv = group->priv; PikaToolWidget *widget = PIKA_TOOL_WIDGET (group); if (priv->hover_widget == child) pika_tool_widget_set_status (widget, status); } static void pika_tool_widget_group_child_status_coords (PikaToolWidget *child, const gchar *title, gdouble x, const gchar *separator, gdouble y, const gchar *help, PikaToolWidgetGroup *group) { PikaToolWidgetGroupPrivate *priv = group->priv; PikaToolWidget *widget = PIKA_TOOL_WIDGET (group); if (priv->hover_widget == child) pika_tool_widget_set_status_coords (widget, title, x, separator, y, help); } static void pika_tool_widget_group_child_message (PikaToolWidget *child, const gchar *message, PikaToolWidgetGroup *group) { PikaToolWidgetGroupPrivate *priv = group->priv; PikaToolWidget *widget = PIKA_TOOL_WIDGET (group); if (priv->focus_widget == child) pika_tool_widget_message_literal (widget, message); } static void pika_tool_widget_group_child_focus_changed (PikaToolWidget *child, PikaToolWidgetGroup *group) { PikaToolWidgetGroupPrivate *priv = group->priv; PikaToolWidget *widget = PIKA_TOOL_WIDGET (group); if (pika_tool_widget_get_focus (child)) { if (priv->focus_widget && priv->focus_widget != child) pika_tool_widget_set_focus (priv->focus_widget, FALSE); priv->focus_widget = child; pika_tool_widget_set_focus (widget, TRUE); } else { if (priv->focus_widget == child) priv->focus_widget = NULL; } } static PikaToolWidget * pika_tool_widget_group_get_hover_widget (PikaToolWidgetGroup *group, const PikaCoords *coords, GdkModifierType state, gboolean proximity, PikaHit *hit) { PikaToolWidgetGroupPrivate *priv = group->priv; PikaToolWidget *indirect_child = NULL; gboolean indirect = FALSE; GList *iter; for (iter = g_queue_peek_tail_link (PIKA_LIST (priv->children)->queue); iter; iter = g_list_previous (iter)) { PikaToolWidget *child = iter->data; switch (pika_tool_widget_hit (child, coords, state, proximity)) { case PIKA_HIT_DIRECT: if (hit) *hit = PIKA_HIT_DIRECT; return child; case PIKA_HIT_INDIRECT: if (! indirect || child == priv->focus_widget) indirect_child = child; else if (indirect_child != priv->focus_widget) indirect_child = NULL; indirect = TRUE; break; case PIKA_HIT_NONE: break; } } if (hit) *hit = indirect_child ? PIKA_HIT_INDIRECT : PIKA_HIT_NONE; return indirect_child; } /* public functions */ PikaToolWidget * pika_tool_widget_group_new (PikaDisplayShell *shell) { g_return_val_if_fail (PIKA_IS_DISPLAY_SHELL (shell), NULL); return g_object_new (PIKA_TYPE_TOOL_WIDGET_GROUP, "shell", shell, NULL); } PikaContainer * pika_tool_widget_group_get_children (PikaToolWidgetGroup *group) { g_return_val_if_fail (PIKA_IS_TOOL_WIDGET_GROUP (group), NULL); return group->priv->children; } PikaToolWidget * pika_tool_widget_group_get_focus_widget (PikaToolWidgetGroup *group) { g_return_val_if_fail (PIKA_IS_TOOL_WIDGET_GROUP (group), NULL); return group->priv->focus_widget; } void pika_tool_widget_group_set_auto_raise (PikaToolWidgetGroup *group, gboolean auto_raise) { g_return_if_fail (PIKA_IS_TOOL_WIDGET_GROUP (group)); group->priv->auto_raise = auto_raise; } gboolean pika_tool_widget_group_get_auto_raise (PikaToolWidgetGroup *group) { g_return_val_if_fail (PIKA_IS_TOOL_WIDGET_GROUP (group), FALSE); return group->priv->auto_raise; }