gimp/libgimpwidgets/gimpsizeentry.c
Alx Sa ff1a92b81e libgimpwidgets: Fix infinite loop on appending invalid input
gimp_size_entry_eevl_unit_resolver () loops through all valid units
to find a match for user's inputted value in GimpSizeEntry.
It runs until gimp_unit_get_by_id () returns NULL, where it does a
final check on GIMP_UNIT_PERCENT.
Due to a small logic error, we kept setting the GimpUnit to
gimp_unit_percent () each time it was NULL, so the loop ran forever.
Per Jehan, this patch breaks the logic up so that we terminate the
loop once the percent check fails.
2024-10-02 23:21:05 +00:00

1702 lines
55 KiB
C

/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpsizeentry.c
* Copyright (C) 1999-2000 Sven Neumann <sven@gimp.org>
* Michael Natterer <mitch@gimp.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 <string.h>
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "gimpwidgets.h"
#include "gimpeevl.h"
#include "gimpsizeentry.h"
/**
* SECTION: gimpsizeentry
* @title: GimpSizeEntry
* @short_description: Widget for entering pixel values and resolutions.
* @see_also: #GimpUnit, #GimpUnitComboBox, gimp_coordinates_new()
*
* This widget is used to enter pixel distances/sizes and resolutions.
*
* You can specify the number of fields the widget should provide. For
* each field automatic mappings are performed between the field's
* "reference value" and its "value".
*
* There is a #GimpUnitComboBox right of the entry fields which lets
* you specify the #GimpUnit of the displayed values.
*
* For each field, there can be one or two #GtkSpinButton's to enter
* "value" and "reference value". If you specify @show_refval as
* %FALSE in gimp_size_entry_new() there will be only one
* #GtkSpinButton and the #GimpUnitComboBox will contain an item for
* selecting GIMP_UNIT_PIXEL.
*
* The "reference value" is either of GIMP_UNIT_PIXEL or dpi,
* depending on which #GimpSizeEntryUpdatePolicy you specify in
* gimp_size_entry_new(). The "value" is either the size in pixels
* mapped to the size in a real-world-unit (see #GimpUnit) or the dpi
* value mapped to pixels per real-world-unit.
**/
#define SIZE_MAX_VALUE 500000.0
#define GIMP_SIZE_ENTRY_DIGITS(unit) (MIN (gimp_unit_get_digits (unit), 5) + 1)
enum
{
VALUE_CHANGED,
REFVAL_CHANGED,
UNIT_CHANGED,
LAST_SIGNAL
};
typedef struct _GimpSizeEntryField
{
GimpSizeEntry *gse;
gdouble resolution;
gdouble lower;
gdouble upper;
GtkAdjustment *value_adjustment;
GtkWidget *value_spinbutton;
gdouble value;
gdouble min_value;
gdouble max_value;
GtkAdjustment *refval_adjustment;
GtkWidget *refval_spinbutton;
gdouble refval;
gdouble min_refval;
gdouble max_refval;
gint refval_digits;
gint stop_recursion;
} GimpSizeEntryField;
typedef struct _GimpSizeEntryPrivate
{
GSList *fields;
gint number_of_fields;
GtkWidget *unit_combo;
GimpUnit *unit;
gboolean menu_show_pixels;
gboolean menu_show_percent;
gboolean show_refval;
GimpSizeEntryUpdatePolicy update_policy;
} GimpSizeEntryPrivate;
static void gimp_size_entry_finalize (GObject *object);
static void gimp_size_entry_update_value (GimpSizeEntryField *gsef,
gdouble value);
static void gimp_size_entry_value_callback (GtkAdjustment *adjustment,
gpointer data);
static void gimp_size_entry_update_refval (GimpSizeEntryField *gsef,
gdouble refval);
static void gimp_size_entry_refval_callback (GtkAdjustment *adjustment,
gpointer data);
static void gimp_size_entry_update_unit (GimpSizeEntry *gse,
GimpUnit *unit);
static void gimp_size_entry_unit_callback (GtkWidget *widget,
GimpSizeEntry *sizeentry);
static void gimp_size_entry_attach_eevl (GtkSpinButton *spin_button,
GimpSizeEntryField *gsef);
static gint gimp_size_entry_eevl_input_callback (GtkSpinButton *spinner,
gdouble *return_val,
gpointer *data);
static gboolean gimp_size_entry_eevl_unit_resolver (const gchar *ident,
GimpEevlQuantity *factor,
gdouble *offset,
gpointer data);
G_DEFINE_TYPE_WITH_PRIVATE (GimpSizeEntry, gimp_size_entry, GTK_TYPE_GRID)
#define parent_class gimp_size_entry_parent_class
static guint gimp_size_entry_signals[LAST_SIGNAL] = { 0 };
static void
gimp_size_entry_class_init (GimpSizeEntryClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
gimp_size_entry_signals[VALUE_CHANGED] =
g_signal_new ("value-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpSizeEntryClass, value_changed),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
gimp_size_entry_signals[REFVAL_CHANGED] =
g_signal_new ("refval-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpSizeEntryClass, refval_changed),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
gimp_size_entry_signals[UNIT_CHANGED] =
g_signal_new ("unit-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpSizeEntryClass, unit_changed),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
object_class->finalize = gimp_size_entry_finalize;
klass->value_changed = NULL;
klass->refval_changed = NULL;
klass->unit_changed = NULL;
}
static void
gimp_size_entry_init (GimpSizeEntry *gse)
{
GimpSizeEntryPrivate *priv;
priv = gimp_size_entry_get_instance_private (gse);
priv->unit = gimp_unit_pixel ();
priv->menu_show_pixels = TRUE;
priv->menu_show_percent = TRUE;
priv->show_refval = FALSE;
priv->update_policy = GIMP_SIZE_ENTRY_UPDATE_NONE;
}
static void
gimp_size_entry_finalize (GObject *object)
{
GimpSizeEntry *gse = GIMP_SIZE_ENTRY (object);
GimpSizeEntryPrivate *priv;
priv = gimp_size_entry_get_instance_private (gse);
if (priv->fields)
{
GSList *list;
for (list = priv->fields; list; list = list->next)
g_slice_free (GimpSizeEntryField, list->data);
g_slist_free (priv->fields);
priv->fields = NULL;
}
G_OBJECT_CLASS (parent_class)->finalize (object);
}
/**
* gimp_size_entry_new:
* @number_of_fields: The number of input fields.
* @unit: The initial unit.
* @unit_format: A printf-like unit-format string as is used with
* gimp_unit_menu_new().
* @menu_show_pixels: %TRUE if the unit menu should contain an item for
* GIMP_UNIT_PIXEL (ignored if the @update_policy is not
* GIMP_SIZE_ENTRY_UPDATE_NONE).
* @menu_show_percent: %TRUE if the unit menu should contain an item for
* GIMP_UNIT_PERCENT.
* @show_refval: %TRUE if you want an extra "reference value"
* spinbutton per input field.
* @spinbutton_width: The minimal horizontal size of the #GtkSpinButton's.
* @update_policy: How the automatic pixel <-> real-world-unit
* calculations should be done.
*
* Creates a new #GimpSizeEntry widget.
*
* To have all automatic calculations performed correctly, set up the
* widget in the following order:
*
* 1. gimp_size_entry_new()
*
* 2. (for each additional input field) gimp_size_entry_add_field()
*
* 3. gimp_size_entry_set_unit()
*
* For each input field:
*
* 4. gimp_size_entry_set_resolution()
*
* 5. gimp_size_entry_set_refval_boundaries()
* (or gimp_size_entry_set_value_boundaries())
*
* 6. gimp_size_entry_set_size()
*
* 7. gimp_size_entry_set_refval() (or gimp_size_entry_set_value())
*
* The #GimpSizeEntry is derived from #GtkGrid and will have
* an empty border of one cell width on each side plus an empty column left
* of the #GimpUnitComboBox to allow the caller to add labels or a
* #GimpChainButton.
*
* Returns: A Pointer to the new #GimpSizeEntry widget.
**/
GtkWidget *
gimp_size_entry_new (gint number_of_fields,
GimpUnit *unit,
const gchar *unit_format,
gboolean menu_show_pixels,
gboolean menu_show_percent,
gboolean show_refval,
gint spinbutton_width,
GimpSizeEntryUpdatePolicy update_policy)
{
GimpSizeEntry *gse;
GimpSizeEntryPrivate *priv;
GimpUnitStore *store;
gint i;
g_return_val_if_fail (GIMP_IS_UNIT (unit), NULL);
g_return_val_if_fail ((number_of_fields >= 0) && (number_of_fields <= 16),
NULL);
gse = g_object_new (GIMP_TYPE_SIZE_ENTRY, NULL);
priv = gimp_size_entry_get_instance_private (gse);
priv->number_of_fields = number_of_fields;
priv->unit = unit;
priv->show_refval = show_refval;
priv->update_policy = update_policy;
/* show the 'pixels' menu entry only if we are a 'size' sizeentry and
* don't have the reference value spinbutton
*/
if ((update_policy == GIMP_SIZE_ENTRY_UPDATE_RESOLUTION) ||
(show_refval == TRUE))
priv->menu_show_pixels = FALSE;
else
priv->menu_show_pixels = menu_show_pixels;
/* show the 'percent' menu entry only if we are a 'size' sizeentry
*/
if (update_policy == GIMP_SIZE_ENTRY_UPDATE_RESOLUTION)
priv->menu_show_percent = FALSE;
else
priv->menu_show_percent = menu_show_percent;
for (i = 0; i < number_of_fields; i++)
{
GimpSizeEntryField *gsef = g_slice_new0 (GimpSizeEntryField);
gint digits;
priv->fields = g_slist_append (priv->fields, gsef);
gsef->gse = gse;
gsef->resolution = 1.0; /* just to avoid division by zero */
gsef->lower = 0.0;
gsef->upper = 100.0;
gsef->value = 0;
gsef->min_value = 0;
gsef->max_value = SIZE_MAX_VALUE;
gsef->refval_adjustment = NULL;
gsef->value_adjustment = NULL;
gsef->refval = 0;
gsef->min_refval = 0;
gsef->max_refval = SIZE_MAX_VALUE;
gsef->refval_digits =
(update_policy == GIMP_SIZE_ENTRY_UPDATE_SIZE) ? 0 : 3;
gsef->stop_recursion = 0;
digits = ((unit == gimp_unit_pixel ()) ?
gsef->refval_digits : ((unit == gimp_unit_percent ()) ?
2 : GIMP_SIZE_ENTRY_DIGITS (unit)));
gsef->value_adjustment = gtk_adjustment_new (gsef->value,
gsef->min_value,
gsef->max_value,
1.0, 10.0, 0.0);
gsef->value_spinbutton = gimp_spin_button_new (gsef->value_adjustment,
1.0, digits);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (gsef->value_spinbutton),
TRUE);
gimp_size_entry_attach_eevl (GTK_SPIN_BUTTON (gsef->value_spinbutton),
gsef);
if (spinbutton_width > 0)
{
if (spinbutton_width < 17)
gtk_entry_set_width_chars (GTK_ENTRY (gsef->value_spinbutton),
spinbutton_width);
else
gtk_widget_set_size_request (gsef->value_spinbutton,
spinbutton_width, -1);
}
gtk_grid_attach (GTK_GRID (gse), gsef->value_spinbutton,
i+1, priv->show_refval+1, 1, 1);
g_signal_connect (gsef->value_adjustment, "value-changed",
G_CALLBACK (gimp_size_entry_value_callback),
gsef);
gtk_widget_show (gsef->value_spinbutton);
if (priv->show_refval)
{
gsef->refval_adjustment = gtk_adjustment_new (gsef->refval,
gsef->min_refval,
gsef->max_refval,
1.0, 10.0, 0.0);
gsef->refval_spinbutton = gimp_spin_button_new (gsef->refval_adjustment,
1.0,
gsef->refval_digits);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (gsef->refval_spinbutton),
TRUE);
gtk_widget_set_size_request (gsef->refval_spinbutton,
spinbutton_width, -1);
gtk_grid_attach (GTK_GRID (gse), gsef->refval_spinbutton,
i + 1, 1, 1, 1);
g_signal_connect (gsef->refval_adjustment,
"value-changed",
G_CALLBACK (gimp_size_entry_refval_callback),
gsef);
gtk_widget_show (gsef->refval_spinbutton);
}
if (priv->menu_show_pixels && (unit == gimp_unit_pixel ()) &&
! priv->show_refval)
gtk_spin_button_set_digits (GTK_SPIN_BUTTON (gsef->value_spinbutton),
gsef->refval_digits);
}
store = gimp_unit_store_new (priv->number_of_fields);
gimp_unit_store_set_has_pixels (store, priv->menu_show_pixels);
gimp_unit_store_set_has_percent (store, priv->menu_show_percent);
if (unit_format)
{
gchar *short_format = g_strdup (unit_format);
gchar *p;
while ((p = strstr (short_format, "%n")))
strcpy (p, "%a");
g_object_set (store,
"short-format", short_format,
"long-format", unit_format,
NULL);
g_free (short_format);
}
priv->unit_combo = gimp_unit_combo_box_new_with_model (store);
g_object_unref (store);
gimp_unit_combo_box_set_active (GIMP_UNIT_COMBO_BOX (priv->unit_combo), unit);
gtk_grid_attach (GTK_GRID (gse), priv->unit_combo,
i+2, priv->show_refval+1, 1, 1);
g_signal_connect (priv->unit_combo, "changed",
G_CALLBACK (gimp_size_entry_unit_callback),
gse);
gtk_widget_show (priv->unit_combo);
return GTK_WIDGET (gse);
}
/**
* gimp_size_entry_add_field:
* @gse: The sizeentry you want to add a field to.
* @value_spinbutton: The spinbutton to display the field's value.
* @refval_spinbutton: (nullable): The spinbutton to display the field's reference value.
*
* Adds an input field to the #GimpSizeEntry.
*
* The new input field will have the index 0. If you specified @show_refval
* as %TRUE in gimp_size_entry_new() you have to pass an additional
* #GtkSpinButton to hold the reference value. If @show_refval was %FALSE,
* @refval_spinbutton will be ignored.
**/
void
gimp_size_entry_add_field (GimpSizeEntry *gse,
GtkSpinButton *value_spinbutton,
GtkSpinButton *refval_spinbutton)
{
GimpSizeEntryPrivate *priv;
GimpSizeEntryField *gsef;
gint digits;
g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
g_return_if_fail (GTK_IS_SPIN_BUTTON (value_spinbutton));
priv = gimp_size_entry_get_instance_private (gse);
if (priv->show_refval)
{
g_return_if_fail (GTK_IS_SPIN_BUTTON (refval_spinbutton));
}
gsef = g_slice_new0 (GimpSizeEntryField);
priv->fields = g_slist_prepend (priv->fields, gsef);
priv->number_of_fields++;
gsef->gse = gse;
gsef->resolution = 1.0; /* just to avoid division by zero */
gsef->lower = 0.0;
gsef->upper = 100.0;
gsef->value = 0;
gsef->min_value = 0;
gsef->max_value = SIZE_MAX_VALUE;
gsef->refval = 0;
gsef->min_refval = 0;
gsef->max_refval = SIZE_MAX_VALUE;
gsef->refval_digits =
(priv->update_policy == GIMP_SIZE_ENTRY_UPDATE_SIZE) ? 0 : 3;
gsef->stop_recursion = 0;
gsef->value_adjustment = gtk_spin_button_get_adjustment (value_spinbutton);
gsef->value_spinbutton = GTK_WIDGET (value_spinbutton);
g_signal_connect (gsef->value_adjustment, "value-changed",
G_CALLBACK (gimp_size_entry_value_callback),
gsef);
gimp_size_entry_attach_eevl (GTK_SPIN_BUTTON (gsef->value_spinbutton),
gsef);
if (priv->show_refval)
{
gsef->refval_adjustment = gtk_spin_button_get_adjustment (refval_spinbutton);
gsef->refval_spinbutton = GTK_WIDGET (refval_spinbutton);
g_signal_connect (gsef->refval_adjustment, "value-changed",
G_CALLBACK (gimp_size_entry_refval_callback),
gsef);
}
digits = ((priv->unit == gimp_unit_pixel ()) ? gsef->refval_digits :
(priv->unit == gimp_unit_percent ()) ? 2 :
GIMP_SIZE_ENTRY_DIGITS (priv->unit));
gtk_spin_button_set_digits (GTK_SPIN_BUTTON (value_spinbutton), digits);
if (priv->menu_show_pixels &&
!priv->show_refval &&
(priv->unit == gimp_unit_pixel ()))
{
gtk_spin_button_set_digits (GTK_SPIN_BUTTON (gsef->value_spinbutton),
gsef->refval_digits);
}
}
GimpSizeEntryUpdatePolicy
gimp_size_entry_get_update_policy (GimpSizeEntry *gse)
{
GimpSizeEntryPrivate *priv;
g_return_val_if_fail (GIMP_IS_SIZE_ENTRY (gse), GIMP_SIZE_ENTRY_UPDATE_SIZE);
priv = gimp_size_entry_get_instance_private (gse);
return priv->update_policy;
}
gint
gimp_size_entry_get_n_fields (GimpSizeEntry *gse)
{
GimpSizeEntryPrivate *priv;
g_return_val_if_fail (GIMP_IS_SIZE_ENTRY (gse), 0);
priv = gimp_size_entry_get_instance_private (gse);
return priv->number_of_fields;
}
/**
* gimp_size_entry_get_unit_combo:
* @gse: a #GimpSizeEntry.
*
* Returns: (transfer none) (type GimpUnitComboBox): the size entry's #GimpUnitComboBox.
**/
GtkWidget *
gimp_size_entry_get_unit_combo (GimpSizeEntry *gse)
{
GimpSizeEntryPrivate *priv;
g_return_val_if_fail (GIMP_IS_SIZE_ENTRY (gse), NULL);
priv = gimp_size_entry_get_instance_private (gse);
return priv->unit_combo;
}
/**
* gimp_size_entry_attach_label:
* @gse: The sizeentry you want to add a label to.
* @text: The text of the label.
* @row: The row where the label will be attached.
* @column: The column where the label will be attached.
* @alignment: The horizontal alignment of the label.
*
* Attaches a #GtkLabel to the #GimpSizeEntry (which is a #GtkGrid).
*
* Returns: (transfer none): A pointer to the new #GtkLabel widget.
**/
GtkWidget *
gimp_size_entry_attach_label (GimpSizeEntry *gse,
const gchar *text,
gint row,
gint column,
gfloat alignment)
{
GtkWidget *label;
g_return_val_if_fail (GIMP_IS_SIZE_ENTRY (gse), NULL);
g_return_val_if_fail (text != NULL, NULL);
label = gtk_label_new_with_mnemonic (text);
if (column == 0)
{
GList *children;
GList *list;
children = gtk_container_get_children (GTK_CONTAINER (gse));
for (list = children; list; list = g_list_next (list))
{
GtkWidget *child = list->data;
gint left_attach;
gint top_attach;
gtk_container_child_get (GTK_CONTAINER (gse), child,
"left-attach", &left_attach,
"top-attach", &top_attach,
NULL);
if (left_attach == 1 && top_attach == row)
{
gtk_label_set_mnemonic_widget (GTK_LABEL (label), child);
break;
}
}
g_list_free (children);
}
gtk_label_set_xalign (GTK_LABEL (label), alignment);
gtk_grid_attach (GTK_GRID (gse), label, column, row, 1, 1);
gtk_widget_show (label);
return label;
}
/**
* gimp_size_entry_set_resolution:
* @gse: The sizeentry you want to set a resolution for.
* @field: The index of the field you want to set the resolution for.
* @resolution: The new resolution (in dpi) for the chosen @field.
* @keep_size: %TRUE if the @field's size in pixels should stay the same.
* %FALSE if the @field's size in units should stay the same.
*
* Sets the resolution (in dpi) for field # @field of the #GimpSizeEntry.
*
* The @resolution passed will be clamped to fit in
* [#GIMP_MIN_RESOLUTION..#GIMP_MAX_RESOLUTION].
*
* This function does nothing if the #GimpSizeEntryUpdatePolicy specified in
* gimp_size_entry_new() doesn't equal to #GIMP_SIZE_ENTRY_UPDATE_SIZE.
**/
void
gimp_size_entry_set_resolution (GimpSizeEntry *gse,
gint field,
gdouble resolution,
gboolean keep_size)
{
GimpSizeEntryPrivate *priv;
GimpSizeEntryField *gsef;
gfloat val;
g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
priv = gimp_size_entry_get_instance_private (gse);
g_return_if_fail ((field >= 0) && (field < priv->number_of_fields));
resolution = CLAMP (resolution, GIMP_MIN_RESOLUTION, GIMP_MAX_RESOLUTION);
gsef = (GimpSizeEntryField*) g_slist_nth_data (priv->fields, field);
gsef->resolution = resolution;
val = gsef->value;
gsef->stop_recursion = 0;
gimp_size_entry_set_refval_boundaries (gse, field,
gsef->min_refval, gsef->max_refval);
if (! keep_size)
gimp_size_entry_set_value (gse, field, val);
}
/**
* gimp_size_entry_set_size:
* @gse: The sizeentry you want to set a size for.
* @field: The index of the field you want to set the size for.
* @lower: The reference value which will be treated as 0%.
* @upper: The reference value which will be treated as 100%.
*
* Sets the pixel values for field # @field of the #GimpSizeEntry
* which will be treated as 0% and 100%.
*
* These values will be used if you specified @menu_show_percent as %TRUE
* in gimp_size_entry_new() and the user has selected GIMP_UNIT_PERCENT in
* the #GimpSizeEntry's #GimpUnitComboBox.
*
* This function does nothing if the #GimpSizeEntryUpdatePolicy specified in
* gimp_size_entry_new() doesn't equal to GIMP_SIZE_ENTRY_UPDATE_SIZE.
**/
void
gimp_size_entry_set_size (GimpSizeEntry *gse,
gint field,
gdouble lower,
gdouble upper)
{
GimpSizeEntryPrivate *priv;
GimpSizeEntryField *gsef;
g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
priv = gimp_size_entry_get_instance_private (gse);
g_return_if_fail ((field >= 0) && (field < priv->number_of_fields));
g_return_if_fail (lower <= upper);
gsef = (GimpSizeEntryField*) g_slist_nth_data (priv->fields, field);
gsef->lower = lower;
gsef->upper = upper;
gimp_size_entry_set_refval (gse, field, gsef->refval);
}
/**
* gimp_size_entry_set_value_boundaries:
* @gse: The sizeentry you want to set value boundaries for.
* @field: The index of the field you want to set value boundaries for.
* @lower: The new lower boundary of the value of the chosen @field.
* @upper: The new upper boundary of the value of the chosen @field.
*
* Limits the range of possible values which can be entered in field # @field
* of the #GimpSizeEntry.
*
* The current value of the @field will be clamped to fit in the @field's
* new boundaries.
*
* NOTE: In most cases you won't be interested in this function because the
* #GimpSizeEntry's purpose is to shield the programmer from unit
* calculations. Use gimp_size_entry_set_refval_boundaries() instead.
* Whatever you do, don't mix these calls. A size entry should either
* be clamped by the value or the reference value.
**/
void
gimp_size_entry_set_value_boundaries (GimpSizeEntry *gse,
gint field,
gdouble lower,
gdouble upper)
{
GimpSizeEntryPrivate *priv;
GimpSizeEntryField *gsef;
g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
priv = gimp_size_entry_get_instance_private (gse);
g_return_if_fail ((field >= 0) && (field < priv->number_of_fields));
g_return_if_fail (lower <= upper);
gsef = (GimpSizeEntryField*) g_slist_nth_data (priv->fields, field);
gsef->min_value = lower;
gsef->max_value = upper;
g_object_freeze_notify (G_OBJECT (gsef->value_adjustment));
gtk_adjustment_set_lower (gsef->value_adjustment, gsef->min_value);
gtk_adjustment_set_upper (gsef->value_adjustment, gsef->max_value);
if (gsef->stop_recursion) /* this is a hack (but useful ;-) */
{
g_object_thaw_notify (G_OBJECT (gsef->value_adjustment));
return;
}
gsef->stop_recursion++;
switch (priv->update_policy)
{
case GIMP_SIZE_ENTRY_UPDATE_NONE:
break;
case GIMP_SIZE_ENTRY_UPDATE_SIZE:
if (priv->unit == gimp_unit_pixel ())
gimp_size_entry_set_refval_boundaries (gse, field,
gsef->min_value,
gsef->max_value);
else if (priv->unit == gimp_unit_percent ())
gimp_size_entry_set_refval_boundaries (gse, field,
gsef->lower +
(gsef->upper - gsef->lower) *
gsef->min_value / 100,
gsef->lower +
(gsef->upper - gsef->lower) *
gsef->max_value / 100);
else
gimp_size_entry_set_refval_boundaries (gse, field,
gsef->min_value *
gsef->resolution /
gimp_unit_get_factor (priv->unit),
gsef->max_value *
gsef->resolution /
gimp_unit_get_factor (priv->unit));
break;
case GIMP_SIZE_ENTRY_UPDATE_RESOLUTION:
gimp_size_entry_set_refval_boundaries (gse, field,
gsef->min_value *
gimp_unit_get_factor (priv->unit),
gsef->max_value *
gimp_unit_get_factor (priv->unit));
break;
default:
break;
}
gsef->stop_recursion--;
gimp_size_entry_set_value (gse, field, gsef->value);
g_object_thaw_notify (G_OBJECT (gsef->value_adjustment));
}
/**
* gimp_size_entry_get_value:
* @gse: The sizeentry you want to know a value of.
* @field: The index of the field you want to know the value of.
*
* Returns the value of field # @field of the #GimpSizeEntry.
*
* The @value returned is a distance or resolution
* in the #GimpUnit the user has selected in the #GimpSizeEntry's
* #GimpUnitComboBox.
*
* NOTE: In most cases you won't be interested in this value because the
* #GimpSizeEntry's purpose is to shield the programmer from unit
* calculations. Use gimp_size_entry_get_refval() instead.
*
* Returns: The value of the chosen @field.
**/
gdouble
gimp_size_entry_get_value (GimpSizeEntry *gse,
gint field)
{
GimpSizeEntryPrivate *priv;
GimpSizeEntryField *gsef;
g_return_val_if_fail (GIMP_IS_SIZE_ENTRY (gse), 0);
priv = gimp_size_entry_get_instance_private (gse);
g_return_val_if_fail ((field >= 0) && (field < priv->number_of_fields), 0);
gsef = (GimpSizeEntryField *) g_slist_nth_data (priv->fields, field);
return gsef->value;
}
static void
gimp_size_entry_update_value (GimpSizeEntryField *gsef,
gdouble value)
{
GimpSizeEntryPrivate *priv = gimp_size_entry_get_instance_private (gsef->gse);
if (gsef->stop_recursion > 1)
return;
gsef->value = value;
switch (priv->update_policy)
{
case GIMP_SIZE_ENTRY_UPDATE_NONE:
break;
case GIMP_SIZE_ENTRY_UPDATE_SIZE:
if (priv->unit == gimp_unit_pixel ())
gsef->refval = value;
else if (priv->unit == gimp_unit_percent ())
gsef->refval = CLAMP (gsef->lower + (gsef->upper - gsef->lower) * value / 100,
gsef->min_refval, gsef->max_refval);
else
gsef->refval = CLAMP (value * gsef->resolution /
gimp_unit_get_factor (priv->unit),
gsef->min_refval, gsef->max_refval);
if (priv->show_refval)
gtk_adjustment_set_value (gsef->refval_adjustment, gsef->refval);
break;
case GIMP_SIZE_ENTRY_UPDATE_RESOLUTION:
gsef->refval =
CLAMP (value * gimp_unit_get_factor (priv->unit),
gsef->min_refval, gsef->max_refval);
if (priv->show_refval)
gtk_adjustment_set_value (gsef->refval_adjustment, gsef->refval);
break;
default:
break;
}
g_signal_emit (gsef->gse, gimp_size_entry_signals[VALUE_CHANGED], 0);
}
/**
* gimp_size_entry_set_value:
* @gse: The sizeentry you want to set a value for.
* @field: The index of the field you want to set a value for.
* @value: The new value for @field.
*
* Sets the value for field # @field of the #GimpSizeEntry.
*
* The @value passed is treated to be a distance or resolution
* in the #GimpUnit the user has selected in the #GimpSizeEntry's
* #GimpUnitComboBox.
*
* NOTE: In most cases you won't be interested in this value because the
* #GimpSizeEntry's purpose is to shield the programmer from unit
* calculations. Use gimp_size_entry_set_refval() instead.
**/
void
gimp_size_entry_set_value (GimpSizeEntry *gse,
gint field,
gdouble value)
{
GimpSizeEntryPrivate *priv;
GimpSizeEntryField *gsef;
g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
priv = gimp_size_entry_get_instance_private (gse);
g_return_if_fail ((field >= 0) && (field < priv->number_of_fields));
gsef = (GimpSizeEntryField *) g_slist_nth_data (priv->fields, field);
value = CLAMP (value, gsef->min_value, gsef->max_value);
gtk_adjustment_set_value (gsef->value_adjustment, value);
gimp_size_entry_update_value (gsef, value);
}
static void
gimp_size_entry_value_callback (GtkAdjustment *adjustment,
gpointer data)
{
GimpSizeEntryField *gsef;
gdouble new_value;
gsef = (GimpSizeEntryField *) data;
new_value = gtk_adjustment_get_value (adjustment);
if (gsef->value != new_value)
gimp_size_entry_update_value (gsef, new_value);
}
/**
* gimp_size_entry_set_refval_boundaries:
* @gse: The sizeentry you want to set the reference value boundaries for.
* @field: The index of the field you want to set the reference value
* boundaries for.
* @lower: The new lower boundary of the reference value of the chosen @field.
* @upper: The new upper boundary of the reference value of the chosen @field.
*
* Limits the range of possible reference values which can be entered in
* field # @field of the #GimpSizeEntry.
*
* The current reference value of the @field will be clamped to fit in the
* @field's new boundaries.
**/
void
gimp_size_entry_set_refval_boundaries (GimpSizeEntry *gse,
gint field,
gdouble lower,
gdouble upper)
{
GimpSizeEntryPrivate *priv;
GimpSizeEntryField *gsef;
g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
priv = gimp_size_entry_get_instance_private (gse);
g_return_if_fail ((field >= 0) && (field < priv->number_of_fields));
g_return_if_fail (lower <= upper);
gsef = (GimpSizeEntryField *) g_slist_nth_data (priv->fields, field);
gsef->min_refval = lower;
gsef->max_refval = upper;
if (priv->show_refval)
{
g_object_freeze_notify (G_OBJECT (gsef->refval_adjustment));
gtk_adjustment_set_lower (gsef->refval_adjustment, gsef->min_refval);
gtk_adjustment_set_upper (gsef->refval_adjustment, gsef->max_refval);
}
if (gsef->stop_recursion) /* this is a hack (but useful ;-) */
{
if (priv->show_refval)
g_object_thaw_notify (G_OBJECT (gsef->refval_adjustment));
return;
}
gsef->stop_recursion++;
switch (priv->update_policy)
{
case GIMP_SIZE_ENTRY_UPDATE_NONE:
break;
case GIMP_SIZE_ENTRY_UPDATE_SIZE:
if (priv->unit == gimp_unit_pixel ())
gimp_size_entry_set_value_boundaries (gse, field,
gsef->min_refval,
gsef->max_refval);
else if (priv->unit == gimp_unit_percent ())
gimp_size_entry_set_value_boundaries (gse, field,
100 * (gsef->min_refval -
gsef->lower) /
(gsef->upper - gsef->lower),
100 * (gsef->max_refval -
gsef->lower) /
(gsef->upper - gsef->lower));
else
gimp_size_entry_set_value_boundaries (gse, field,
gsef->min_refval *
gimp_unit_get_factor (priv->unit) /
gsef->resolution,
gsef->max_refval *
gimp_unit_get_factor (priv->unit) /
gsef->resolution);
break;
case GIMP_SIZE_ENTRY_UPDATE_RESOLUTION:
gimp_size_entry_set_value_boundaries (gse, field,
gsef->min_refval /
gimp_unit_get_factor (priv->unit),
gsef->max_refval /
gimp_unit_get_factor (priv->unit));
break;
default:
break;
}
gsef->stop_recursion--;
gimp_size_entry_set_refval (gse, field, gsef->refval);
if (priv->show_refval)
g_object_thaw_notify (G_OBJECT (gsef->refval_adjustment));
}
/**
* gimp_size_entry_set_refval_digits:
* @gse: The sizeentry you want to set the reference value digits for.
* @field: The index of the field you want to set the reference value for.
* @digits: The new number of decimal digits for the #GtkSpinButton which
* displays @field's reference value.
*
* Sets the decimal digits of field # @field of the #GimpSizeEntry to
* @digits.
*
* If you don't specify this value explicitly, the reference value's number
* of digits will equal to 0 for #GIMP_SIZE_ENTRY_UPDATE_SIZE and to 2 for
* #GIMP_SIZE_ENTRY_UPDATE_RESOLUTION.
**/
void
gimp_size_entry_set_refval_digits (GimpSizeEntry *gse,
gint field,
gint digits)
{
GimpSizeEntryPrivate *priv;
GimpSizeEntryField *gsef;
g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
priv = gimp_size_entry_get_instance_private (gse);
g_return_if_fail ((field >= 0) && (field < priv->number_of_fields));
g_return_if_fail ((digits >= 0) && (digits <= 6));
gsef = (GimpSizeEntryField*) g_slist_nth_data (priv->fields, field);
gsef->refval_digits = digits;
if (priv->update_policy == GIMP_SIZE_ENTRY_UPDATE_SIZE)
{
if (priv->show_refval)
gtk_spin_button_set_digits (GTK_SPIN_BUTTON (gsef->refval_spinbutton),
gsef->refval_digits);
else if (priv->unit == gimp_unit_pixel ())
gtk_spin_button_set_digits (GTK_SPIN_BUTTON (gsef->value_spinbutton),
gsef->refval_digits);
}
}
/**
* gimp_size_entry_get_refval:
* @gse: The sizeentry you want to know a reference value of.
* @field: The index of the field you want to know the reference value of.
*
* Returns the reference value for field # @field of the #GimpSizeEntry.
*
* The reference value is either a distance in pixels or a resolution
* in dpi, depending on which #GimpSizeEntryUpdatePolicy you chose in
* gimp_size_entry_new().
*
* Returns: The reference value of the chosen @field.
**/
gdouble
gimp_size_entry_get_refval (GimpSizeEntry *gse,
gint field)
{
GimpSizeEntryPrivate *priv;
GimpSizeEntryField *gsef;
/* return 1.0 to avoid division by zero */
g_return_val_if_fail (GIMP_IS_SIZE_ENTRY (gse), 1.0);
priv = gimp_size_entry_get_instance_private (gse);
g_return_val_if_fail ((field >= 0) && (field < priv->number_of_fields), 1.0);
gsef = (GimpSizeEntryField*) g_slist_nth_data (priv->fields, field);
return gsef->refval;
}
static void
gimp_size_entry_update_refval (GimpSizeEntryField *gsef,
gdouble refval)
{
GimpSizeEntryPrivate *priv = gimp_size_entry_get_instance_private (gsef->gse);
if (gsef->stop_recursion > 1)
return;
gsef->refval = refval;
switch (priv->update_policy)
{
case GIMP_SIZE_ENTRY_UPDATE_NONE:
break;
case GIMP_SIZE_ENTRY_UPDATE_SIZE:
if (priv->unit == gimp_unit_pixel ())
gsef->value = refval;
else if (priv->unit == gimp_unit_percent ())
gsef->value = CLAMP (100 * (refval - gsef->lower) / (gsef->upper - gsef->lower),
gsef->min_value, gsef->max_value);
else
gsef->value = CLAMP (refval * gimp_unit_get_factor (priv->unit) /
gsef->resolution,
gsef->min_value, gsef->max_value);
gtk_adjustment_set_value (gsef->value_adjustment, gsef->value);
break;
case GIMP_SIZE_ENTRY_UPDATE_RESOLUTION:
gsef->value =
CLAMP (refval / gimp_unit_get_factor (priv->unit),
gsef->min_value, gsef->max_value);
gtk_adjustment_set_value (gsef->value_adjustment, gsef->value);
break;
default:
break;
}
g_signal_emit (gsef->gse, gimp_size_entry_signals[REFVAL_CHANGED], 0);
}
/**
* gimp_size_entry_set_refval:
* @gse: The sizeentry you want to set a reference value for.
* @field: The index of the field you want to set the reference value for.
* @refval: The new reference value for @field.
*
* Sets the reference value for field # @field of the #GimpSizeEntry.
*
* The @refval passed is either a distance in pixels or a resolution in dpi,
* depending on which #GimpSizeEntryUpdatePolicy you chose in
* gimp_size_entry_new().
**/
void
gimp_size_entry_set_refval (GimpSizeEntry *gse,
gint field,
gdouble refval)
{
GimpSizeEntryPrivate *priv;
GimpSizeEntryField *gsef;
g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
priv = gimp_size_entry_get_instance_private (gse);
g_return_if_fail ((field >= 0) && (field < priv->number_of_fields));
gsef = (GimpSizeEntryField *) g_slist_nth_data (priv->fields, field);
refval = CLAMP (refval, gsef->min_refval, gsef->max_refval);
if (priv->show_refval)
gtk_adjustment_set_value (gsef->refval_adjustment, refval);
gimp_size_entry_update_refval (gsef, refval);
}
static void
gimp_size_entry_refval_callback (GtkAdjustment *adjustment,
gpointer data)
{
GimpSizeEntryField *gsef;
gdouble new_refval;
gsef = (GimpSizeEntryField *) data;
new_refval = gtk_adjustment_get_value (adjustment);
if (gsef->refval != new_refval)
gimp_size_entry_update_refval (gsef, new_refval);
}
/**
* gimp_size_entry_get_unit:
* @gse: The sizeentry you want to know the unit of.
*
* Returns the #GimpUnit the user has selected in the #GimpSizeEntry's
* #GimpUnitComboBox.
*
* Returns: (transfer none): The sizeentry's unit.
**/
GimpUnit *
gimp_size_entry_get_unit (GimpSizeEntry *gse)
{
GimpSizeEntryPrivate *priv;
g_return_val_if_fail (GIMP_IS_SIZE_ENTRY (gse), gimp_unit_inch ());
priv = gimp_size_entry_get_instance_private (gse);
return priv->unit;
}
static void
gimp_size_entry_update_unit (GimpSizeEntry *gse,
GimpUnit *unit)
{
GimpSizeEntryPrivate *priv = gimp_size_entry_get_instance_private (gse);
GimpSizeEntryField *gsef;
gint i;
gint digits;
priv->unit = unit;
digits = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (gse),
"gimp-pixel-digits"));
for (i = 0; i < priv->number_of_fields; i++)
{
gsef = (GimpSizeEntryField *) g_slist_nth_data (priv->fields, i);
if (priv->update_policy == GIMP_SIZE_ENTRY_UPDATE_SIZE)
{
if (unit == gimp_unit_pixel ())
gtk_spin_button_set_digits (GTK_SPIN_BUTTON (gsef->value_spinbutton),
gsef->refval_digits + digits);
else if (unit == gimp_unit_percent ())
gtk_spin_button_set_digits (GTK_SPIN_BUTTON (gsef->value_spinbutton),
2 + digits);
else
gtk_spin_button_set_digits (GTK_SPIN_BUTTON (gsef->value_spinbutton),
GIMP_SIZE_ENTRY_DIGITS (unit) + digits);
}
else if (priv->update_policy == GIMP_SIZE_ENTRY_UPDATE_RESOLUTION)
{
digits = (gimp_unit_get_digits (gimp_unit_inch ()) - gimp_unit_get_digits (unit));
gtk_spin_button_set_digits (GTK_SPIN_BUTTON (gsef->value_spinbutton),
MAX (3 + digits, 3));
}
gsef->stop_recursion = 0; /* hack !!! */
gimp_size_entry_set_refval_boundaries (gse, i,
gsef->min_refval,
gsef->max_refval);
}
g_signal_emit (gse, gimp_size_entry_signals[UNIT_CHANGED], 0);
}
/**
* gimp_size_entry_set_unit:
* @gse: The sizeentry you want to change the unit for.
* @unit: The new unit.
*
* Sets the #GimpSizeEntry's unit. The reference value for all fields will
* stay the same but the value in units or pixels per unit will change
* according to which #GimpSizeEntryUpdatePolicy you chose in
* gimp_size_entry_new().
**/
void
gimp_size_entry_set_unit (GimpSizeEntry *gse,
GimpUnit *unit)
{
GimpSizeEntryPrivate *priv;
g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
priv = gimp_size_entry_get_instance_private (gse);
g_return_if_fail (priv->menu_show_pixels || (unit != gimp_unit_pixel ()));
g_return_if_fail (priv->menu_show_percent || (unit != gimp_unit_percent ()));
gimp_unit_combo_box_set_active (GIMP_UNIT_COMBO_BOX (priv->unit_combo), unit);
gimp_size_entry_update_unit (gse, unit);
}
static void
gimp_size_entry_unit_callback (GtkWidget *widget,
GimpSizeEntry *gse)
{
GimpSizeEntryPrivate *priv;
GimpUnit *new_unit;
new_unit = gimp_unit_combo_box_get_active (GIMP_UNIT_COMBO_BOX (widget));
priv = gimp_size_entry_get_instance_private (gse);
if (priv->unit != new_unit)
gimp_size_entry_update_unit (gse, new_unit);
}
/**
* gimp_size_entry_attach_eevl:
* @spin_button: one of the size_entry's spinbuttons.
* @gsef: a size entry field.
*
* Hooks in the GimpEevl unit expression parser into the
* #GtkSpinButton of the #GimpSizeEntryField.
**/
static void
gimp_size_entry_attach_eevl (GtkSpinButton *spin_button,
GimpSizeEntryField *gsef)
{
gtk_spin_button_set_numeric (spin_button, FALSE);
gtk_spin_button_set_update_policy (spin_button, GTK_UPDATE_IF_VALID);
g_signal_connect_after (spin_button, "input",
G_CALLBACK (gimp_size_entry_eevl_input_callback),
gsef);
}
static gint
gimp_size_entry_eevl_input_callback (GtkSpinButton *spinner,
gdouble *return_val,
gpointer *data)
{
GimpSizeEntryField *gsef = (GimpSizeEntryField *) data;
GimpSizeEntryPrivate *priv;
GimpEevlOptions options = GIMP_EEVL_OPTIONS_INIT;
gboolean success = FALSE;
const gchar *error_pos = NULL;
GError *error = NULL;
GimpEevlQuantity result;
g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spinner), FALSE);
g_return_val_if_fail (GIMP_IS_SIZE_ENTRY (gsef->gse), FALSE);
priv = gimp_size_entry_get_instance_private (gsef->gse);
options.unit_resolver_proc = gimp_size_entry_eevl_unit_resolver;
options.data = data;
/* enable ratio expressions when there are two fields */
if (priv->number_of_fields == 2)
{
GimpSizeEntryField *other_gsef;
GimpEevlQuantity default_unit_factor;
gdouble default_unit_offset;
options.ratio_expressions = TRUE;
if (gsef == priv->fields->data)
{
other_gsef = priv->fields->next->data;
options.ratio_invert = FALSE;
}
else
{
other_gsef = priv->fields->data;
options.ratio_invert = TRUE;
}
options.unit_resolver_proc (NULL,
&default_unit_factor, &default_unit_offset,
options.data);
options.ratio_quantity.value = other_gsef->value /
default_unit_factor.value;
options.ratio_quantity.dimension = default_unit_factor.dimension;
}
success = gimp_eevl_evaluate (gtk_entry_get_text (GTK_ENTRY (spinner)),
&options,
&result,
&error_pos,
&error);
if (! success)
{
if (error && error_pos)
{
g_printerr ("ERROR: %s at '%s'\n",
error->message,
*error_pos ? error_pos : "<End of input>");
}
else
{
g_printerr ("ERROR: Expression evaluation failed without error.\n");
}
g_clear_error (&error);
gtk_widget_error_bell (GTK_WIDGET (spinner));
return GTK_INPUT_ERROR;
}
else if (result.dimension != 1 && priv->unit != gimp_unit_percent ())
{
g_printerr ("ERROR: result has wrong dimension (expected 1, got %d)\n", result.dimension);
gtk_widget_error_bell (GTK_WIDGET (spinner));
return GTK_INPUT_ERROR;
}
else if (result.dimension != 0 && priv->unit == gimp_unit_percent ())
{
g_printerr ("ERROR: result has wrong dimension (expected 0, got %d)\n", result.dimension);
gtk_widget_error_bell (GTK_WIDGET (spinner));
return GTK_INPUT_ERROR;
}
else
{
/* transform back to UI-unit */
GimpEevlQuantity ui_unit;
GtkAdjustment *adj;
gdouble val;
if (priv->unit == gimp_unit_pixel ())
{
ui_unit.value = gsef->resolution;
ui_unit.dimension = 1;
}
else if (priv->unit == gimp_unit_percent ())
{
ui_unit.value = 1.0;
ui_unit.dimension = 0;
}
else
{
ui_unit.value = gimp_unit_get_factor(priv->unit);
ui_unit.dimension = 1;
}
*return_val = result.value * ui_unit.value;
/* CLAMP() to adjustment bounds, or too large/small values
* will make the validation machinery revert to the old value.
* See bug #694477.
*/
adj = gtk_spin_button_get_adjustment (spinner);
val = CLAMP (*return_val,
gtk_adjustment_get_lower (adj),
gtk_adjustment_get_upper (adj));
if (val != *return_val)
{
gtk_widget_error_bell (GTK_WIDGET (spinner));
*return_val = val;
}
return TRUE;
}
}
static gboolean
gimp_size_entry_eevl_unit_resolver (const gchar *identifier,
GimpEevlQuantity *factor,
gdouble *offset,
gpointer data)
{
GimpSizeEntryField *gsef = (GimpSizeEntryField *) data;
GimpSizeEntryPrivate *priv;
gboolean resolve_default_unit = (identifier == NULL);
GimpUnit *unit = gimp_unit_pixel ();
gint i = 1;
g_return_val_if_fail (gsef, FALSE);
g_return_val_if_fail (factor != NULL, FALSE);
g_return_val_if_fail (offset != NULL, FALSE);
g_return_val_if_fail (GIMP_IS_SIZE_ENTRY (gsef->gse), FALSE);
priv = gimp_size_entry_get_instance_private (gsef->gse);
*offset = 0.0;
while (unit != NULL)
{
if ((resolve_default_unit && unit == priv->unit) ||
(identifier &&
(strcmp (gimp_unit_get_symbol (unit), identifier) == 0 ||
strcmp (gimp_unit_get_abbreviation (unit), identifier) == 0)))
{
if (unit == gimp_unit_percent ())
{
if (priv->unit == gimp_unit_percent ())
{
factor->value = 1;
factor->dimension = 0;
}
else
{
/* gsef->upper contains the '100%'-value */
factor->value = 100*gsef->resolution/(gsef->upper - gsef->lower);
/* gsef->lower contains the '0%'-value */
*offset = gsef->lower/gsef->resolution;
factor->dimension = 1;
}
/* return here, don't perform percentage conversion */
return TRUE;
}
else if (unit == gimp_unit_pixel ())
{
factor->value = gsef->resolution;
}
else
{
factor->value = gimp_unit_get_factor (unit);
}
if (priv->unit == gimp_unit_percent ())
{
/* map non-percentages onto percent */
factor->value = gsef->upper/(100*gsef->resolution);
factor->dimension = 0;
}
else
{
factor->dimension = 1;
}
/* We are done */
return TRUE;
}
/* Hack to handle percent within the loop */
if (unit == gimp_unit_percent ())
break;
unit = gimp_unit_get_by_id (i++);
if (unit == NULL)
unit = gimp_unit_percent ();
}
return FALSE;
}
/**
* gimp_size_entry_show_unit_menu:
* @gse: a #GimpSizeEntry
* @show: Boolean
*
* Controls whether a unit menu is shown in the size entry. If
* @show is %TRUE, the menu is shown; otherwise it is hidden.
*
* Since: 2.4
**/
void
gimp_size_entry_show_unit_menu (GimpSizeEntry *gse,
gboolean show)
{
GimpSizeEntryPrivate *priv;
g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
priv = gimp_size_entry_get_instance_private (gse);
gtk_widget_set_visible (priv->unit_combo, show);
}
/**
* gimp_size_entry_set_pixel_digits:
* @gse: a #GimpSizeEntry
* @digits: the number of digits to display for a pixel size
*
* This function allows you set up a #GimpSizeEntry so that sub-pixel
* sizes can be entered.
**/
void
gimp_size_entry_set_pixel_digits (GimpSizeEntry *gse,
gint digits)
{
GimpSizeEntryPrivate *priv;
GimpUnitComboBox *combo;
g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
priv = gimp_size_entry_get_instance_private (gse);
combo = GIMP_UNIT_COMBO_BOX (priv->unit_combo);
g_object_set_data (G_OBJECT (gse), "gimp-pixel-digits",
GINT_TO_POINTER (digits));
gimp_size_entry_update_unit (gse, gimp_unit_combo_box_get_active (combo));
}
/**
* gimp_size_entry_grab_focus:
* @gse: The sizeentry you want to grab the keyboard focus.
*
* This function is rather ugly and just a workaround for the fact that
* it's impossible to implement gtk_widget_grab_focus() for a #GtkGrid (is this actually true after the Table->Grid conversion?).
**/
void
gimp_size_entry_grab_focus (GimpSizeEntry *gse)
{
GimpSizeEntryPrivate *priv;
GimpSizeEntryField *gsef;
g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
priv = gimp_size_entry_get_instance_private (gse);
gsef = priv->fields->data;
if (gsef)
gtk_widget_grab_focus (priv->show_refval ?
gsef->refval_spinbutton : gsef->value_spinbutton);
}
/**
* gimp_size_entry_set_activates_default:
* @gse: A #GimpSizeEntry
* @setting: %TRUE to activate window's default widget on Enter keypress
*
* Iterates over all entries in the #GimpSizeEntry and calls
* gtk_entry_set_activates_default() on them.
*
* Since: 2.4
**/
void
gimp_size_entry_set_activates_default (GimpSizeEntry *gse,
gboolean setting)
{
GimpSizeEntryPrivate *priv;
GSList *list;
g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
priv = gimp_size_entry_get_instance_private (gse);
for (list = priv->fields; list; list = g_slist_next (list))
{
GimpSizeEntryField *gsef = list->data;
if (gsef->value_spinbutton)
gtk_entry_set_activates_default (GTK_ENTRY (gsef->value_spinbutton),
setting);
if (gsef->refval_spinbutton)
gtk_entry_set_activates_default (GTK_ENTRY (gsef->refval_spinbutton),
setting);
}
}
/**
* gimp_size_entry_get_help_widget:
* @gse: a #GimpSizeEntry
* @field: the index of the widget you want to get a pointer to
*
* You shouldn't fiddle with the internals of a #GimpSizeEntry but
* if you want to set tooltips using gimp_help_set_help_data() you
* can use this function to get a pointer to the spinbuttons.
*
* Returns: (transfer none): a #GtkWidget pointer that you can attach a tooltip to.
**/
GtkWidget *
gimp_size_entry_get_help_widget (GimpSizeEntry *gse,
gint field)
{
GimpSizeEntryPrivate *priv;
GimpSizeEntryField *gsef;
g_return_val_if_fail (GIMP_IS_SIZE_ENTRY (gse), NULL);
priv = gimp_size_entry_get_instance_private (gse);
g_return_val_if_fail ((field >= 0) && (field < priv->number_of_fields), NULL);
gsef = g_slist_nth_data (priv->fields, field);
if (!gsef)
return NULL;
return (gsef->refval_spinbutton ?
gsef->refval_spinbutton : gsef->value_spinbutton);
}