ui-utils.c   [plain text]


/*
 * "$Id: ui-utils.c,v 1.1.1.2 2004/12/22 23:49:37 jlovell Exp $"
 *
 *   Main window code for Print plug-in for the GIMP.
 *
 *   Copyright 1997-2000 Michael Sweet (mike@easysw.com),
 *	Robert Krawitz (rlk@alum.mit.edu), Steve Miller (smiller@rni.net)
 *      and Michael Natterer (mitch@gimp.org)
 *
 *   This program is free software; you can redistribute it and/or modify it
 *   under the terms of the GNU General Public License as published by the Free
 *   Software Foundation; either version 2 of the License, or (at your option)
 *   any later version.
 *
 *   This program is distributed in the hope that it will be useful, but
 *   WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 *   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 *   for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <gimp-print/gimp-print-intl-internal.h>
#include <gimp-print-ui2/gimp-print-ui.h>
#include "gimp-print-ui-internal.h"

#include <stdio.h>
#include <string.h>

static void default_errfunc(void *data, const char *buffer, size_t bytes);
static gchar *image_filename;
static stp_outfunc_t the_errfunc = default_errfunc;
static void *the_errdata = NULL;
static get_thumbnail_func_t thumbnail_func;
static void *thumbnail_private_data;

/*****************************************************************\
*                                                                 *
*             The following from libgimp/gimpdialog.c             *
*                                                                 *
\*****************************************************************/

typedef void (*StpuiBasicCallback) (GObject *object,
				    gpointer user_data);

/*  local callbacks of dialog_new ()  */
static gint
dialog_delete_callback (GtkWidget *widget,
			GdkEvent  *event,
			gpointer   data)
{
  StpuiBasicCallback cancel_callback;
  GtkWidget     *cancel_widget;

  cancel_callback =
    (StpuiBasicCallback) g_object_get_data (G_OBJECT (widget),
					    "dialog_cancel_callback");
  cancel_widget =
    (GtkWidget*) g_object_get_data (G_OBJECT (widget),
				    "dialog_cancel_widget");

  /*  the cancel callback has to destroy the dialog  */
  if (cancel_callback)
    (* cancel_callback) (G_OBJECT(cancel_widget), data);

  return TRUE;
}

/**
 * dialog_create_action_areav:
 * @dialog: The #GtkDialog you want to create the action_area for.
 * @args: A @va_list as obtained with va_start() describing the action_area
 *        buttons.
 *
 */
static void
dialog_create_action_areav (GtkDialog *dialog,
			    va_list    args)
{
  GtkWidget *hbbox = NULL;
  GtkWidget *button;

  /*  action area variables  */
  const gchar    *label;
  GtkSignalFunc   callback;
  gpointer        data;
  GObject        *slot_object;
  GtkWidget     **widget_ptr;
  gboolean        default_action;
  gboolean        connect_delete;

  gboolean delete_connected = FALSE;

  g_return_if_fail (dialog != NULL);
  g_return_if_fail (GTK_IS_DIALOG (dialog));

  /*  prepare the action_area  */
  label = va_arg (args, const gchar *);

  if (label)
    {
      gtk_container_set_border_width (GTK_CONTAINER (dialog->action_area), 2);
      gtk_box_set_homogeneous (GTK_BOX (dialog->action_area), FALSE);

      hbbox = gtk_hbutton_box_new ();
      gtk_button_box_set_spacing (GTK_BUTTON_BOX (hbbox), 4);
      gtk_box_pack_end (GTK_BOX (dialog->action_area), hbbox, FALSE, FALSE, 0);
      gtk_widget_show (hbbox);
    }

  /*  the action_area buttons  */
  while (label)
    {
      callback       = va_arg (args, GtkSignalFunc);
      data           = va_arg (args, gpointer);
      slot_object    = va_arg (args, GObject *);
      widget_ptr     = va_arg (args, GtkWidget **);
      default_action = va_arg (args, gboolean);
      connect_delete = va_arg (args, gboolean);

      button = gtk_button_new_with_label (label);
      GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
      gtk_box_pack_start (GTK_BOX (hbbox), button, FALSE, FALSE, 0);

      if (slot_object == (GObject *) 1)
	slot_object = G_OBJECT (dialog);

      if (data == NULL)
	data = dialog;

      if (callback)
	{
	  if (slot_object)
	    g_signal_connect_object (G_OBJECT (button), "clicked",
				     G_CALLBACK (callback),
				     slot_object, G_CONNECT_SWAPPED);
	  else
	    g_signal_connect (G_OBJECT (button), "clicked",
			      G_CALLBACK (callback),
			      data);
	}

      if (widget_ptr)
	*widget_ptr = button;

      if (connect_delete && callback && !delete_connected)
	{
	  gtk_object_set_data (G_OBJECT (dialog),
			       "dialog_cancel_callback",
			       (gpointer) callback);
	  gtk_object_set_data (G_OBJECT (dialog),
			       "dialog_cancel_widget",
			       slot_object ? slot_object : G_OBJECT (button));

	  /*  catch the WM delete event  */
	  g_signal_connect (G_OBJECT (dialog), "delete_event",
			    G_CALLBACK (dialog_delete_callback),
			    data);

	  delete_connected = TRUE;
	}

      if (default_action)
	gtk_widget_grab_default (button);
      gtk_widget_show (button);

      label = va_arg (args, gchar *);
    }
}

/**
 * dialog_new:
 * @title: The dialog's title which will be set with gtk_window_set_title().
 * @position: The dialog's initial position which will be set with
 *            gtk_window_set_position().
 * @resizable: Is the dialog resizable?, ...
 * @allow_grow: ... it't @allow_grow flag and ...
 * @auto_shrink: ... it's @auto_shrink flag which will all be set with
 *               gtk_window_set_policy().
 * @...: A #NULL terminated @va_list destribing the action_area buttons.
 *
 * This function simply packs the action_area arguments passed in "..."
 * into a @va_list variable and passes everything to dialog_newv().
 *
 * For a description of the format of the @va_list describing the
 * action_area buttons see dialog_create_action_areav().
 *
 * Returns: A #GtkDialog.
 *
 */
GtkWidget *
stpui_dialog_new (const gchar       *title,
		  GtkWindowPosition  position,
		  gboolean           resizable,

		  /* specify action area buttons as va_list:
		   *  const gchar    *label,
		   *  GtkSignalFunc   callback,
		   *  gpointer        data,
		   *  GObject      *slot_object,
		   *  GtkWidget     **widget_ptr,
		   *  gboolean        default_action,
		   *  gboolean        connect_delete,
		   */

		  ...)
{
  GtkWidget *dialog;
  va_list    args;

  va_start (args, resizable);

  g_return_val_if_fail (title != NULL, NULL);

  dialog = gtk_dialog_new ();
  gtk_window_set_title (GTK_WINDOW (dialog), title);
  gtk_window_set_position (GTK_WINDOW (dialog), position);
  gtk_window_set_resizable (GTK_WINDOW (dialog), resizable);

  /*  prepare the action_area  */
  dialog_create_action_areav (GTK_DIALOG (dialog), args);

  va_end (args);

  return dialog;
}

/*****************************************************************\
*                                                                 *
*             The following from libgimp/gimpwidgets.c            *
*                                                                 *
\*****************************************************************/

/**
 * option_menu_new:
 * @menu_only: #TRUE if the function should return a #GtkMenu only.
 * @...:       A #NULL terminated @va_list describing the menu items.
 *
 * Returns: A #GtkOptionMenu or a #GtkMenu (depending on @menu_only).
 **/
GtkWidget *
stpui_option_menu_new(gboolean            menu_only,

		      /* specify menu items as va_list:
		       *  const gchar    *label,
		       *  GtkSignalFunc   callback,
		       *  gpointer        data,
		       *  gpointer        user_data,
		       *  GtkWidget     **widget_ptr,
		       *  gboolean        active
		       */

		       ...)
{
  GtkWidget *menu;
  GtkWidget *menuitem;

  /*  menu item variables  */
  const gchar    *label;
  GtkSignalFunc   callback;
  gpointer        data;
  gpointer        user_data;
  GtkWidget     **widget_ptr;
  gboolean        active;

  va_list args;
  gint    i;
  gint    initial_index;

  menu = gtk_menu_new ();

  /*  create the menu items  */
  initial_index = 0;

  va_start (args, menu_only);
  label = va_arg (args, const gchar *);

  for (i = 0; label; i++)
    {
      callback   = va_arg (args, GtkSignalFunc);
      data       = va_arg (args, gpointer);
      user_data  = va_arg (args, gpointer);
      widget_ptr = va_arg (args, GtkWidget **);
      active     = va_arg (args, gboolean);

      if (strcmp (label, "---"))
	{
	  menuitem = gtk_menu_item_new_with_label (label);

	  g_signal_connect (G_OBJECT (menuitem), "activate",
			    callback,
			    data);

	  if (user_data)
	    gtk_object_set_user_data (G_OBJECT (menuitem), user_data);
	}
      else
	{
	  menuitem = gtk_menu_item_new ();

	  gtk_widget_set_sensitive (menuitem, FALSE);
	}

      gtk_menu_append (GTK_MENU (menu), menuitem);

      if (widget_ptr)
	*widget_ptr = menuitem;

      gtk_widget_show (menuitem);

      /*  remember the initial menu item  */
      if (active)
	initial_index = i;

      label = va_arg (args, const gchar *);
    }
  va_end (args);

  if (! menu_only)
    {
      GtkWidget *optionmenu;

      optionmenu = gtk_option_menu_new ();
      gtk_option_menu_set_menu (GTK_OPTION_MENU (optionmenu), menu);

      /*  select the initial menu item  */
      gtk_option_menu_set_history (GTK_OPTION_MENU (optionmenu), initial_index);

      return optionmenu;
    }

  return menu;
}

/*****************************************************************\
*                                                                 *
*             The following from libgimp/gimpwidgets.c            *
*                                                                 *
\*****************************************************************/

/**
 * spin_button_new:
 * @adjustment:     Returns the spinbutton's #GtkAdjustment.
 * @value:          The initial value of the spinbutton.
 * @lower:          The lower boundary.
 * @upper:          The uppper boundary.
 * @step_increment: The spinbutton's step increment.
 * @page_increment: The spinbutton's page increment (mouse button 2).
 * @page_size:      The spinbutton's 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() and does some more initialisation stuff like
 * setting a standard minimun horizontal size.
 *
 * Returns: A #GtkSpinbutton and it's #GtkAdjustment.
 **/
static GtkWidget *
spin_button_new (GtkObject **adjustment,  /* return value */
		      gfloat      value,
		      gfloat      lower,
		      gfloat      upper,
		      gfloat      step_increment,
		      gfloat      page_increment,
		      gfloat      page_size,
		      gfloat      climb_rate,
		      guint       digits)
{
  GtkWidget *spinbutton;

  *adjustment = gtk_adjustment_new (value, lower, upper,
				    step_increment, page_increment, page_size);

  spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (*adjustment),
				    climb_rate, digits);
  gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
  gtk_widget_set_usize (spinbutton, 75, -1);

  return spinbutton;
}

static void
scale_entry_unconstrained_adjustment_callback (GtkAdjustment *adjustment,
					       GtkAdjustment *other_adj)
{
  g_signal_handlers_block_matched (G_OBJECT (other_adj),
				   G_SIGNAL_MATCH_DATA,
				   0,
				   0,
				   NULL,
				   NULL,
				   adjustment);

  gtk_adjustment_set_value (other_adj, adjustment->value);

  g_signal_handlers_unblock_matched (G_OBJECT (other_adj),
				     G_SIGNAL_MATCH_DATA,
				     0,
				     0,
				     NULL,
				     NULL,
				     adjustment);
}

/**
 * scale_entry_new:
 * @table:               The #GtkTable the widgets will be attached to.
 * @column:              The column to start with.
 * @row:                 The row to attach the widgets.
 * @text:                The text for the #GtkLabel which will appear
 *                       left of the #GtkHScale.
 * @scale_usize:         The minimum horizontal size of the #GtkHScale.
 * @spinbutton_usize:    The minimum horizontal size of the #GtkSpinButton.
 * @value:               The initial value.
 * @lower:               The lower boundary.
 * @upper:               The upper boundary.
 * @step_increment:      The step increment.
 * @page_increment:      The page increment.
 * @digits:              The number of decimal digits.
 * @constrain:           #TRUE if the range of possible values of the
 *                       #GtkSpinButton should be the same as of the #GtkHScale.
 * @unconstrained_lower: The spinbutton's lower boundary
 *                       if @constrain == #FALSE.
 * @unconstrained_upper: The spinbutton's upper boundary
 *                       if @constrain == #FALSE.
 * @tooltip:             A tooltip message for the scale and the spinbutton.
 *
 * This function creates a #GtkLabel, a #GtkHScale and a #GtkSpinButton and
 * attaches them to a 3-column #GtkTable.
 *
 * Returns: The #GtkSpinButton's #GtkAdjustment.
 **/
GtkObject *
stpui_scale_entry_new(GtkTable    *table,
		      gint         column,
		      gint         row,
		      const gchar *text,
		      gint         scale_usize,
		      gint         spinbutton_usize,
		      gfloat       value,
		      gfloat       lower,
		      gfloat       upper,
		      gfloat       step_increment,
		      gfloat       page_increment,
		      guint        digits,
		      gboolean     constrain,
		      gfloat       unconstrained_lower,
		      gfloat       unconstrained_upper,
		      const gchar *tooltip)
{
  GtkWidget *label;
  GtkWidget *scale;
  GtkWidget *spinbutton;
  GtkObject *adjustment;
  GtkObject *return_adj;

  label = gtk_label_new (text);
  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
  gtk_table_attach (GTK_TABLE (table), label,
                    column + 1, column + 2, row, row + 1,
                    GTK_FILL, GTK_FILL, 0, 0);
  gtk_widget_show (label);

  if (! constrain &&
      unconstrained_lower <= lower &&
      unconstrained_upper >= upper)
    {
      GtkObject *constrained_adj;

      constrained_adj = gtk_adjustment_new (value, lower, upper,
					    step_increment, page_increment,
					    0.0);

      spinbutton = spin_button_new (&adjustment, value,
				    unconstrained_lower,
				    unconstrained_upper,
				    step_increment, page_increment, 0.0,
				    1.0, digits);

      g_signal_connect
	(G_OBJECT (constrained_adj), "value_changed",
	 G_CALLBACK (scale_entry_unconstrained_adjustment_callback),
	 adjustment);

      g_signal_connect
	(G_OBJECT (adjustment), "value_changed",
	 G_CALLBACK (scale_entry_unconstrained_adjustment_callback),
	 constrained_adj);

      return_adj = adjustment;

      adjustment = constrained_adj;
    }
  else
    {
      spinbutton = spin_button_new (&adjustment, value, lower, upper,
				    step_increment, page_increment, 0.0,
				    1.0, digits);

      return_adj = adjustment;
    }

  if (spinbutton_usize > 0)
    gtk_widget_set_usize (spinbutton, spinbutton_usize, -1);

  scale = gtk_hscale_new (GTK_ADJUSTMENT (adjustment));
  if (scale_usize > 0)
    gtk_widget_set_usize (scale, scale_usize, -1);
  gtk_scale_set_digits (GTK_SCALE (scale), digits);
  gtk_scale_set_draw_value (GTK_SCALE (scale), FALSE);
  gtk_table_attach (GTK_TABLE (table), scale,
		    column + 2, column + 3, row, row + 1,
		    GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
  gtk_widget_show (scale);

  gtk_table_attach (GTK_TABLE (table), spinbutton,
		    column + 3, column + 4, row, row + 1,
		    GTK_SHRINK, GTK_SHRINK, 0, 0);
  gtk_widget_show (spinbutton);

  if (tooltip)
    {
      stpui_set_help_data (scale, tooltip);
      stpui_set_help_data (spinbutton, tooltip);
    }

  gtk_object_set_data (G_OBJECT (return_adj), "label", label);
  gtk_object_set_data (G_OBJECT (return_adj), "scale", scale);
  gtk_object_set_data (G_OBJECT (return_adj), "spinbutton", spinbutton);

  return return_adj;
}

/**
 * table_attach_aligned:
 * @table:      The #GtkTable the widgets will be attached to.
 * @column:     The column to start with.
 * @row:        The row to attach the eidgets.
 * @label_text: The text for the #GtkLabel which will be attached left of the
 *              widget.
 * @xalign:     The horizontal alignment of the #GtkLabel.
 * @yalign:     The vertival alignment of the #GtkLabel.
 * @widget:     The #GtkWidget to attach right of the label.
 * @colspan:    The number of columns the widget will use.
 * @left_align: #TRUE if the widget should be left-aligned.
 *
 * Note that the @label_text can be #NULL and that the widget will be attached
 * starting at (@column + 1) in this case, too.
 **/
void
stpui_table_attach_aligned (GtkTable    *table,
			    gint         column,
			    gint         row,
			    const gchar *label_text,
			    gfloat       xalign,
			    gfloat       yalign,
			    GtkWidget   *widget,
			    gint         colspan,
			    gboolean     left_align)
{
  if (label_text)
    {
      GtkWidget *label;

      label = gtk_label_new (label_text);
      gtk_misc_set_alignment (GTK_MISC (label), xalign, yalign);
      gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
      gtk_table_attach (table, label, column, column + 1, row, row + 1,
			GTK_FILL, GTK_FILL, 0, 0);
      gtk_widget_show (label);
    }

  gtk_widget_show (widget);
  if (left_align)
    {
      GtkWidget *alignment;

      alignment = gtk_alignment_new (0.0, 0.5, 0.0, 0.0);
      gtk_container_add (GTK_CONTAINER (alignment), widget);

      widget = alignment;
    }

  gtk_table_attach (table, widget, column + 1, column + 1 + colspan,
		    row, row + 1,
		    GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);

  gtk_widget_show (widget);
}

/*****************************************************************\
*                                                                 *
*        The following borrowed from libgimp/gimphelpui.c         *
*                                                                 *
\*****************************************************************/

static GtkTooltips * tool_tips  = NULL;

/**
 * stpui_help_init:
 *
 * This function initializes GIMP's help system.
 *
 * Currently it only creates a #GtkTooltips object with gtk_tooltips_new()
 * which will be used by help_stpui_set_help_data().
 **/
void
stpui_help_init (void)
{
  tool_tips = gtk_tooltips_new ();
}

/**
 * stpui_help_free:
 *
 * This function frees the memory used by the #GtkTooltips created by
 * stpui_help_init().
 **/
void
stpui_help_free (void)
{
  gtk_object_destroy (GTK_OBJECT (tool_tips));
  gtk_object_unref   (GTK_OBJECT (tool_tips));
}

/**
 * help_enable_tooltips:
 *
 * This function calls gtk_tooltips_enable().
 **/
void
stpui_enable_help (void)
{
  gtk_tooltips_enable (tool_tips);
}

/**
 * help_disable_tooltips:
 *
 * This function calls gtk_tooltips_disable().
 **/

void
stpui_disable_help (void)
{
  gtk_tooltips_disable (tool_tips);
}

void
stpui_set_help_data (GtkWidget *widget, const gchar *tooltip)
{
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_WIDGET (widget));

  if (tooltip)
    {
      gtk_tooltips_set_tip (tool_tips, widget, tooltip, NULL);
    }
}

/*****************************************************************\
*                                                                 *
*                     The below is new code                       *
*                                                                 *
\*****************************************************************/


void
stpui_set_image_filename(const char *name)
{
  if (name && name == image_filename)
    return;
  if (image_filename)
    g_free(image_filename);
  if (name)
    image_filename = g_strdup(name);
  else
    image_filename = g_strdup("");
}

const char *
stpui_get_image_filename(void)
{
  stpui_set_image_filename(image_filename);
  return(image_filename);
}

static void
default_errfunc(void *data, const char *buffer, size_t bytes)
{
  fwrite(buffer, 1, bytes, data ? data : stderr);
}

void
stpui_set_errfunc(stp_outfunc_t wfunc)
{
  the_errfunc = wfunc;
}

stp_outfunc_t
stpui_get_errfunc(void)
{
  return the_errfunc;
}

void
stpui_set_errdata(void *errdata)
{
  the_errdata = errdata;
}

void *
stpui_get_errdata(void)
{
  return the_errdata;
}

void
stpui_set_thumbnail_func(get_thumbnail_func_t func)
{
  thumbnail_func = func;
}

get_thumbnail_func_t
stpui_get_thumbnail_func(void)
{
  return thumbnail_func;
}

void
stpui_set_thumbnail_data(void *data)
{
  thumbnail_private_data = data;
}

void *
stpui_get_thumbnail_data(void)
{
  return thumbnail_private_data;
}

GtkWidget *
stpui_create_entry(GtkWidget *table, int hpos, int vpos, const char *text,
		   const char *help, GtkSignalFunc callback)
{
  GtkWidget *entry = gtk_entry_new();
  gtk_widget_set_usize(entry, 60, 0);
  stpui_table_attach_aligned(GTK_TABLE(table), hpos, vpos, text,
			     0.0, 0.5, entry, 1, TRUE);
  stpui_set_help_data(entry, help);
  g_signal_connect(G_OBJECT(entry), "activate",
		   G_CALLBACK(callback), NULL);
  return entry;
}

GSList *
stpui_create_radio_button(radio_group_t *radio, GSList *group,
			  GtkWidget *table, int hpos, int vpos,
			  GtkSignalFunc callback)
{
  radio->button = gtk_radio_button_new_with_label(group, _(radio->name));
  group = gtk_radio_button_group(GTK_RADIO_BUTTON(radio->button));
  stpui_table_attach_aligned(GTK_TABLE(table), hpos, vpos, NULL, 0.5, 0.5,
			     radio->button, 1, FALSE);
  stpui_set_help_data(radio->button, _(radio->help));
  g_signal_connect(G_OBJECT(radio->button), "toggled",
		   G_CALLBACK(callback), (gpointer) radio->value);
  return group;
}

void
stpui_set_adjustment_tooltip (GtkObject *adj, const gchar *tip)
{
  stpui_set_help_data (GTK_WIDGET (SCALE_ENTRY_SCALE (adj)), tip);
  stpui_set_help_data (GTK_WIDGET (SCALE_ENTRY_SPINBUTTON (adj)), tip);
}

static GtkWidget *
table_label(GtkTable *table, gint column, gint row)
{
  GList *children = table->children;;
  while (children)
    {
      GtkTableChild *child = (GtkTableChild *)children->data;
      if (child->left_attach == column + 1 && child->top_attach == row)
	return child->widget;
      children = children->next;
    }
  return NULL;
}

void
stpui_create_new_combo(option_t *option, GtkWidget *table,
		       int hpos, int vpos, gboolean is_optional)
{
  GtkWidget *event_box = gtk_event_box_new();
  GtkWidget *combo = gtk_combo_new();

  option->checkbox = gtk_check_button_new();
  gtk_table_attach(GTK_TABLE(table), option->checkbox,
		   hpos, hpos + 1, vpos, vpos + 1,
		   GTK_EXPAND|GTK_FILL, GTK_FILL, 0, 0);

  option->info.list.combo = combo;
  gtk_container_add(GTK_CONTAINER(event_box), combo);
  gtk_widget_show(combo);
  gtk_widget_show(event_box);
  stpui_set_help_data(event_box, _(option->fast_desc->help));
  stpui_table_attach_aligned
    (GTK_TABLE(table), hpos + 1, vpos, _(option->fast_desc->text),
     0.0, 0.5, event_box, 2, TRUE);
  option->info.list.label = table_label(GTK_TABLE(table), hpos, vpos);
}

const char *
stpui_combo_get_name(GtkWidget   *combo,
		     const stp_string_list_t *options)
{
  if (options)
    {
      gint   i;
      gint num_options = stp_string_list_count(options);
      gchar *text = (gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(combo)->entry)));

      if (text == NULL)
	return (NULL);

      if (num_options == 0)
	return ((const char *)text);

      for (i = 0; i < num_options; i ++)
	if (strcmp(stp_string_list_param(options, i)->text, text) == 0)
	  return (stp_string_list_param(options, i)->name);
    }
  return (NULL);
}

void
stpui_create_scale_entry(option_t    *opt,
			 GtkTable    *table,
			 gint         column,
			 gint         row,
			 const gchar *text,
			 gint         scale_usize,
			 gint         spinbutton_usize,
			 gfloat       value,
			 gfloat       lower,
			 gfloat       upper,
			 gfloat       step_increment,
			 gfloat       page_increment,
			 guint        digits,
			 gboolean     constrain,
			 gfloat       unconstrained_lower,
			 gfloat       unconstrained_upper,
			 const gchar *tooltip,
			 gboolean     is_optional)
{
  opt->checkbox = gtk_check_button_new();
  gtk_table_attach(GTK_TABLE(table), opt->checkbox,
		   column, column + 1, row, row + 1,
		   GTK_EXPAND|GTK_FILL, GTK_FILL, 0, 0);
  opt->info.flt.adjustment =
    stpui_scale_entry_new(table, column, row, text, scale_usize,
			  spinbutton_usize, value, lower, upper,
			  step_increment, page_increment, digits, constrain,
			  unconstrained_lower, unconstrained_upper, tooltip);
}