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

Quelle  gimpnumberpairentry.c   Sprache: C

 

/* LIBGIMP - The GIMP Library
 * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
 *
 * gimpnumberpairentry.c
 * Copyright (C) 2006  Simon Budig       <simon@gimp.org>
 * Copyright (C) 2007  Sven Neumann      <sven@gimp.org>
 * Copyright (C) 2007  Martin Nordholts  <martin@svn.gnome.org>
 *
 * This library is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see
 * <https://www.gnu.org/licenses/>.
 */


#include "config.h"

#include <stdlib.h>
#include <string.h>

#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>

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

#include "gimpwidgetstypes.h"

#include "gimpicons.h"
#include "gimpnumberpairentry.h"


/**
 * SECTION: gimpnumberpairentry
 * @title: GimpNumberPairEntry
 * @short_description: A #GtkEntry subclass to enter ratios.
 *
 * A #GtkEntry subclass to enter ratios.
 **/



#define EPSILON 0.000001


enum
{
  NUMBERS_CHANGED,
  RATIO_CHANGED,
  LAST_SIGNAL
};

enum
{
  PROP_0,
  PROP_LEFT_NUMBER,
  PROP_RIGHT_NUMBER,
  PROP_DEFAULT_LEFT_NUMBER,
  PROP_DEFAULT_RIGHT_NUMBER,
  PROP_USER_OVERRIDE,
  PROP_SEPARATORS,
  PROP_DEFAULT_TEXT,
  PROP_ALLOW_SIMPLIFICATION,
  PROP_MIN_VALID_VALUE,
  PROP_MAX_VALID_VALUE,
  PROP_RATIO,
  PROP_ASPECT
};

typedef enum
{
  PARSE_VALID,
  PARSE_CLEAR,
  PARSE_INVALID
} ParseResult;


struct _GimpNumberPairEntry
{
  GtkEntry     parent_instance;

  /* The current number pair displayed in the widget. */
  gdouble      left_number;
  gdouble      right_number;

  /* What number pair that should be displayed when not in
     user_override mode. */

  gdouble      default_left_number;
  gdouble      default_right_number;

  /* Whether or not the current value in the entry has been explicitly
   * set by the user.
   */

  gboolean     user_override;

  /* Is the font style currently set to ITALIC or NORMAL ? */
  gboolean     font_italic;

  /* What separators that are valid when parsing input, e.g. when the
   * widget is used for aspect ratio, valid separators are typically
   * ':' and '/'.
   */

  gunichar    *separators;
  glong        num_separators;

  /* A string to be shown in the entry when in automatic mode */
  gchar       *default_text;

  /* Whether or to not to divide the numbers with the greatest common
   * divisor when input ends in '='.
   */

  gboolean     allow_simplification;

  /* What range of values considered valid. */
  gdouble      min_valid_value;
  gdouble      max_valid_value;
};


static void         gimp_number_pair_entry_finalize          (GObject             *entry);

static gboolean     gimp_number_pair_entry_valid_separator   (GimpNumberPairEntry *entry,
                                                              gunichar             candidate);
static void         gimp_number_pair_entry_ratio_to_fraction (gdouble              ratio,
                                                              gdouble             *numerator,
                                                              gdouble             *denominator);

static void         gimp_number_pair_entry_set_property      (GObject             *object,
                                                              guint                property_id,
                                                              const GValue        *value,
                                                              GParamSpec          *pspec);
static void         gimp_number_pair_entry_get_property      (GObject             *object,
                                                              guint                property_id,
                                                              GValue              *value,
                                                              GParamSpec          *pspec);
static void         gimp_number_pair_entry_changed           (GimpNumberPairEntry *entry);
static void         gimp_number_pair_entry_icon_press        (GimpNumberPairEntry *entry);
static gboolean     gimp_number_pair_entry_events            (GtkWidget           *widget,
                                                              GdkEvent            *event);

static void         gimp_number_pair_entry_update_text       (GimpNumberPairEntry *entry);

static ParseResult  gimp_number_pair_entry_parse_text        (GimpNumberPairEntry *entry,
                                                              const gchar         *text,
                                                              gdouble             *left_value,
                                                              gdouble             *right_value);
static gboolean     gimp_number_pair_entry_numbers_in_range  (GimpNumberPairEntry *entry,
                                                              gdouble              left_number,
                                                              gdouble              right_number);

static gchar *      gimp_number_pair_entry_strdup_number_pair_string
                                                             (GimpNumberPairEntry *entry,
                                                              gdouble left_number,
                                                              gdouble right_number);



G_DEFINE_TYPE (GimpNumberPairEntry, gimp_number_pair_entry, GTK_TYPE_ENTRY)


#define parent_class gimp_number_pair_entry_parent_class

/* What the user shall end the input with when simplification is desired. */
#define SIMPLIFICATION_CHAR  ((gunichar) '=')

#define DEFAULT_SEPARATOR    ((gunichar) ',')


static guint entry_signals[LAST_SIGNAL] = { 0 };


static void
gimp_number_pair_entry_class_init (GimpNumberPairEntryClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  entry_signals[NUMBERS_CHANGED] =
    g_signal_new ("numbers-changed",
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_FIRST,
                  0,
                  NULL, NULL, NULL,
                  G_TYPE_NONE, 0);

  entry_signals[RATIO_CHANGED] =
    g_signal_new ("ratio-changed",
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_FIRST,
                  0,
                  NULL, NULL, NULL,
                  G_TYPE_NONE, 0);

  object_class->set_property = gimp_number_pair_entry_set_property;
  object_class->get_property = gimp_number_pair_entry_get_property;
  object_class->finalize     = gimp_number_pair_entry_finalize;

  g_object_class_install_property (object_class, PROP_LEFT_NUMBER,
                                   g_param_spec_double ("left-number",
                                                        "Left number",
                                                        "The left number",
                                                        G_MINDOUBLE, G_MAXDOUBLE,
                                                        100.0,
                                                        GIMP_PARAM_READWRITE));

  g_object_class_install_property (object_class, PROP_RIGHT_NUMBER,
                                   g_param_spec_double ("right-number",
                                                        "Right number",
                                                        "The right number",
                                                        G_MINDOUBLE, G_MAXDOUBLE,
                                                        100.0,
                                                        GIMP_PARAM_READWRITE));

  g_object_class_install_property (object_class, PROP_DEFAULT_LEFT_NUMBER,
                                   g_param_spec_double ("default-left-number",
                                                        "Default left number",
                                                        "The default left number",
                                                        G_MINDOUBLE, G_MAXDOUBLE,
                                                        100.0,
                                                        GIMP_PARAM_READWRITE));

  g_object_class_install_property (object_class, PROP_DEFAULT_RIGHT_NUMBER,
                                   g_param_spec_double ("default-right-number",
                                                        "Default right number",
                                                        "The default right number",
                                                        G_MINDOUBLE, G_MAXDOUBLE,
                                                        100.0,
                                                        GIMP_PARAM_READWRITE));

  g_object_class_install_property (object_class, PROP_USER_OVERRIDE,
                                   g_param_spec_boolean ("user-override",
                                                         "User override",
                                                         "Whether the widget is in 'user override' mode",
                                                         FALSE,
                                                         GIMP_PARAM_READWRITE));

  g_object_class_install_property (object_class, PROP_SEPARATORS,
                                   g_param_spec_string ("separators",
                                                        "Separators",
                                                        "A string of valid separators",
                                                        NULL,
                                                        GIMP_PARAM_READWRITE |
                                                        G_PARAM_CONSTRUCT_ONLY));

  g_object_class_install_property (object_class, PROP_DEFAULT_TEXT,
                                   g_param_spec_string ("default-text",
                                                        "Default text",
                                                        "String to show when in automatic mode",
                                                        NULL,
                                                        GIMP_PARAM_READWRITE));

  g_object_class_install_property (object_class, PROP_ALLOW_SIMPLIFICATION,
                                   g_param_spec_boolean ("allow-simplification",
                                                         "Allow simplification",
                                                         "Whether to allow simplification",
                                                         FALSE,
                                                         GIMP_PARAM_READWRITE));

  g_object_class_install_property (object_class, PROP_MIN_VALID_VALUE,
                                   g_param_spec_double ("min-valid-value",
                                                        "Min valid value",
                                                        "Minimum value valid when parsing input",
                                                        G_MINDOUBLE, G_MAXDOUBLE,
                                                        G_MINDOUBLE,
                                                        GIMP_PARAM_READWRITE));

  g_object_class_install_property (object_class, PROP_MAX_VALID_VALUE,
                                   g_param_spec_double ("max-valid-value",
                                                        "Max valid value",
                                                        "Maximum value valid when parsing input",
                                                        G_MINDOUBLE, G_MAXDOUBLE,
                                                        G_MAXDOUBLE,
                                                        GIMP_PARAM_READWRITE));

  g_object_class_install_property (object_class, PROP_RATIO,
                                   g_param_spec_double ("ratio",
                                                        "Ratio",
                                                        "The value as ratio",
                                                        G_MINDOUBLE, G_MAXDOUBLE,
                                                        1.0,
                                                        GIMP_PARAM_READWRITE));

  g_object_class_install_property (object_class, PROP_ASPECT,
                                   g_param_spec_enum ("aspect",
                                                      "Aspect",
                                                      "The value as aspect",
                                                      GIMP_TYPE_ASPECT_TYPE,
                                                      GIMP_ASPECT_SQUARE,
                                                      GIMP_PARAM_READWRITE));
}

static void
gimp_number_pair_entry_init (GimpNumberPairEntry *entry)
{
  entry->left_number          = 1.0;
  entry->right_number         = 1.0;
  entry->default_left_number  = 1.0;
  entry->default_right_number = 1.0;
  entry->user_override        = FALSE;
  entry->font_italic          = FALSE;
  entry->separators           = NULL;
  entry->default_text         = NULL;
  entry->num_separators       = 0;
  entry->allow_simplification = FALSE;
  entry->min_valid_value      = G_MINDOUBLE;
  entry->max_valid_value      = G_MAXDOUBLE;

  g_signal_connect (entry, "changed",
                    G_CALLBACK (gimp_number_pair_entry_changed),
                    NULL);
  g_signal_connect (entry, "icon-press",
                    G_CALLBACK (gimp_number_pair_entry_icon_press),
                    NULL);
  g_signal_connect (entry, "focus-out-event",
                    G_CALLBACK (gimp_number_pair_entry_events),
                    NULL);
  g_signal_connect (entry, "key-press-event",
                    G_CALLBACK (gimp_number_pair_entry_events),
                    NULL);

  gtk_widget_set_direction (GTK_WIDGET (entry), GTK_TEXT_DIR_LTR);

  gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry),
                                     GTK_ENTRY_ICON_SECONDARY,
                                     GIMP_ICON_EDIT_CLEAR);
}

static void
gimp_number_pair_entry_finalize (GObject *object)
{
  GimpNumberPairEntry *entry = GIMP_NUMBER_PAIR_ENTRY (object);

  g_clear_pointer (&entry->separators, g_free);
  entry->num_separators = 0;

  g_clear_pointer (&entry->default_text, g_free);

  G_OBJECT_CLASS (parent_class)->finalize (object);
}

/**
 * gimp_number_pair_entry_new:
 * @separators:           The allowed separators.
 * @allow_simplification: Whether to do simplification on the entered term.
 * @min_valid_value:      The minimum allowed result value.
 * @max_valid_value:      The maximum allowed result value.
 *
 * Creates a new #GimpNumberPairEntry widget, which is a GtkEntry that
 * accepts two numbers separated by a separator. Typical input example
 * with a 'x' separator: "377x233".
 *
 * The widget supports simplification of the entered ratio when the
 * input ends in '=', if "allow-simplification" is TRUE.
 *
 * The "separators" property contains a string of characters valid as
 * separators when parsing input. The first separator is used when
 * displaying the current values.
 *
 * It is possible to specify what range of values that shall be
 * considered as valid when parsing user input, by changing
 * "min-valid-value" and "max-valid-value".
 *
 * The first separator of @separators is used to display the current
 * value.
 *
 * Returns: The new #GimpNumberPairEntry widget.
 *
 * Since: 2.4
 **/

GtkWidget *
gimp_number_pair_entry_new (const gchar *separators,
                            gboolean     allow_simplification,
                            gdouble      min_valid_value,
                            gdouble      max_valid_value)
{
  return g_object_new (GIMP_TYPE_NUMBER_PAIR_ENTRY,
                       "separators",           separators,
                       "allow-simplification", allow_simplification,
                       "min-valid-value",      min_valid_value,
                       "max-valid-value",      max_valid_value,
                       NULL);
}

static void
gimp_number_pair_entry_ratio_to_fraction (gdouble  ratio,
                                          gdouble *numerator,
                                          gdouble *denominator)
{
  gdouble  remainder, next_cf;
  gint     p0, p1, p2;
  gint     q0, q1, q2;

  /* calculate the continued fraction to approximate the desired ratio */

  p0 = 1;
  q0 = 0;
  p1 = floor (ratio);
  q1 = 1;

  remainder = ratio - p1;

  while (fabs (remainder) >= 0.0001 &&
         fabs (((gdouble) p1 / q1) - ratio) > 0.0001)
    {
      remainder = 1.0 / remainder;

      next_cf = floor (remainder);

      p2 = next_cf * p1 + p0;
      q2 = next_cf * q1 + q0;

      /* remember the last two fractions */
      p0 = p1;
      q0 = q1;
      p1 = p2;
      q1 = q2;

      remainder = remainder - next_cf;
    }

  /* only use the calculated fraction if it is "reasonable" */
  if (p1 < 1000 && q1 < 1000)
    {
      *numerator   = p1;
      *denominator = q1;
    }
  else
    {
      *numerator   = ratio;
      *denominator = 1.0;
    }
}

/**
 * gimp_number_pair_entry_set_ratio:
 * @entry: A #GimpNumberPairEntry widget.
 * @ratio: Ratio to set in the widget.
 *
 * Sets the numbers of the #GimpNumberPairEntry to have the desired
 * ratio. If the new ratio is different than the previous ratio, the
 * "ratio-changed" signal is emitted.
 *
 * An attempt is made to convert the decimal number into a fraction
 * with left_number and right_number < 1000.
 *
 * Since: 2.4
 **/

void
gimp_number_pair_entry_set_ratio (GimpNumberPairEntry *entry,
                                  gdouble              ratio)
{
  gdouble numerator;
  gdouble denominator;

  g_return_if_fail (GIMP_IS_NUMBER_PAIR_ENTRY (entry));

  gimp_number_pair_entry_ratio_to_fraction (ratio, &numerator, &denominator);

  gimp_number_pair_entry_set_values (entry, numerator, denominator);
}

/**
 * gimp_number_pair_entry_get_ratio:
 * @entry: A #GimpNumberPairEntry widget.
 *
 * Retrieves the ratio of the numbers displayed by a #GimpNumberPairEntry.
 *
 * Returns: The ratio value.
 *
 * Since: 2.4
 **/

gdouble
gimp_number_pair_entry_get_ratio (GimpNumberPairEntry *entry)
{
  g_return_val_if_fail (GIMP_IS_NUMBER_PAIR_ENTRY (entry), 1.0);

  return entry->left_number / entry->right_number;
}

/**
 * gimp_number_pair_entry_set_values:
 * @entry: A #GimpNumberPairEntry widget.
 * @left:  Left number in the entry.
 * @right: Right number in the entry.
 *
 * Forces setting the numbers displayed by a #GimpNumberPairEntry,
 * ignoring if the user has set their own value. The state of
 * user-override will not be changed.
 *
 * Since: 2.4
 **/

void
gimp_number_pair_entry_set_values (GimpNumberPairEntry *entry,
                                   gdouble              left,
                                   gdouble              right)
{
  GimpAspectType old_aspect;
  gdouble        old_ratio;
  gdouble        old_left_number;
  gdouble        old_right_number;
  gboolean       numbers_changed = FALSE;
  gboolean       ratio_changed   = FALSE;

  g_return_if_fail (GIMP_IS_NUMBER_PAIR_ENTRY (entry));

  /* Store current values */

  old_left_number  = entry->left_number;
  old_right_number = entry->right_number;
  old_ratio        = gimp_number_pair_entry_get_ratio (entry);
  old_aspect       = gimp_number_pair_entry_get_aspect (entry);


  /* Freeze notification */

  g_object_freeze_notify (G_OBJECT (entry));


  /* Set the new numbers and update the entry */

  entry->left_number  = left;
  entry->right_number = right;

  g_object_notify (G_OBJECT (entry), "left-number");
  g_object_notify (G_OBJECT (entry), "right-number");

  gimp_number_pair_entry_update_text (entry);


  /* Find out what has changed */

  if (fabs (old_ratio - gimp_number_pair_entry_get_ratio (entry)) > EPSILON)
    {
      g_object_notify (G_OBJECT (entry), "ratio");

      ratio_changed = TRUE;

      if (old_aspect != gimp_number_pair_entry_get_aspect (entry))
        g_object_notify (G_OBJECT (entry), "aspect");
    }

  if (old_left_number  != entry->left_number ||
      old_right_number != entry->right_number)
    {
      numbers_changed = TRUE;
    }


  /* Thaw */

  g_object_thaw_notify (G_OBJECT (entry));


  /* Emit relevant signals */

  if (numbers_changed)
    g_signal_emit (entry, entry_signals[NUMBERS_CHANGED], 0);

  if (ratio_changed)
    g_signal_emit (entry, entry_signals[RATIO_CHANGED], 0);
}

/**
 * gimp_number_pair_entry_get_values:
 * @entry: A #GimpNumberPairEntry widget.
 * @left:  (out) (optional): Location to store the left number (may be %NULL).
 * @right: (out) (optional): Location to store the right number (may be %NULL).
 *
 * Gets the numbers displayed by a #GimpNumberPairEntry.
 *
 * Since: 2.4
 **/

void
gimp_number_pair_entry_get_values (GimpNumberPairEntry *entry,
                                   gdouble             *left,
                                   gdouble             *right)
{
  g_return_if_fail (GIMP_IS_NUMBER_PAIR_ENTRY (entry));

  if (left != NULL)
    *left  = entry->left_number;

  if (right != NULL)
    *right = entry->right_number;
}

/**
 * gimp_number_pair_entry_set_default_text:
 * @entry:  A #GimpNumberPairEntry widget.
 * @string: Default string.
 *
 * Causes the entry to show a given string when in automatic mode,
 * instead of the default numbers. The only thing this does is making
 * the #GimpNumberPairEntry showing this string, the internal state
 * and API calls are not affected.
 *
 * Set the default string to %NULL to display default values as
 * normal.
 *
 * Since: 2.4
 */

void
gimp_number_pair_entry_set_default_text (GimpNumberPairEntry *entry,
                                         const gchar         *string)
{
  g_return_if_fail (GIMP_IS_NUMBER_PAIR_ENTRY (entry));

  g_free (entry->default_text);
  entry->default_text = g_strdup (string);

  gimp_number_pair_entry_update_text (entry);

  g_object_notify (G_OBJECT (entry), "default-text");
}

/**
 * gimp_number_pair_entry_get_default_text:
 * @entry:  A #GimpNumberPairEntry widget.
 *
 * Returns: (nullable): the string manually set to be shown,
 *          or %NULL if values are shown in a normal fashion.
 *
 * Since: 2.4
 */

const gchar *
gimp_number_pair_entry_get_default_text (GimpNumberPairEntry *entry)
{
  g_return_val_if_fail (GIMP_IS_NUMBER_PAIR_ENTRY (entry), NULL);

  return entry->default_text;
}

/**
 * gimp_number_pair_entry_set_aspect:
 * @entry:  A #GimpNumberPairEntry widget.
 * @aspect: The new aspect.
 *
 * Sets the aspect of the ratio by swapping the left_number and
 * right_number if necessary (or setting them to 1.0 in case that
 * @aspect is %GIMP_ASPECT_SQUARE).
 *
 * Since: 2.4
 **/

void
gimp_number_pair_entry_set_aspect (GimpNumberPairEntry *entry,
                                   GimpAspectType       aspect)
{
  g_return_if_fail (GIMP_IS_NUMBER_PAIR_ENTRY (entry));

  if (gimp_number_pair_entry_get_aspect (entry) == aspect)
    return;

  switch (aspect)
    {
    case GIMP_ASPECT_SQUARE:
      gimp_number_pair_entry_set_values (entry,
                                         entry->left_number,
                                         entry->left_number);
      break;

    case GIMP_ASPECT_LANDSCAPE:
    case GIMP_ASPECT_PORTRAIT:
      gimp_number_pair_entry_set_values (entry,
                                         entry->right_number,
                                         entry->left_number);
      break;
    }
}

/**
 * gimp_number_pair_entry_get_aspect:
 * @entry: A #GimpNumberPairEntry widget.
 *
 * Gets the aspect of the ratio displayed by a #GimpNumberPairEntry.
 *
 * Returns: The entry's current aspect.
 *
 * Since: 2.4
 **/

GimpAspectType
gimp_number_pair_entry_get_aspect (GimpNumberPairEntry *entry)
{
  g_return_val_if_fail (GIMP_IS_NUMBER_PAIR_ENTRY (entry), GIMP_ASPECT_SQUARE);

  if (entry->left_number > entry->right_number)
    {
      return GIMP_ASPECT_LANDSCAPE;
    }
  else if (entry->left_number < entry->right_number)
    {
      return GIMP_ASPECT_PORTRAIT;
    }
  else
    {
      return GIMP_ASPECT_SQUARE;
    }
}

static void
gimp_number_pair_entry_modify_font (GimpNumberPairEntry *entry,
                                    gboolean             italic)
{
  GtkStyleContext *style;

  if (entry->font_italic == italic)
    return;

  style = gtk_widget_get_style_context (GTK_WIDGET (entry));

  if (italic)
    gtk_style_context_add_class (style, "italic");
  else
    gtk_style_context_remove_class (style, "italic");

  gtk_entry_set_icon_sensitive (GTK_ENTRY (entry),
                                GTK_ENTRY_ICON_SECONDARY,
                                ! italic);

  entry->font_italic = italic;
}


/**
 * gimp_number_pair_entry_set_user_override:
 * @entry:         A #GimpNumberPairEntry widget.
 * @user_override: %TRUE sets the entry in user overridden mode,
 *                 %FALSE disables.
 *
 * When the entry is not in user overridden mode, the values will
 * change when the default values are changed. When in user overridden
 * mode, setting default values will not affect the active values.
 *
 * Since: 2.4
 **/

void
gimp_number_pair_entry_set_user_override (GimpNumberPairEntry *entry,
                                          gboolean             user_override)
{
  g_return_if_fail (GIMP_IS_NUMBER_PAIR_ENTRY (entry));

  entry->user_override = user_override;

  if (! user_override)
    {
      gimp_number_pair_entry_set_default_values (entry,
                                                 entry->default_left_number,
                                                 entry->default_right_number);
    }

  gimp_number_pair_entry_modify_font (entry, ! user_override);

  g_object_notify (G_OBJECT (entry), "user-override");
}

/**
 * gimp_number_pair_entry_get_user_override:
 * @entry: A #GimpNumberPairEntry widget.
 *
 * Returns: Whether or not the the widget is in user overridden mode.
 *
 * Since: 2.4
 **/

gboolean
gimp_number_pair_entry_get_user_override (GimpNumberPairEntry *entry)
{
  g_return_val_if_fail (GIMP_IS_NUMBER_PAIR_ENTRY (entry), FALSE);

  return entry->user_override;
}

static void
gimp_number_pair_entry_changed (GimpNumberPairEntry *entry)
{
  gimp_number_pair_entry_modify_font (entry, FALSE);
}

static void
gimp_number_pair_entry_icon_press (GimpNumberPairEntry *entry)
{
  gimp_number_pair_entry_set_user_override (entry, FALSE);

  gtk_editable_set_position (GTK_EDITABLE (entry), -1);
}

static gboolean
gimp_number_pair_entry_events (GtkWidget *widget,
                               GdkEvent  *event)
{
  GimpNumberPairEntry *entry = GIMP_NUMBER_PAIR_ENTRY (widget);
  gboolean             force_user_override;

  force_user_override = FALSE;

  switch (event->type)
    {
    case GDK_KEY_PRESS:
      {
        GdkEventKey *kevent = (GdkEventKey *) event;

        if (kevent->keyval != GDK_KEY_Return   &&
            kevent->keyval != GDK_KEY_KP_Enter &&
            kevent->keyval != GDK_KEY_ISO_Enter)
          break;

        /* If parsing was done due to widgets focus being lost, we only change
         * to user-override mode if the values differ from the default ones. If
         * Return was pressed however, we always switch to user-override mode.
         */

        force_user_override = TRUE;
      }
      /* Fall through */

    case GDK_FOCUS_CHANGE:
      {
        const gchar *text;
        ParseResult  parse_result;
        gdouble      left_value;
        gdouble      right_value;

        text = gtk_entry_get_text (GTK_ENTRY (entry));

        parse_result = gimp_number_pair_entry_parse_text (entry,
                                                          text,
                                                          &left_value,
                                                          &right_value);
        switch (parse_result)
          {
          case PARSE_VALID:
            {
              if (entry->left_number  != left_value  ||
                  entry->right_number != right_value ||
                  force_user_override)
                {
                  gimp_number_pair_entry_set_values (entry,
                                                     left_value,
                                                     right_value);

                  gimp_number_pair_entry_set_user_override (entry, TRUE);
                }
            }
            break;

          case PARSE_CLEAR:
            gimp_number_pair_entry_set_user_override (entry, FALSE);
            break;

          default:
            break;
          }

        /* Make sure the entry text is up to date */

        gimp_number_pair_entry_update_text (entry);

        gtk_editable_set_position (GTK_EDITABLE (entry), -1);
      }
      break;

    default:
      break;
    }

  return FALSE;
}

/**
 * gimp_number_pair_entry_strdup_number_pair_string:
 * @entry:
 * @left_number:
 * @right_number:
 *
 * Returns: allocated data, must be g_free:d.
 **/

static gchar *
gimp_number_pair_entry_strdup_number_pair_string (GimpNumberPairEntry *entry,
                                                  gdouble              left_number,
                                                  gdouble              right_number)
{
  gchar sep[8];
  gint  len;

  if (entry->num_separators > 0)
    len = g_unichar_to_utf8 (entry->separators[0], sep);
  else
    len = g_unichar_to_utf8 (DEFAULT_SEPARATOR, sep);

  sep[len] = '\0';

  return g_strdup_printf ("%g%s%g", left_number, sep, right_number);
}

static void
gimp_number_pair_entry_update_text (GimpNumberPairEntry *entry)
{
  gchar *buffer;

  if (! entry->user_override &&
      entry->default_text != NULL)
    {
      /* Instead of the numbers, show the string explicitly set by a
       * client to show when in automatic mode.
       */

      buffer = g_strdup (entry->default_text);
    }
  else
    {
      buffer = gimp_number_pair_entry_strdup_number_pair_string (entry,
                                                                 entry->left_number,
                                                                 entry->right_number);
    }

  g_signal_handlers_block_by_func (entry,
                                   gimp_number_pair_entry_changed, NULL);

  gtk_entry_set_text (GTK_ENTRY (entry), buffer);
  g_free (buffer);

  g_signal_handlers_unblock_by_func (entry,
                                     gimp_number_pair_entry_changed, NULL);

  gimp_number_pair_entry_modify_font (entry, ! entry->user_override);
}

static gboolean
gimp_number_pair_entry_valid_separator (GimpNumberPairEntry *entry,
                                        gunichar             candidate)
{
  if (entry->num_separators > 0)
    {
      gint i;

      for (i = 0; i < entry->num_separators; i++)
        if (entry->separators[i] == candidate)
          return TRUE;
    }
  else if (candidate == DEFAULT_SEPARATOR)
    {
      return TRUE;
    }

  return FALSE;
}

static ParseResult
gimp_number_pair_entry_parse_text (GimpNumberPairEntry *entry,
                                   const gchar         *text,
                                   gdouble             *left_value,
                                   gdouble             *right_value)
{
  gdouble   new_left_number;
  gdouble   new_right_number;
  gboolean  simplify = FALSE;
  gchar    *end;

  /* skip over whitespace */
  while (g_unichar_isspace (g_utf8_get_char (text)))
    text = g_utf8_next_char (text);

  /* check if clear */
  if (! *text)
    return PARSE_CLEAR;

  /* try to parse a number */
  new_left_number = strtod (text, &end);

  if (end == text)
    return PARSE_INVALID;
  else
    text = end;

  /* skip over whitespace */
  while (g_unichar_isspace (g_utf8_get_char (text)))
    text = g_utf8_next_char (text);

  /* check for a valid separator */
  if (! gimp_number_pair_entry_valid_separator (entry, g_utf8_get_char (text)))
    return PARSE_INVALID;
  else
    text = g_utf8_next_char (text);

  /* try to parse another number */
  new_right_number = strtod (text, &end);

  if (end == text)
    return PARSE_INVALID;
  else
    text = end;

  /* skip over whitespace */
  while (g_unichar_isspace (g_utf8_get_char (text)))
    text = g_utf8_next_char (text);

  /* check for the simplification char */
  if (g_utf8_get_char (text) == SIMPLIFICATION_CHAR)
    {
      simplify = entry->allow_simplification;
      text = g_utf8_next_char (text);
    }

  /* skip over whitespace */
  while (g_unichar_isspace (g_utf8_get_char (text)))
    text = g_utf8_next_char (text);

  /* check for trailing garbage */
  if (*text)
    return PARSE_INVALID;

  if (! gimp_number_pair_entry_numbers_in_range (entry,
                                                 new_left_number,
                                                 new_right_number))
    return PARSE_INVALID;

  if (simplify && new_right_number != 0.0)
    {
      gimp_number_pair_entry_ratio_to_fraction (new_left_number /
                                                new_right_number,
                                                left_value,
                                                right_value);
    }
  else
    {
      *left_value = new_left_number;
      *right_value = new_right_number;
    }

  return PARSE_VALID;
}

static void
gimp_number_pair_entry_set_property (GObject      *object,
                                     guint         property_id,
                                     const GValue *value,
                                     GParamSpec   *pspec)
{
  GimpNumberPairEntry        *entry = GIMP_NUMBER_PAIR_ENTRY (object);

  switch (property_id)
    {
    case PROP_LEFT_NUMBER:
      gimp_number_pair_entry_set_values (entry,
                                         g_value_get_double (value),
                                         entry->right_number);
      break;
    case PROP_RIGHT_NUMBER:
      gimp_number_pair_entry_set_values (entry,
                                         entry->left_number,
                                         g_value_get_double (value));
      break;
    case PROP_DEFAULT_LEFT_NUMBER:
      gimp_number_pair_entry_set_default_values (entry,
                                                 g_value_get_double (value),
                                                 entry->default_right_number);
      break;
    case PROP_DEFAULT_RIGHT_NUMBER:
      gimp_number_pair_entry_set_default_values (entry,
                                                 entry->default_left_number,
                                                 g_value_get_double (value));
      break;
    case PROP_USER_OVERRIDE:
      gimp_number_pair_entry_set_user_override (entry,
                                                g_value_get_boolean (value));
      break;
    case PROP_SEPARATORS:
      g_free (entry->separators);
      entry->num_separators = 0;
      if (g_value_get_string (value))
        entry->separators = g_utf8_to_ucs4 (g_value_get_string (value), -1,
                                           NULL, &entry->num_separators, NULL);
      else
        entry->separators = NULL;
      break;
    case PROP_DEFAULT_TEXT:
      gimp_number_pair_entry_set_default_text (entry,
                                               g_value_get_string (value));
      break;
    case PROP_ALLOW_SIMPLIFICATION:
      entry->allow_simplification = g_value_get_boolean (value);
      break;
    case PROP_MIN_VALID_VALUE:
      entry->min_valid_value = g_value_get_double (value);
      break;
    case PROP_MAX_VALID_VALUE:
      entry->max_valid_value = g_value_get_double (value);
      break;
    case PROP_RATIO:
      gimp_number_pair_entry_set_ratio (entry, g_value_get_double (value));
      break;
    case PROP_ASPECT:
      gimp_number_pair_entry_set_aspect (entry, g_value_get_enum (value));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
gimp_number_pair_entry_get_property (GObject    *object,
                                     guint       property_id,
                                     GValue     *value,
                                     GParamSpec *pspec)
{
  GimpNumberPairEntry *entry = GIMP_NUMBER_PAIR_ENTRY (object);

  switch (property_id)
    {
    case PROP_LEFT_NUMBER:
      g_value_set_double (value, entry->left_number);
      break;
    case PROP_RIGHT_NUMBER:
      g_value_set_double (value, entry->right_number);
      break;
    case PROP_DEFAULT_LEFT_NUMBER:
      g_value_set_double (value, entry->default_left_number);
      break;
    case PROP_DEFAULT_RIGHT_NUMBER:
      g_value_set_double (value, entry->default_right_number);
      break;
    case PROP_USER_OVERRIDE:
      g_value_set_boolean (value, entry->user_override);
      break;
    case PROP_SEPARATORS:
      g_value_take_string (value,
                           g_ucs4_to_utf8 (entry->separators,
                                           entry->num_separators,
                                           NULL, NULL, NULL));
      break;
    case PROP_ALLOW_SIMPLIFICATION:
      g_value_set_boolean (value, entry->allow_simplification);
      break;
    case PROP_DEFAULT_TEXT:
      g_value_set_string (value, entry->default_text);
      break;
    case PROP_MIN_VALID_VALUE:
      g_value_set_double (value, entry->min_valid_value);
      break;
    case PROP_MAX_VALID_VALUE:
      g_value_set_double (value, entry->max_valid_value);
      break;
    case PROP_RATIO:
      g_value_set_double (value, gimp_number_pair_entry_get_ratio (entry));
      break;
    case PROP_ASPECT:
      g_value_set_enum (value, gimp_number_pair_entry_get_aspect (entry));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

/**
 * gimp_number_pair_entry_set_default_values:
 * @entry: A #GimpNumberPairEntry widget.
 * @left:  Default left value in the entry.
 * @right: Default right value in the entry.
 *
 * Since: 2.4
 **/

void
gimp_number_pair_entry_set_default_values (GimpNumberPairEntry *entry,
                                           gdouble              left,
                                           gdouble              right)
{
  g_return_if_fail (GIMP_IS_NUMBER_PAIR_ENTRY (entry));

  entry->default_left_number  = left;
  entry->default_right_number = right;

  if (! entry->user_override)
    {
      gimp_number_pair_entry_set_values (entry,
                                         entry->default_left_number,
                                         entry->default_right_number);
    }
}

/**
 * gimp_number_pair_entry_get_default_values:
 * @entry: A #GimpNumberPairEntry widget.
 * @left:  (out) (optional): Location to put the default left value, or %NULL.
 * @right: (out) (optional): Location to put the default right value, or %NULL.
 *
 * Since: 2.4
 **/
void
gimp_number_pair_entry_get_default_values (GimpNumberPairEntry *entry,
                                           gdouble             *left,
                                           gdouble             *right)
{
  g_return_if_fail (GIMP_IS_NUMBER_PAIR_ENTRY (entry));

  if (left != NULL)
    *left = entry->default_left_number;

  if (right != NULL)
    *right = entry->default_right_number;
}

static gboolean
gimp_number_pair_entry_numbers_in_range (GimpNumberPairEntry *entry,
                                         gdouble              left_number,
                                         gdouble              right_number)
{
  return (left_number  >= entry->min_valid_value &&
          left_number  <= entry->max_valid_value &&
          right_number >= entry->min_valid_value &&
          right_number <= entry->max_valid_value);
}

Messung
CHZG
979410096

¤ Dauer der Verarbeitung: 0.19 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.