summaryrefslogtreecommitdiffstats
path: root/libgimpwidgets/gimpwidgets.c
diff options
context:
space:
mode:
Diffstat (limited to 'libgimpwidgets/gimpwidgets.c')
-rw-r--r--libgimpwidgets/gimpwidgets.c997
1 files changed, 997 insertions, 0 deletions
diff --git a/libgimpwidgets/gimpwidgets.c b/libgimpwidgets/gimpwidgets.c
new file mode 100644
index 0000000..2958de9
--- /dev/null
+++ b/libgimpwidgets/gimpwidgets.c
@@ -0,0 +1,997 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpwidgets.c
+ * Copyright (C) 2000 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 <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpmath/gimpmath.h"
+#include "libgimpbase/gimpbase.h"
+
+#include "gimpwidgets.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/* hack: declare prototype here instead of #undef GIMP_DISABLE_DEPRECATED */
+void gimp_toggle_button_sensitive_update (GtkToggleButton *toggle_button);
+
+
+/**
+ * SECTION: gimpwidgets
+ * @title: GimpWidgets
+ * @short_description: A collection of convenient widget constructors,
+ * standard callbacks and helper functions.
+ *
+ * A collection of convenient widget constructors, standard callbacks
+ * and helper functions.
+ **/
+
+
+/**
+ * gimp_radio_group_new:
+ * @in_frame: %TRUE if you want a #GtkFrame around the radio button group.
+ * @frame_title: The title of the Frame or %NULL if you don't want a title.
+ * @...: A %NULL-terminated @va_list describing the radio buttons.
+ *
+ * Convenience function to create a group of radio buttons embedded into
+ * a #GtkFrame or #GtkVBox.
+ *
+ * Returns: A #GtkFrame or #GtkVBox (depending on @in_frame).
+ **/
+GtkWidget *
+gimp_radio_group_new (gboolean in_frame,
+ const gchar *frame_title,
+
+ /* specify radio buttons as va_list:
+ * const gchar *label,
+ * GCallback callback,
+ * gpointer callback_data,
+ * gpointer item_data,
+ * GtkWidget **widget_ptr,
+ * gboolean active,
+ */
+
+ ...)
+{
+ GtkWidget *vbox;
+ GtkWidget *button;
+ GSList *group;
+
+ /* radio button variables */
+ const gchar *label;
+ GCallback callback;
+ gpointer callback_data;
+ gpointer item_data;
+ GtkWidget **widget_ptr;
+ gboolean active;
+
+ va_list args;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+
+ group = NULL;
+
+ /* create the radio buttons */
+ va_start (args, frame_title);
+ label = va_arg (args, const gchar *);
+ while (label)
+ {
+ callback = va_arg (args, GCallback);
+ callback_data = va_arg (args, gpointer);
+ item_data = va_arg (args, gpointer);
+ widget_ptr = va_arg (args, GtkWidget **);
+ active = va_arg (args, gboolean);
+
+ if (label != (gpointer) 1)
+ button = gtk_radio_button_new_with_mnemonic (group, label);
+ else
+ button = gtk_radio_button_new (group);
+
+ group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+
+ if (item_data)
+ {
+ g_object_set_data (G_OBJECT (button), "gimp-item-data", item_data);
+
+ /* backward compatibility */
+ g_object_set_data (G_OBJECT (button), "user_data", item_data);
+ }
+
+ if (widget_ptr)
+ *widget_ptr = button;
+
+ if (active)
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
+
+ g_signal_connect (button, "toggled",
+ callback,
+ callback_data);
+
+ gtk_widget_show (button);
+
+ label = va_arg (args, const gchar *);
+ }
+ va_end (args);
+
+ if (in_frame)
+ {
+ GtkWidget *frame;
+
+ frame = gimp_frame_new (frame_title);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ return frame;
+ }
+
+ return vbox;
+}
+
+/**
+ * gimp_radio_group_new2:
+ * @in_frame: %TRUE if you want a #GtkFrame around the
+ * radio button group.
+ * @frame_title: The title of the Frame or %NULL if you don't want
+ * a title.
+ * @radio_button_callback: The callback each button's "toggled" signal will
+ * be connected with.
+ * @radio_button_callback_data:
+ * The data which will be passed to g_signal_connect().
+ * @initial: The @item_data of the initially pressed radio button.
+ * @...: A %NULL-terminated @va_list describing
+ * the radio buttons.
+ *
+ * Convenience function to create a group of radio buttons embedded into
+ * a #GtkFrame or #GtkVBox.
+ *
+ * Returns: A #GtkFrame or #GtkVBox (depending on @in_frame).
+ **/
+GtkWidget *
+gimp_radio_group_new2 (gboolean in_frame,
+ const gchar *frame_title,
+ GCallback radio_button_callback,
+ gpointer callback_data,
+ gpointer initial, /* item_data */
+
+ /* specify radio buttons as va_list:
+ * const gchar *label,
+ * gpointer item_data,
+ * GtkWidget **widget_ptr,
+ */
+
+ ...)
+{
+ GtkWidget *vbox;
+ GtkWidget *button;
+ GSList *group;
+
+ /* radio button variables */
+ const gchar *label;
+ gpointer item_data;
+ GtkWidget **widget_ptr;
+
+ va_list args;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+
+ group = NULL;
+
+ /* create the radio buttons */
+ va_start (args, initial);
+ label = va_arg (args, const gchar *);
+
+ while (label)
+ {
+ item_data = va_arg (args, gpointer);
+ widget_ptr = va_arg (args, GtkWidget **);
+
+ if (label != (gpointer) 1)
+ button = gtk_radio_button_new_with_mnemonic (group, label);
+ else
+ button = gtk_radio_button_new (group);
+
+ group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+
+ if (item_data)
+ {
+ g_object_set_data (G_OBJECT (button), "gimp-item-data", item_data);
+
+ /* backward compatibility */
+ g_object_set_data (G_OBJECT (button), "user_data", item_data);
+ }
+
+ if (widget_ptr)
+ *widget_ptr = button;
+
+ if (initial == item_data)
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
+
+ g_signal_connect (button, "toggled",
+ radio_button_callback,
+ callback_data);
+
+ gtk_widget_show (button);
+
+ label = va_arg (args, const gchar *);
+ }
+ va_end (args);
+
+ if (in_frame)
+ {
+ GtkWidget *frame;
+
+ frame = gimp_frame_new (frame_title);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ return frame;
+ }
+
+ return vbox;
+}
+
+/**
+ * gimp_int_radio_group_new:
+ * @in_frame: %TRUE if you want a #GtkFrame around the
+ * radio button group.
+ * @frame_title: The title of the Frame or %NULL if you don't want
+ * a title.
+ * @radio_button_callback: The callback each button's "toggled" signal will
+ * be connected with.
+ * @radio_button_callback_data:
+ * The data which will be passed to g_signal_connect().
+ * @initial: The @item_data of the initially pressed radio button.
+ * @...: A %NULL-terminated @va_list describing
+ * the radio buttons.
+ *
+ * Convenience function to create a group of radio buttons embedded into
+ * a #GtkFrame or #GtkVBox. This function does the same thing as
+ * gimp_radio_group_new2(), but it takes integers as @item_data instead of
+ * pointers, since that is a very common case (mapping an enum to a radio
+ * group).
+ *
+ * Returns: A #GtkFrame or #GtkVBox (depending on @in_frame).
+ **/
+GtkWidget *
+gimp_int_radio_group_new (gboolean in_frame,
+ const gchar *frame_title,
+ GCallback radio_button_callback,
+ gpointer callback_data,
+ gint initial, /* item_data */
+
+ /* specify radio buttons as va_list:
+ * const gchar *label,
+ * gint item_data,
+ * GtkWidget **widget_ptr,
+ */
+
+ ...)
+{
+ GtkWidget *vbox;
+ GtkWidget *button;
+ GSList *group;
+
+ /* radio button variables */
+ const gchar *label;
+ gint item_data;
+ gpointer item_ptr;
+ GtkWidget **widget_ptr;
+
+ va_list args;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+
+ group = NULL;
+
+ /* create the radio buttons */
+ va_start (args, initial);
+ label = va_arg (args, const gchar *);
+
+ while (label)
+ {
+ item_data = va_arg (args, gint);
+ widget_ptr = va_arg (args, GtkWidget **);
+
+ item_ptr = GINT_TO_POINTER (item_data);
+
+ if (label != GINT_TO_POINTER (1))
+ button = gtk_radio_button_new_with_mnemonic (group, label);
+ else
+ button = gtk_radio_button_new (group);
+
+ group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+
+ if (item_data)
+ {
+ g_object_set_data (G_OBJECT (button), "gimp-item-data", item_ptr);
+
+ /* backward compatibility */
+ g_object_set_data (G_OBJECT (button), "user_data", item_ptr);
+ }
+
+ if (widget_ptr)
+ *widget_ptr = button;
+
+ if (initial == item_data)
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
+
+ g_signal_connect (button, "toggled",
+ radio_button_callback,
+ callback_data);
+
+ gtk_widget_show (button);
+
+ label = va_arg (args, const gchar *);
+ }
+ va_end (args);
+
+ if (in_frame)
+ {
+ GtkWidget *frame;
+
+ frame = gimp_frame_new (frame_title);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ return frame;
+ }
+
+ return vbox;
+}
+
+/**
+ * gimp_radio_group_set_active:
+ * @radio_button: Pointer to a #GtkRadioButton.
+ * @item_data: The @item_data of the radio button you want to select.
+ *
+ * Calls gtk_toggle_button_set_active() with the radio button that was
+ * created with a matching @item_data.
+ **/
+void
+gimp_radio_group_set_active (GtkRadioButton *radio_button,
+ gpointer item_data)
+{
+ GtkWidget *button;
+ GSList *group;
+
+ g_return_if_fail (GTK_IS_RADIO_BUTTON (radio_button));
+
+ for (group = gtk_radio_button_get_group (radio_button);
+ group;
+ group = g_slist_next (group))
+ {
+ button = GTK_WIDGET (group->data);
+
+ if (g_object_get_data (G_OBJECT (button), "gimp-item-data") == item_data)
+ {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
+ return;
+ }
+ }
+}
+
+/**
+ * gimp_int_radio_group_set_active:
+ * @radio_button: Pointer to a #GtkRadioButton.
+ * @item_data: The @item_data of the radio button you want to select.
+ *
+ * Calls gtk_toggle_button_set_active() with the radio button that was created
+ * with a matching @item_data. This function does the same thing as
+ * gimp_radio_group_set_active(), but takes integers as @item_data instead
+ * of pointers.
+ **/
+void
+gimp_int_radio_group_set_active (GtkRadioButton *radio_button,
+ gint item_data)
+{
+ g_return_if_fail (GTK_IS_RADIO_BUTTON (radio_button));
+
+ gimp_radio_group_set_active (radio_button, GINT_TO_POINTER (item_data));
+}
+
+/**
+ * gimp_spin_button_new:
+ * @adjustment: Returns the spinbutton's #GtkAdjustment.
+ * @value: The initial value of the spinbutton.
+ * @lower: The lower boundary.
+ * @upper: The upper boundary.
+ * @step_increment: The spinbutton's step increment.
+ * @page_increment: The spinbutton's page increment (mouse button 2).
+ * @page_size: Ignored, spin buttons must always have a zero page size.
+ * @climb_rate: The spinbutton's climb rate.
+ * @digits: The spinbutton's number of decimal digits.
+ *
+ * This function is a shortcut for gtk_adjustment_new() and a
+ * subsequent gtk_spin_button_new(). It also calls
+ * gtk_spin_button_set_numeric() so that non-numeric text cannot be
+ * entered.
+ *
+ * Deprecated: 2.10: Use gtk_spin_button_new() instead.
+ *
+ * Returns: A #GtkSpinButton and its #GtkAdjustment.
+ **/
+GtkWidget *
+gimp_spin_button_new (GtkObject **adjustment, /* return value */
+ gdouble value,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ gdouble page_size,
+ gdouble climb_rate,
+ guint digits)
+{
+ GtkWidget *spinbutton;
+
+ *adjustment = gtk_adjustment_new (value, lower, upper,
+ step_increment, page_increment, 0);
+
+ spinbutton = gimp_spin_button_new (GTK_ADJUSTMENT (*adjustment),
+ climb_rate, digits);
+
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+
+ return spinbutton;
+}
+
+static void
+gimp_random_seed_update (GtkWidget *widget,
+ gpointer data)
+{
+ GtkWidget *spinbutton = data;
+
+ /* Generate a new seed if the "New Seed" button was clicked or
+ * of the "Randomize" toggle is activated
+ */
+ if (! GTK_IS_TOGGLE_BUTTON (widget) ||
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ {
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinbutton),
+ (guint) g_random_int ());
+ }
+}
+
+/**
+ * gimp_random_seed_new:
+ * @seed: A pointer to the variable which stores the random seed.
+ * @random_seed: A pointer to a boolean indicating whether seed should be
+ * initialised randomly or not.
+ *
+ * Creates a widget that allows the user to control how the random number
+ * generator is initialized.
+ *
+ * Returns: A #GtkHBox containing a #GtkSpinButton for the seed and
+ * a #GtkButton for setting a random seed.
+ **/
+GtkWidget *
+gimp_random_seed_new (guint *seed,
+ gboolean *random_seed)
+{
+ GtkWidget *hbox;
+ GtkWidget *toggle;
+ GtkWidget *spinbutton;
+ GtkAdjustment *adj;
+ GtkWidget *button;
+
+ g_return_val_if_fail (seed != NULL, NULL);
+ g_return_val_if_fail (random_seed != NULL, NULL);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+
+ /* If we're being asked to generate a random seed, generate one. */
+ if (*random_seed)
+ *seed = g_random_int ();
+
+ adj = (GtkAdjustment *)
+ gtk_adjustment_new (*seed, 0, (guint32) -1, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adj, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_uint_adjustment_update),
+ seed);
+
+ gimp_help_set_help_data (spinbutton,
+ _("Use this value for random number generator "
+ "seed - this allows you to repeat a "
+ "given \"random\" operation"), NULL);
+
+ button = gtk_button_new_with_mnemonic (_("_New Seed"));
+ gtk_misc_set_padding (GTK_MISC (gtk_bin_get_child (GTK_BIN (button))), 2, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ /* Send spinbutton as data so that we can change the value in
+ * gimp_random_seed_update()
+ */
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (gimp_random_seed_update),
+ spinbutton);
+
+ gimp_help_set_help_data (button,
+ _("Seed random number generator with a generated "
+ "random number"),
+ NULL);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("_Randomize"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), *random_seed);
+ gtk_box_pack_start (GTK_BOX (hbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ random_seed);
+
+ /* Need to create a new seed when the "Randomize" toggle is activated */
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_random_seed_update),
+ spinbutton);
+
+ g_object_set_data (G_OBJECT (hbox), "spinbutton", spinbutton);
+ g_object_set_data (G_OBJECT (hbox), "button", button);
+ g_object_set_data (G_OBJECT (hbox), "toggle", toggle);
+
+ g_object_bind_property (toggle, "active",
+ spinbutton, "sensitive",
+ G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
+ g_object_bind_property (toggle, "active",
+ button, "sensitive",
+ G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
+
+ return hbox;
+}
+
+typedef struct
+{
+ GimpChainButton *chainbutton;
+ gboolean chain_constrains_ratio;
+ gdouble orig_x;
+ gdouble orig_y;
+ gdouble last_x;
+ gdouble last_y;
+} GimpCoordinatesData;
+
+static void
+gimp_coordinates_callback (GtkWidget *widget,
+ GimpCoordinatesData *data)
+{
+ gdouble new_x;
+ gdouble new_y;
+
+ new_x = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 0);
+ new_y = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 1);
+
+ if (gimp_chain_button_get_active (data->chainbutton))
+ {
+ if (data->chain_constrains_ratio)
+ {
+ if ((data->orig_x != 0) && (data->orig_y != 0))
+ {
+ g_signal_handlers_block_by_func (widget,
+ gimp_coordinates_callback,
+ data);
+
+ if (ROUND (new_x) != ROUND (data->last_x))
+ {
+ data->last_x = new_x;
+ new_y = (new_x * data->orig_y) / data->orig_x;
+
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (widget), 1,
+ new_y);
+ data->last_y
+ = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 1);
+ }
+ else if (ROUND (new_y) != ROUND (data->last_y))
+ {
+ data->last_y = new_y;
+ new_x = (new_y * data->orig_x) / data->orig_y;
+
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (widget), 0,
+ new_x);
+ data->last_x
+ = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 0);
+ }
+
+ g_signal_handlers_unblock_by_func (widget,
+ gimp_coordinates_callback,
+ data);
+ }
+ }
+ else
+ {
+ if (new_x != data->last_x)
+ {
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (widget), 1, new_x);
+ data->last_y = data->last_x
+ = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 1);
+ }
+ else if (new_y != data->last_y)
+ {
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (widget), 0, new_y);
+ data->last_x = data->last_y
+ = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 0);
+ }
+ }
+ }
+ else
+ {
+ if (new_x != data->last_x)
+ data->last_x = new_x;
+ if (new_y != data->last_y)
+ data->last_y = new_y;
+ }
+}
+
+static void
+gimp_coordinates_data_free (GimpCoordinatesData *data)
+{
+ g_slice_free (GimpCoordinatesData, data);
+}
+
+static void
+gimp_coordinates_chainbutton_toggled (GimpChainButton *button,
+ GimpSizeEntry *entry)
+{
+ if (gimp_chain_button_get_active (button))
+ {
+ GimpCoordinatesData *data;
+
+ data = g_object_get_data (G_OBJECT (entry), "coordinates-data");
+
+ data->orig_x = gimp_size_entry_get_refval (entry, 0);
+ data->orig_y = gimp_size_entry_get_refval (entry, 1);
+ }
+}
+
+/**
+ * gimp_coordinates_new:
+ * @unit: The initial unit of the #GimpUnitMenu.
+ * @unit_format: A printf-like unit-format string as is used with
+ * gimp_unit_menu_new().
+ * @menu_show_pixels: %TRUE if the #GimpUnitMenu should contain an item
+ * for GIMP_UNIT_PIXEL.
+ * @menu_show_percent: %TRUE if the #GimpUnitMenu should contain an item
+ * for GIMP_UNIT_PERCENT.
+ * @spinbutton_width: The horizontal size of the #GimpSizeEntry's
+ * #GtkSpinButton's.
+ * @update_policy: The update policy for the #GimpSizeEntry.
+ * @chainbutton_active: %TRUE if the attached #GimpChainButton should be
+ * active.
+ * @chain_constrains_ratio: %TRUE if the chainbutton should constrain the
+ * fields' aspect ratio. If %FALSE, the values will
+ * be constrained.
+ * @xlabel: The label for the X coordinate.
+ * @x: The initial value of the X coordinate.
+ * @xres: The horizontal resolution in DPI.
+ * @lower_boundary_x: The lower boundary of the X coordinate.
+ * @upper_boundary_x: The upper boundary of the X coordinate.
+ * @xsize_0: The X value which will be treated as 0%.
+ * @xsize_100: The X value which will be treated as 100%.
+ * @ylabel: The label for the Y coordinate.
+ * @y: The initial value of the Y coordinate.
+ * @yres: The vertical resolution in DPI.
+ * @lower_boundary_y: The lower boundary of the Y coordinate.
+ * @upper_boundary_y: The upper boundary of the Y coordinate.
+ * @ysize_0: The Y value which will be treated as 0%.
+ * @ysize_100: The Y value which will be treated as 100%.
+ *
+ * Convenience function that creates a #GimpSizeEntry with two fields for x/y
+ * coordinates/sizes with a #GimpChainButton attached to constrain either the
+ * two fields' values or the ratio between them.
+ *
+ * Returns: The new #GimpSizeEntry.
+ **/
+GtkWidget *
+gimp_coordinates_new (GimpUnit unit,
+ const gchar *unit_format,
+ gboolean menu_show_pixels,
+ gboolean menu_show_percent,
+ gint spinbutton_width,
+ GimpSizeEntryUpdatePolicy update_policy,
+
+ gboolean chainbutton_active,
+ gboolean chain_constrains_ratio,
+
+ const gchar *xlabel,
+ gdouble x,
+ gdouble xres,
+ gdouble lower_boundary_x,
+ gdouble upper_boundary_x,
+ gdouble xsize_0, /* % */
+ gdouble xsize_100, /* % */
+
+ const gchar *ylabel,
+ gdouble y,
+ gdouble yres,
+ gdouble lower_boundary_y,
+ gdouble upper_boundary_y,
+ gdouble ysize_0, /* % */
+ gdouble ysize_100 /* % */)
+{
+ GimpCoordinatesData *data;
+ GtkAdjustment *adjustment;
+ GtkWidget *spinbutton;
+ GtkWidget *sizeentry;
+ GtkWidget *chainbutton;
+
+ adjustment = (GtkAdjustment *) gtk_adjustment_new (1, 0, 1, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adjustment, 1.0, 2);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+
+ if (spinbutton_width > 0)
+ {
+ if (spinbutton_width < 17)
+ gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), spinbutton_width);
+ else
+ gtk_widget_set_size_request (spinbutton, spinbutton_width, -1);
+ }
+
+ sizeentry = gimp_size_entry_new (1, unit, unit_format,
+ menu_show_pixels,
+ menu_show_percent,
+ FALSE,
+ spinbutton_width,
+ update_policy);
+ gtk_table_set_col_spacing (GTK_TABLE (sizeentry), 0, 4);
+ gtk_table_set_col_spacing (GTK_TABLE (sizeentry), 2, 4);
+ gimp_size_entry_add_field (GIMP_SIZE_ENTRY (sizeentry),
+ GTK_SPIN_BUTTON (spinbutton), NULL);
+ gtk_table_attach_defaults (GTK_TABLE (sizeentry), spinbutton, 1, 2, 0, 1);
+ gtk_widget_show (spinbutton);
+
+ gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (sizeentry),
+ (update_policy == GIMP_SIZE_ENTRY_UPDATE_RESOLUTION) ||
+ (menu_show_pixels == FALSE) ?
+ GIMP_UNIT_INCH : GIMP_UNIT_PIXEL);
+
+ gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (sizeentry), 0, xres, TRUE);
+ gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (sizeentry), 1, yres, TRUE);
+ gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (sizeentry), 0,
+ lower_boundary_x, upper_boundary_x);
+ gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (sizeentry), 1,
+ lower_boundary_y, upper_boundary_y);
+
+ if (menu_show_percent)
+ {
+ gimp_size_entry_set_size (GIMP_SIZE_ENTRY (sizeentry), 0,
+ xsize_0, xsize_100);
+ gimp_size_entry_set_size (GIMP_SIZE_ENTRY (sizeentry), 1,
+ ysize_0, ysize_100);
+ }
+
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (sizeentry), 0, x);
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (sizeentry), 1, y);
+
+ gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (sizeentry),
+ xlabel, 0, 0, 0.0);
+ gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (sizeentry),
+ ylabel, 1, 0, 0.0);
+
+ chainbutton = gimp_chain_button_new (GIMP_CHAIN_RIGHT);
+
+ if (chainbutton_active)
+ gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (chainbutton), TRUE);
+
+ gtk_table_attach (GTK_TABLE (sizeentry), chainbutton, 2, 3, 0, 2,
+ GTK_SHRINK | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ gtk_widget_show (chainbutton);
+
+ data = g_slice_new (GimpCoordinatesData);
+
+ data->chainbutton = GIMP_CHAIN_BUTTON (chainbutton);
+ data->chain_constrains_ratio = chain_constrains_ratio;
+ data->orig_x = x;
+ data->orig_y = y;
+ data->last_x = x;
+ data->last_y = y;
+
+ g_object_set_data_full (G_OBJECT (sizeentry), "coordinates-data",
+ data,
+ (GDestroyNotify) gimp_coordinates_data_free);
+
+ g_signal_connect (sizeentry, "value-changed",
+ G_CALLBACK (gimp_coordinates_callback),
+ data);
+
+ g_object_set_data (G_OBJECT (sizeentry), "chainbutton", chainbutton);
+
+ g_signal_connect (chainbutton, "toggled",
+ G_CALLBACK (gimp_coordinates_chainbutton_toggled),
+ sizeentry);
+
+ return sizeentry;
+}
+
+
+/*
+ * Standard Callbacks
+ */
+
+/**
+ * gimp_toggle_button_sensitive_update:
+ * @toggle_button: The #GtkToggleButton the "set_sensitive" and
+ * "inverse_sensitive" lists are attached to.
+ *
+ * If you attached a pointer to a #GtkWidget with g_object_set_data() and
+ * the "set_sensitive" key to the #GtkToggleButton, the sensitive state of
+ * the attached widget will be set according to the toggle button's
+ * "active" state.
+ *
+ * You can attach an arbitrary list of widgets by attaching another
+ * "set_sensitive" data pointer to the first widget (and so on...).
+ *
+ * This function can also set the sensitive state according to the toggle
+ * button's inverse "active" state by attaching widgets with the
+ * "inverse_sensitive" key.
+ *
+ * Deprecated: use g_object_bind_property() instead of using the
+ * "set_sensitive" and "inverse_sensitive" data pointers.
+ **/
+void
+gimp_toggle_button_sensitive_update (GtkToggleButton *toggle_button)
+{
+ GtkWidget *set_sensitive;
+ gboolean active;
+
+ active = gtk_toggle_button_get_active (toggle_button);
+
+ set_sensitive =
+ g_object_get_data (G_OBJECT (toggle_button), "set_sensitive");
+ while (set_sensitive)
+ {
+ gtk_widget_set_sensitive (set_sensitive, active);
+ set_sensitive =
+ g_object_get_data (G_OBJECT (set_sensitive), "set_sensitive");
+ }
+
+ set_sensitive =
+ g_object_get_data (G_OBJECT (toggle_button), "inverse_sensitive");
+ while (set_sensitive)
+ {
+ gtk_widget_set_sensitive (set_sensitive, ! active);
+ set_sensitive =
+ g_object_get_data (G_OBJECT (set_sensitive), "inverse_sensitive");
+ }
+}
+
+/**
+ * gimp_toggle_button_update:
+ * @widget: A #GtkToggleButton.
+ * @data: A pointer to a #gint variable which will store the value of
+ * gtk_toggle_button_get_active().
+ *
+ * Note that this function calls gimp_toggle_button_sensitive_update()
+ * which is a deprecated hack you shouldn't use. See that function's
+ * documentation for a proper replacement of its functionality.
+ **/
+void
+gimp_toggle_button_update (GtkWidget *widget,
+ gpointer data)
+{
+ gint *toggle_val = (gint *) data;
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ *toggle_val = TRUE;
+ else
+ *toggle_val = FALSE;
+
+ gimp_toggle_button_sensitive_update (GTK_TOGGLE_BUTTON (widget));
+}
+
+/**
+ * gimp_radio_button_update:
+ * @widget: A #GtkRadioButton.
+ * @data: A pointer to a #gint variable which will store the value of
+ * GPOINTER_TO_INT (g_object_get_data (@widget, "gimp-item-data")).
+ *
+ * Note that this function calls gimp_toggle_button_sensitive_update()
+ * which is a deprecated hack you shouldn't use. See that function's
+ * documentation for a proper replacement of its functionality.
+ **/
+void
+gimp_radio_button_update (GtkWidget *widget,
+ gpointer data)
+{
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ {
+ gint *toggle_val = (gint *) data;
+
+ *toggle_val = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
+ "gimp-item-data"));
+ }
+
+ gimp_toggle_button_sensitive_update (GTK_TOGGLE_BUTTON (widget));
+}
+
+/**
+ * gimp_int_adjustment_update:
+ * @adjustment: A #GtkAdjustment.
+ * @data: A pointer to a #gint variable which will store the
+ * @adjustment's value.
+ *
+ * Note that the #GtkAdjustment's value (which is a #gdouble) will be
+ * rounded with RINT().
+ **/
+void
+gimp_int_adjustment_update (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ gint *val = (gint *) data;
+
+ *val = RINT (gtk_adjustment_get_value (adjustment));
+}
+
+/**
+ * gimp_uint_adjustment_update:
+ * @adjustment: A #GtkAdjustment.
+ * @data: A pointer to a #guint variable which will store the
+ * @adjustment's value.
+ *
+ * Note that the #GtkAdjustment's value (which is a #gdouble) will be rounded
+ * with (#guint) (value + 0.5).
+ **/
+void
+gimp_uint_adjustment_update (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ guint *val = (guint *) data;
+
+ *val = (guint) (gtk_adjustment_get_value (adjustment) + 0.5);
+}
+
+/**
+ * gimp_float_adjustment_update:
+ * @adjustment: A #GtkAdjustment.
+ * @data: A pointer to a #gfloat variable which will store the
+ * @adjustment's value.
+ **/
+void
+gimp_float_adjustment_update (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ gfloat *val = (gfloat *) data;
+
+ *val = gtk_adjustment_get_value (adjustment);
+
+}
+
+/**
+ * gimp_double_adjustment_update:
+ * @adjustment: A #GtkAdjustment.
+ * @data: A pointer to a #gdouble variable which will store the
+ * @adjustment's value.
+ **/
+void
+gimp_double_adjustment_update (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ gdouble *val = (gdouble *) data;
+
+ *val = gtk_adjustment_get_value (adjustment);
+}