Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Gimp/app/display/   (Gimp Bildeditor Version 3.2.0©)  Datei vom 14.2.2026 mit Größe 37 kB image not shown  

Quelle  gimptoolfocus.c   Sprache: C

 

/* GIMP - The GNU Image Manipulation Program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * gimptoolfocus.c
 * Copyright (C) 2020 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 <https://www.gnu.org/licenses/>.
 */


#include "config.h"

#include <gegl.h>
#include <gtk/gtk.h>

#include "libgimpbase/gimpbase.h"
#include "libgimpmath/gimpmath.h"

#include "display-types.h"

#include "widgets/gimpwidgets-utils.h"

#include "gimpcanvasgroup.h"
#include "gimpcanvashandle.h"
#include "gimpcanvaslimit.h"
#include "gimpdisplayshell.h"
#include "gimpdisplayshell-transform.h"
#include "gimpdisplayshell-utils.h"
#include "gimptoolfocus.h"

#include "gimp-intl.h"


#define HANDLE_SIZE   12.0
#define SNAP_DISTANCE 12.0

#define EPSILON       1e-6


enum
{
  PROP_0,
  PROP_TYPE,
  PROP_X,
  PROP_Y,
  PROP_RADIUS,
  PROP_ASPECT_RATIO,
  PROP_ANGLE,
  PROP_INNER_LIMIT,
  PROP_MIDPOINT
};

enum
{
  LIMIT_OUTER,
  LIMIT_INNER,
  LIMIT_MIDDLE,

  N_LIMITS
};


typedef enum
{
  HOVER_NONE,
  HOVER_LIMIT,
  HOVER_HANDLE,
  HOVER_MOVE,
  HOVER_ROTATE
} Hover;


typedef struct
{
  GimpCanvasItem *item;

  GtkOrientation  orientation;
  GimpVector2     dir;
} GimpToolFocusHandle;

typedef struct
{
  GimpCanvasGroup     *group;
  GimpCanvasItem      *item;

  gint                 n_handles;
  GimpToolFocusHandle  handles[4];
} GimpToolFocusLimit;

struct _GimpToolFocusPrivate
{
  GimpLimitType       type;

  gdouble             x;
  gdouble             y;
  gdouble             radius;
  gdouble             aspect_ratio;
  gdouble             angle;

  gdouble             inner_limit;
  gdouble             midpoint;

  GimpToolFocusLimit  limits[N_LIMITS];

  Hover               hover;
  gint                hover_limit;
  gint                hover_handle;
  GimpCanvasItem     *hover_item;

  GimpCanvasItem     *last_hover_item;

  gdouble             saved_x;
  gdouble             saved_y;
  gdouble             saved_radius;
  gdouble             saved_aspect_ratio;
  gdouble             saved_angle;

  gdouble             saved_inner_limit;
  gdouble             saved_midpoint;

  gdouble             orig_x;
  gdouble             orig_y;
};


/*  local function prototypes  */

static void       gimp_tool_focus_constructed      (GObject               *object);
static void       gimp_tool_focus_set_property     (GObject               *object,
                                                    guint                  property_id,
                                                    const GValue          *value,
                                                    GParamSpec            *pspec);
static void       gimp_tool_focus_get_property     (GObject               *object,
                                                    guint                  property_id,
                                                    GValue                *value,
                                                    GParamSpec            *pspec);

static void       gimp_tool_focus_changed          (GimpToolWidget        *widget);
static gint       gimp_tool_focus_button_press     (GimpToolWidget        *widget,
                                                    const GimpCoords      *coords,
                                                    guint32                time,
                                                    GdkModifierType        state,
                                                    GimpButtonPressType    press_type);
static void       gimp_tool_focus_button_release   (GimpToolWidget        *widget,
                                                    const GimpCoords      *coords,
                                                    guint32                time,
                                                    GdkModifierType        state,
                                                    GimpButtonReleaseType  release_type);
static void       gimp_tool_focus_motion           (GimpToolWidget        *widget,
                                                    const GimpCoords      *coords,
                                                    guint32                time,
                                                    GdkModifierType        state);
static GimpHit    gimp_tool_focus_hit              (GimpToolWidget        *widget,
                                                    const GimpCoords      *coords,
                                                    GdkModifierType        state,
                                                    gboolean               proximity);
static void       gimp_tool_focus_hover            (GimpToolWidget        *widget,
                                                    const GimpCoords      *coords,
                                                    GdkModifierType        state,
                                                    gboolean               proximity);
static void       gimp_tool_focus_leave_notify     (GimpToolWidget        *widget);
static void       gimp_tool_focus_motion_modifier  (GimpToolWidget        *widget,
                                                    GdkModifierType        key,
                                                    gboolean               press,
                                                    GdkModifierType        state);
static void       gimp_tool_focus_hover_modifier   (GimpToolWidget        *widget,
                                                    GdkModifierType        key,
                                                    gboolean               press,
                                                    GdkModifierType        state);
static gboolean   gimp_tool_focus_get_cursor       (GimpToolWidget        *widget,
                                                    const GimpCoords      *coords,
                                                    GdkModifierType        state,
                                                    GimpCursorType        *cursor,
                                                    GimpToolCursorType    *tool_cursor,
                                                    GimpCursorModifier    *modifier);

static void       gimp_tool_focus_update_hover     (GimpToolFocus         *focus,
                                                    const GimpCoords      *coords,
                                                    gboolean               proximity);

static void       gimp_tool_focus_update_highlight (GimpToolFocus         *focus);
static void       gimp_tool_focus_update_status    (GimpToolFocus         *focus,
                                                    GdkModifierType        state);

static void       gimp_tool_focus_save             (GimpToolFocus         *focus);
static void       gimp_tool_focus_restore          (GimpToolFocus         *focus);


G_DEFINE_TYPE_WITH_PRIVATE (GimpToolFocus, gimp_tool_focus,
                            GIMP_TYPE_TOOL_WIDGET)

#define parent_class gimp_tool_focus_parent_class


/*  private functions  */

static void
gimp_tool_focus_class_init (GimpToolFocusClass *klass)
{
  GObjectClass        *object_class = G_OBJECT_CLASS (klass);
  GimpToolWidgetClass *widget_class = GIMP_TOOL_WIDGET_CLASS (klass);

  object_class->constructed     = gimp_tool_focus_constructed;
  object_class->set_property    = gimp_tool_focus_set_property;
  object_class->get_property    = gimp_tool_focus_get_property;

  widget_class->changed         = gimp_tool_focus_changed;
  widget_class->button_press    = gimp_tool_focus_button_press;
  widget_class->button_release  = gimp_tool_focus_button_release;
  widget_class->motion          = gimp_tool_focus_motion;
  widget_class->hit             = gimp_tool_focus_hit;
  widget_class->hover           = gimp_tool_focus_hover;
  widget_class->leave_notify    = gimp_tool_focus_leave_notify;
  widget_class->motion_modifier = gimp_tool_focus_motion_modifier;
  widget_class->hover_modifier  = gimp_tool_focus_hover_modifier;
  widget_class->get_cursor      = gimp_tool_focus_get_cursor;
  widget_class->update_on_scale = TRUE;

  g_object_class_install_property (object_class, PROP_TYPE,
                                   g_param_spec_enum ("type", NULL, NULL,
                                                      GIMP_TYPE_LIMIT_TYPE,
                                                      GIMP_LIMIT_CIRCLE,
                                                      GIMP_PARAM_READWRITE |
                                                      G_PARAM_CONSTRUCT));

  g_object_class_install_property (object_class, PROP_X,
                                   g_param_spec_double ("x", NULL, NULL,
                                                        -G_MAXDOUBLE,
                                                        +G_MAXDOUBLE,
                                                        0.0,
                                                        GIMP_PARAM_READWRITE |
                                                        G_PARAM_CONSTRUCT));

  g_object_class_install_property (object_class, PROP_Y,
                                   g_param_spec_double ("y", NULL, NULL,
                                                        -G_MAXDOUBLE,
                                                        +G_MAXDOUBLE,
                                                        0.0,
                                                        GIMP_PARAM_READWRITE |
                                                        G_PARAM_CONSTRUCT));

  g_object_class_install_property (object_class, PROP_RADIUS,
                                   g_param_spec_double ("radius", NULL, NULL,
                                                        0.0,
                                                        +G_MAXDOUBLE,
                                                        0.0,
                                                        GIMP_PARAM_READWRITE |
                                                        G_PARAM_CONSTRUCT));

  g_object_class_install_property (object_class, PROP_ASPECT_RATIO,
                                   g_param_spec_double ("aspect-ratio", NULL, NULL,
                                                        -1.0,
                                                        +1.0,
                                                        0.0,
                                                        GIMP_PARAM_READWRITE |
                                                        G_PARAM_CONSTRUCT));

  g_object_class_install_property (object_class, PROP_ANGLE,
                                   g_param_spec_double ("angle", NULL, NULL,
                                                        -G_MAXDOUBLE,
                                                        +G_MAXDOUBLE,
                                                        0.0,
                                                        GIMP_PARAM_READWRITE |
                                                        G_PARAM_CONSTRUCT));

  g_object_class_install_property (object_class, PROP_INNER_LIMIT,
                                   g_param_spec_double ("inner-limit", NULL, NULL,
                                                        0.0,
                                                        1.0,
                                                        0.0,
                                                        GIMP_PARAM_READWRITE |
                                                        G_PARAM_CONSTRUCT));

  g_object_class_install_property (object_class, PROP_MIDPOINT,
                                   g_param_spec_double ("midpoint", NULL, NULL,
                                                        0.0,
                                                        1.0,
                                                        0.0,
                                                        GIMP_PARAM_READWRITE |
                                                        G_PARAM_CONSTRUCT));
}

static void
gimp_tool_focus_init (GimpToolFocus *focus)
{
  GimpToolFocusPrivate *priv;

  priv = gimp_tool_focus_get_instance_private (focus);

  focus->priv = priv;

  priv->hover = HOVER_NONE;
}

static void
gimp_tool_focus_constructed (GObject *object)
{
  GimpToolFocus        *focus  = GIMP_TOOL_FOCUS (object);
  GimpToolWidget       *widget = GIMP_TOOL_WIDGET (object);
  GimpToolFocusPrivate *priv   = focus->priv;
  gint                  i;

  G_OBJECT_CLASS (parent_class)->constructed (object);

  for (i = N_LIMITS - 1; i >= 0; i--)
    {
      priv->limits[i].group = gimp_tool_widget_add_group (widget);

      gimp_tool_widget_push_group (widget, priv->limits[i].group);

      priv->limits[i].item = gimp_tool_widget_add_limit (
        widget,
        GIMP_LIMIT_CIRCLE,
        0.0, 0.0,
        0.0,
        1.0,
        0.0,
        /* dashed = */ i == LIMIT_MIDDLE);

      if (i == LIMIT_OUTER || i == LIMIT_INNER)
        {
          gint j;

          priv->limits[i].n_handles = 4;

          for (j = priv->limits[i].n_handles - 1; j >= 0; j--)
            {
              priv->limits[i].handles[j].item = gimp_tool_widget_add_handle (
                widget,
                GIMP_HANDLE_FILLED_CIRCLE,
                0.0, 0.0, HANDLE_SIZE, HANDLE_SIZE,
                GIMP_HANDLE_ANCHOR_CENTER);

              priv->limits[i].handles[j].orientation =
                j % 2 == 0 ? GTK_ORIENTATION_HORIZONTAL :
                             GTK_ORIENTATION_VERTICAL;

              priv->limits[i].handles[j].dir.x = cos (j * G_PI / 2.0);
              priv->limits[i].handles[j].dir.y = sin (j * G_PI / 2.0);
            }
        }

      gimp_tool_widget_pop_group (widget);
    }

  gimp_tool_focus_changed (widget);
}

static void
gimp_tool_focus_set_property (GObject      *object,
                              guint         property_id,
                              const GValue *value,
                              GParamSpec   *pspec)
{
  GimpToolFocus        *focus = GIMP_TOOL_FOCUS (object);
  GimpToolFocusPrivate *priv  = focus->priv;

  switch (property_id)
    {
    case PROP_TYPE:
      priv->type = g_value_get_enum (value);
      break;

    case PROP_X:
      priv->x = g_value_get_double (value);
      break;

    case PROP_Y:
      priv->y = g_value_get_double (value);
      break;

    case PROP_RADIUS:
      priv->radius = g_value_get_double (value);
      break;

    case PROP_ASPECT_RATIO:
      priv->aspect_ratio = g_value_get_double (value);
      break;

    case PROP_ANGLE:
      priv->angle = g_value_get_double (value);
      break;

    case PROP_INNER_LIMIT:
      priv->inner_limit = g_value_get_double (value);
      break;

    case PROP_MIDPOINT:
      priv->midpoint = g_value_get_double (value);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
gimp_tool_focus_get_property (GObject    *object,
                              guint       property_id,
                              GValue     *value,
                              GParamSpec *pspec)
{
  GimpToolFocus        *focus = GIMP_TOOL_FOCUS (object);
  GimpToolFocusPrivate *priv  = focus->priv;

  switch (property_id)
    {
    case PROP_TYPE:
      g_value_set_enum (value, priv->type);
      break;

    case PROP_X:
      g_value_set_double (value, priv->x);
      break;

    case PROP_Y:
      g_value_set_double (value, priv->y);
      break;

    case PROP_RADIUS:
      g_value_set_double (value, priv->radius);
      break;

    case PROP_ASPECT_RATIO:
      g_value_set_double (value, priv->aspect_ratio);
      break;

    case PROP_ANGLE:
      g_value_set_double (value, priv->angle);
      break;

    case PROP_INNER_LIMIT:
      g_value_set_double (value, priv->inner_limit);
      break;

    case PROP_MIDPOINT:
      g_value_set_double (value, priv->midpoint);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
gimp_tool_focus_changed (GimpToolWidget *widget)
{
  GimpToolFocus        *focus = GIMP_TOOL_FOCUS (widget);
  GimpToolFocusPrivate *priv  = focus->priv;
  gint                  i;

  for (i = 0; i < N_LIMITS; i++)
    {
      gimp_canvas_item_begin_change (priv->limits[i].item);

      g_object_set (priv->limits[i].item,
                    "type",         priv->type,
                    "x",            priv->x,
                    "y",            priv->y,
                    "aspect-ratio", priv->aspect_ratio,
                    "angle",        priv->angle,
                    NULL);
    }

  g_object_set (priv->limits[LIMIT_OUTER].item,
                "radius", priv->radius,
                NULL);

  g_object_set (priv->limits[LIMIT_INNER].item,
                "radius", priv->radius * priv->inner_limit,
                NULL);

  g_object_set (priv->limits[LIMIT_MIDDLE].item,
                "radius", priv->radius * (priv->inner_limit         +
                                          (1.0 - priv->inner_limit) *
                                          priv->midpoint),
                NULL);

  for (i = 0; i < N_LIMITS; i++)
    {
      gdouble rx, ry;
      gdouble max_r = 0.0;
      gint    j;

      gimp_canvas_limit_get_radii (GIMP_CANVAS_LIMIT (priv->limits[i].item),
                                   &rx, &ry);

      for (j = 0; j < priv->limits[i].n_handles; j++)
        {
          GimpVector2 p = priv->limits[i].handles[j].dir;
          gdouble     r;

          p.x *= rx;
          p.y *= ry;

          gimp_vector2_rotate (&p, -priv->angle);

          p.x += priv->x;
          p.y += priv->y;

          gimp_canvas_handle_set_position (priv->limits[i].handles[j].item,
                                           p.x, p.y);

          r = gimp_canvas_item_transform_distance (
            priv->limits[i].handles[j].item,
            priv->x, priv->y,
            p.x,     p.y);

          max_r = MAX (max_r, r);
        }

      for (j = 0; j < priv->limits[i].n_handles; j++)
        {
          gimp_canvas_item_set_visible (priv->limits[i].handles[j].item,
                                        priv->type != GIMP_LIMIT_HORIZONTAL &&
                                        priv->type != GIMP_LIMIT_VERTICAL   &&
                                        max_r >= 1.5 * HANDLE_SIZE);
        }

      gimp_canvas_item_end_change (priv->limits[i].item);
    }
}

static gint
gimp_tool_focus_button_press (GimpToolWidget      *widget,
                              const GimpCoords    *coords,
                              guint32              time,
                              GdkModifierType      state,
                              GimpButtonPressType  press_type)
{
  GimpToolFocus        *focus = GIMP_TOOL_FOCUS (widget);
  GimpToolFocusPrivate *priv  = focus->priv;

  if (press_type == GIMP_BUTTON_PRESS_NORMAL)
    {
      gimp_tool_focus_save (focus);

      priv->orig_x = coords->x;
      priv->orig_y = coords->y;

      return TRUE;
    }

  return FALSE;
}

static void
gimp_tool_focus_button_release (GimpToolWidget        *widget,
                                const GimpCoords      *coords,
                                guint32                time,
                                GdkModifierType        state,
                                GimpButtonReleaseType  release_type)
{
  GimpToolFocus *focus = GIMP_TOOL_FOCUS (widget);

  if (release_type == GIMP_BUTTON_RELEASE_CANCEL)
    gimp_tool_focus_restore (focus);
}

static void
gimp_tool_focus_motion (GimpToolWidget   *widget,
                        const GimpCoords *coords,
                        guint32           time,
                        GdkModifierType   state)
{
  GimpToolFocus        *focus = GIMP_TOOL_FOCUS (widget);
  GimpToolFocusPrivate *priv  = focus->priv;
  GimpDisplayShell     *shell = gimp_tool_widget_get_shell (widget);
  gboolean              extend;
  gboolean              constrain;

  extend    = state & gimp_get_extend_selection_mask ();
  constrain = state & gimp_get_constrain_behavior_mask ();

  switch (priv->hover)
    {
    case HOVER_NONE:
      break;

    case HOVER_LIMIT:
      {
        GimpCanvasItem *limit = priv->limits[priv->hover_limit].item;
        gdouble         radius;
        gdouble         outer_radius;
        gdouble         inner_radius;
        gdouble         x,  y;
        gdouble         cx, cy;

        x = coords->x;
        y = coords->y;

        gimp_canvas_limit_center_point (GIMP_CANVAS_LIMIT (limit),
                                        x,   y,
                                        &cx, &cy);

        if (gimp_canvas_item_transform_distance (limit,
                                                 x,  y,
                                                 cx, cy) <= SNAP_DISTANCE)
          {
            x = cx;
            y = cy;
          }

        if (fabs (fabs (priv->aspect_ratio) - 1.0) <= EPSILON)
          {
            if (priv->radius <= EPSILON)
              {
                g_object_set (focus,
                              "aspect-ratio", 0.0,
                              NULL);
              }
            else
              {
                break;
              }
          }

        radius = gimp_canvas_limit_boundary_radius (GIMP_CANVAS_LIMIT (limit),
                                                    x, y);

        outer_radius = priv->radius;
        inner_radius = priv->radius * priv->inner_limit;

        switch (priv->hover_limit)
          {
          case LIMIT_OUTER:
            {
              outer_radius = radius;

              if (extend)
                inner_radius = priv->inner_limit * radius;
              else
                outer_radius = MAX (outer_radius, inner_radius);
            }
            break;

          case LIMIT_INNER:
            {
              inner_radius = radius;

              if (extend)
                {
                  if (priv->inner_limit > EPSILON)
                    outer_radius = inner_radius / priv->inner_limit;
                  else
                    inner_radius = 0.0;
                }
              else
                {
                  inner_radius = MIN (inner_radius, outer_radius);
                }
            }
            break;

          case LIMIT_MIDDLE:
            {
              if (extend)
                {
                  if (priv->inner_limit > EPSILON || priv->midpoint > EPSILON)
                    {
                      outer_radius = radius / (priv->inner_limit         +
                                               (1.0 - priv->inner_limit) *
                                               priv->midpoint);
                      inner_radius = priv->inner_limit * outer_radius;
                    }
                  else
                    {
                      radius = 0.0;
                    }
                }
              else
                {
                  radius = CLAMP (radius, inner_radius, outer_radius);
                }

              if (fabs (outer_radius - inner_radius) > EPSILON)
                {
                  g_object_set (focus,
                                "midpoint", MAX ((radius       - inner_radius) /
                                                 (outer_radius - inner_radius),
                                                 0.0),
                                NULL);
                }
            }
            break;
          }

        g_object_set (focus,
                      "radius", outer_radius,
                      NULL);

        if (outer_radius > EPSILON)
          {
            g_object_set (focus,
                          "inner-limit", inner_radius / outer_radius,
                          NULL);
          }
      }
      break;

    case HOVER_HANDLE:
      {
        GimpToolFocusHandle *handle;
        GimpVector2          e;
        GimpVector2          s;
        GimpVector2          p;
        gdouble              rx, ry;
        gdouble              r;

        handle = &priv->limits[priv->hover_limit].handles[priv->hover_handle];

        e = handle->dir;

        gimp_vector2_rotate (&e, -priv->angle);

        s = e;

        gimp_canvas_limit_get_radii (
          GIMP_CANVAS_LIMIT (priv->limits[priv->hover_limit].item),
          &rx, &ry);

        if (handle->orientation == GTK_ORIENTATION_HORIZONTAL)
          gimp_vector2_mul (&s, ry);
        else
          gimp_vector2_mul (&s, rx);

        p.x = coords->x - priv->x;
        p.y = coords->y - priv->y;

        r = gimp_vector2_inner_product (&p, &e);
        r = MAX (r, 0.0);

        p = e;

        gimp_vector2_mul (&p, r);

        if (extend)
          {
            if (handle->orientation == GTK_ORIENTATION_HORIZONTAL)
              {
                if (rx <= EPSILON && ry > EPSILON)
                  break;

                ry = r * (1.0 - priv->aspect_ratio);
              }
            else
              {
                if (ry <= EPSILON && rx > EPSILON)
                  break;

                rx = r * (1.0 + priv->aspect_ratio);
              }
          }
        else
          {
            if (gimp_canvas_item_transform_distance (
                  priv->limits[priv->hover_limit].item,
                  s.x, s.y,
                  p.x, p.y) <= SNAP_DISTANCE * 0.75)
              {
                if (handle->orientation == GTK_ORIENTATION_HORIZONTAL)
                  r = ry;
                else
                  r = rx;
              }
          }

        if (handle->orientation == GTK_ORIENTATION_HORIZONTAL)
          rx = r;
        else
          ry = r;

        r = MAX (rx, ry);

        if (priv->hover_limit == LIMIT_INNER)
          r /= priv->inner_limit;

        g_object_set (focus,
                      "radius", r,
                      NULL);

        if (! extend)
          {
            gdouble aspect_ratio;

            if (fabs (rx - ry) <= EPSILON)
              aspect_ratio = 0.0;
            else if (rx > ry)
              aspect_ratio = 1.0 - ry / rx;
            else
              aspect_ratio = rx / ry - 1.0;

            g_object_set (focus,
                          "aspect-ratio", aspect_ratio,
                          NULL);
          }
      }
      break;

    case HOVER_MOVE:
      g_object_set (focus,
                    "x", priv->saved_x + (coords->x - priv->orig_x),
                    "y", priv->saved_y + (coords->y - priv->orig_y),
                    NULL);
      break;

    case HOVER_ROTATE:
      {
        gdouble angle;
        gdouble orig_angle;

        angle      = atan2 (coords->y    - priv->y, coords->x    - priv->x);
        orig_angle = atan2 (priv->orig_y - priv->y, priv->orig_x - priv->x);

        angle = priv->saved_angle + (angle - orig_angle);

        if (constrain)
          angle = gimp_display_shell_constrain_angle (shell, angle, 12);

        g_object_set (focus,
                      "angle", angle,
                      NULL);
      }
      break;
    }
}

static GimpHit
gimp_tool_focus_hit (GimpToolWidget   *widget,
                     const GimpCoords *coords,
                     GdkModifierType   state,
                     gboolean          proximity)
{
  GimpToolFocus        *focus = GIMP_TOOL_FOCUS (widget);
  GimpToolFocusPrivate *priv  = focus->priv;

  gimp_tool_focus_update_hover (focus, coords, proximity);

  switch (priv->hover)
    {
    case HOVER_NONE:
      return GIMP_HIT_NONE;

    case HOVER_LIMIT:
    case HOVER_HANDLE:
      return GIMP_HIT_DIRECT;

    case HOVER_MOVE:
    case HOVER_ROTATE:
      return GIMP_HIT_INDIRECT;
    }

  g_return_val_if_reached (GIMP_HIT_NONE);
}

static void
gimp_tool_focus_hover (GimpToolWidget   *widget,
                       const GimpCoords *coords,
                       GdkModifierType   state,
                       gboolean          proximity)
{
  GimpToolFocus *focus = GIMP_TOOL_FOCUS (widget);

  gimp_tool_focus_update_hover (focus, coords, proximity);

  gimp_tool_focus_update_highlight (focus);
  gimp_tool_focus_update_status    (focus, state);
}

static void
gimp_tool_focus_leave_notify (GimpToolWidget *widget)
{
  GimpToolFocus *focus = GIMP_TOOL_FOCUS (widget);

  gimp_tool_focus_update_hover (focus, NULL, FALSE);

  gimp_tool_focus_update_highlight (focus);

  GIMP_TOOL_WIDGET_CLASS (parent_class)->leave_notify (widget);
}

static void
gimp_tool_focus_motion_modifier (GimpToolWidget  *widget,
                                 GdkModifierType  key,
                                 gboolean         press,
                                 GdkModifierType  state)
{
  GimpToolFocus *focus = GIMP_TOOL_FOCUS (widget);

  gimp_tool_focus_update_status (focus, state);
}

static void
gimp_tool_focus_hover_modifier (GimpToolWidget  *widget,
                                GdkModifierType  key,
                                gboolean         press,
                                GdkModifierType  state)
{
  GimpToolFocus *focus = GIMP_TOOL_FOCUS (widget);

  gimp_tool_focus_update_status (focus, state);
}

static gboolean
gimp_tool_focus_get_cursor (GimpToolWidget     *widget,
                            const GimpCoords   *coords,
                            GdkModifierType     state,
                            GimpCursorType     *cursor,
                            GimpToolCursorType *tool_cursor,
                            GimpCursorModifier *modifier)
{
  GimpToolFocus        *focus = GIMP_TOOL_FOCUS (widget);
  GimpToolFocusPrivate *priv  = focus->priv;

  switch (priv->hover)
    {
    case HOVER_NONE:
      return FALSE;

    case HOVER_LIMIT:
    case HOVER_HANDLE:
      *modifier = GIMP_CURSOR_MODIFIER_RESIZE;
      return TRUE;

    case HOVER_MOVE:
      *modifier = GIMP_CURSOR_MODIFIER_MOVE;
      return TRUE;

    case HOVER_ROTATE:
      *modifier = GIMP_CURSOR_MODIFIER_ROTATE;
      return TRUE;
    }

  g_return_val_if_reached (FALSE);
}

static void
gimp_tool_focus_update_hover (GimpToolFocus    *focus,
                              const GimpCoords *coords,
                              gboolean          proximity)
{
  GimpToolFocusPrivate *priv            = focus->priv;
  gdouble               min_handle_dist = HANDLE_SIZE;
  gint                  i;

  priv->hover      = HOVER_NONE;
  priv->hover_item = NULL;

  if (! proximity)
    return;

  for (i = 0; i < N_LIMITS; i++)
    {
      gint j;

      for (j = 0; j < priv->limits[i].n_handles; j++)
        {
          GimpCanvasItem *handle = priv->limits[i].handles[j].item;

          if (gimp_canvas_item_get_visible (handle))
            {
              gdouble x, y;
              gdouble dist;

              g_object_get (handle,
                            "x", &x,
                            "y", &y,
                            NULL);

              dist = gimp_canvas_item_transform_distance (handle,
                                                          x,         y,
                                                          coords->x, coords->y);

              if (dist < min_handle_dist)
                {
                  min_handle_dist = dist;

                  priv->hover        = HOVER_HANDLE;
                  priv->hover_limit  = i;
                  priv->hover_handle = j;
                  priv->hover_item   = handle;
                }
            }
        }
    }

  if (priv->hover != HOVER_NONE)
    return;

  if (  gimp_canvas_limit_is_inside (
          GIMP_CANVAS_LIMIT (priv->limits[LIMIT_OUTER].item),
          coords->x, coords->y) &&
      ! gimp_canvas_limit_is_inside (
          GIMP_CANVAS_LIMIT (priv->limits[LIMIT_INNER].item),
          coords->x, coords->y))
    {
      if (gimp_canvas_item_hit (priv->limits[LIMIT_MIDDLE].item,
                                coords->x, coords->y))
        {
          priv->hover       = HOVER_LIMIT;
          priv->hover_limit = LIMIT_MIDDLE;
          priv->hover_item  = priv->limits[LIMIT_MIDDLE].item;

          return;
        }
    }

  if (! gimp_canvas_limit_is_inside (
          GIMP_CANVAS_LIMIT (priv->limits[LIMIT_INNER].item),
          coords->x, coords->y))
    {
      if (gimp_canvas_item_hit (priv->limits[LIMIT_OUTER].item,
                                coords->x, coords->y))
        {
          priv->hover       = HOVER_LIMIT;
          priv->hover_limit = LIMIT_OUTER;
          priv->hover_item  = priv->limits[LIMIT_OUTER].item;

          return;
        }
    }

  if (gimp_canvas_item_hit (priv->limits[LIMIT_INNER].item,
                            coords->x, coords->y))
    {
      priv->hover       = HOVER_LIMIT;
      priv->hover_limit = LIMIT_INNER;
      priv->hover_item  = priv->limits[LIMIT_INNER].item;

      return;
    }

  if (gimp_canvas_limit_is_inside (
        GIMP_CANVAS_LIMIT (priv->limits[LIMIT_OUTER].item),
        coords->x, coords->y))
    {
      priv->hover = HOVER_MOVE;
    }
  else
    {
      priv->hover = HOVER_ROTATE;
    }
}

static void
gimp_tool_focus_update_highlight (GimpToolFocus *focus)
{
  GimpToolWidget       *widget = GIMP_TOOL_WIDGET (focus);
  GimpToolFocusPrivate *priv   = focus->priv;
  gint                  i;

  if (priv->hover_item == priv->last_hover_item)
    return;

  if (priv->last_hover_item)
    gimp_canvas_item_set_highlight (priv->last_hover_item, FALSE);

  #define RAISE_ITEM(item)                           \
    G_STMT_START                                     \
      {                                              \
        g_object_ref (item);                         \
                                                     \
        gimp_tool_widget_remove_item (widget, item); \
        gimp_tool_widget_add_item    (widget, item); \
                                                     \
        g_object_unref (item);                       \
      }                                              \
    G_STMT_END

  for (i = N_LIMITS - 1; i >= 0; i--)
    RAISE_ITEM (GIMP_CANVAS_ITEM (priv->limits[i].group));

  if (priv->hover_item)
    {
      gimp_canvas_item_set_highlight (priv->hover_item, TRUE);

      RAISE_ITEM (GIMP_CANVAS_ITEM (priv->limits[priv->hover_limit].group));
    }

  #undef RAISE_ITEM

  priv->last_hover_item = priv->hover_item;
}

static void
gimp_tool_focus_update_status (GimpToolFocus   *focus,
                               GdkModifierType  state)
{
  GimpToolFocusPrivate *priv                    = focus->priv;
  GdkModifierType       state_mask              = 0;
  const gchar          *message                 = NULL;
  const gchar          *extend_selection_format = NULL;
  const gchar          *toggle_behavior_format  = NULL;
  gchar                *status;

  switch (priv->hover)
    {
    case HOVER_NONE:
      break;

    case HOVER_LIMIT:
      if (! (state & gimp_get_extend_selection_mask ()))
        {
          if (priv->hover_limit == LIMIT_MIDDLE)
            message                = _("Click-Drag to change the midpoint");
          else
            message                = _("Click-Drag to resize the limit");

          extend_selection_format  = _("%s to resize the focus");
          state_mask              |= gimp_get_extend_selection_mask ();
        }
      else
        {
          message                  = _("Click-Drag to resize the focus");
        }
      break;

    case HOVER_HANDLE:
      if (! (state & gimp_get_extend_selection_mask ()))
        {
          message                  = _("Click-Drag to change the aspect ratio");
          extend_selection_format  = _("%s to resize the focus");
          state_mask              |= gimp_get_extend_selection_mask ();
        }
      else
        {
          message                  = _("Click-Drag to resize the focus");
        }
      break;

    case HOVER_MOVE:
      message                      = _("Click-Drag to move the focus");
      break;

    case HOVER_ROTATE:
      message                      = _("Click-Drag to rotate the focus");
      toggle_behavior_format       = _("%s for constrained angles");
      state_mask                  |= gimp_get_constrain_behavior_mask ();
      break;
    }

  status = gimp_suggest_modifiers (message,
                                   ~state & state_mask,
                                   extend_selection_format,
                                   toggle_behavior_format,
                                   NULL);

  gimp_tool_widget_set_status (GIMP_TOOL_WIDGET (focus), status);

  g_free (status);
}

static void
gimp_tool_focus_save (GimpToolFocus *focus)
{
  GimpToolFocusPrivate *priv = focus->priv;

  priv->saved_x            = priv->x;
  priv->saved_y            = priv->y;
  priv->saved_radius       = priv->radius;
  priv->saved_aspect_ratio = priv->aspect_ratio;
  priv->saved_angle        = priv->angle;

  priv->saved_inner_limit  = priv->inner_limit;
  priv->saved_midpoint     = priv->midpoint;
}

static void
gimp_tool_focus_restore (GimpToolFocus *focus)
{
  GimpToolFocusPrivate *priv = focus->priv;

  g_object_set (focus,
                "x",            priv->saved_x,
                "y",            priv->saved_y,
                "radius",       priv->saved_radius,
                "aspect-ratio", priv->saved_aspect_ratio,
                "angle",        priv->saved_angle,

                "inner_limit",  priv->saved_inner_limit,
                "midpoint",     priv->saved_midpoint,

                NULL);
}


/*  public functions  */

GimpToolWidget *
gimp_tool_focus_new (GimpDisplayShell *shell)
{
  g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL);

  return g_object_new (GIMP_TYPE_TOOL_FOCUS,
                       "shell", shell,
                       NULL);
}

Messung
CHZG
81999892

¤ Dauer der Verarbeitung: 0.8 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.