1/*
2 * "$Id: ui-utils.c,v 1.3 2006/05/28 16:59:04 rlk Exp $"
3 *
4 *   Main window code for Print plug-in for the GIMP.
5 *
6 *   Copyright 1997-2000 Michael Sweet (mike@easysw.com),
7 *	Robert Krawitz (rlk@alum.mit.edu), Steve Miller (smiller@rni.net)
8 *      and Michael Natterer (mitch@gimp.org)
9 *
10 *   This program is free software; you can redistribute it and/or modify it
11 *   under the terms of the GNU General Public License as published by the Free
12 *   Software Foundation; either version 2 of the License, or (at your option)
13 *   any later version.
14 *
15 *   This program is distributed in the hope that it will be useful, but
16 *   WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17 *   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
18 *   for more details.
19 *
20 *   You should have received a copy of the GNU General Public License
21 *   along with this program; if not, write to the Free Software
22 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 */
24
25#ifdef HAVE_CONFIG_H
26#include <config.h>
27#endif
28
29#include <gutenprint/gutenprint-intl-internal.h>
30#include <gutenprintui2/gutenprintui.h>
31#include "gutenprintui-internal.h"
32
33#include <stdio.h>
34#include <string.h>
35
36static void default_errfunc(void *data, const char *buffer, size_t bytes);
37static gchar *image_filename;
38static stp_outfunc_t the_errfunc = default_errfunc;
39static void *the_errdata = NULL;
40static get_thumbnail_func_t thumbnail_func;
41static void *thumbnail_private_data;
42
43/*****************************************************************\
44*                                                                 *
45*             The following from libgimp/gimpdialog.c             *
46*                                                                 *
47\*****************************************************************/
48
49typedef void (*StpuiBasicCallback) (GObject *object,
50				    gpointer user_data);
51
52/*  local callbacks of dialog_new ()  */
53static gint
54dialog_delete_callback (GtkWidget *widget,
55			GdkEvent  *event,
56			gpointer   data)
57{
58  StpuiBasicCallback cancel_callback;
59  GtkWidget     *cancel_widget;
60
61  cancel_callback =
62    (StpuiBasicCallback) g_object_get_data (G_OBJECT (widget),
63					    "dialog_cancel_callback");
64  cancel_widget =
65    (GtkWidget*) g_object_get_data (G_OBJECT (widget),
66				    "dialog_cancel_widget");
67
68  /*  the cancel callback has to destroy the dialog  */
69  if (cancel_callback)
70    (* cancel_callback) (G_OBJECT(cancel_widget), data);
71
72  return TRUE;
73}
74
75/**
76 * dialog_create_action_areav:
77 * @dialog: The #GtkDialog you want to create the action_area for.
78 * @args: A @va_list as obtained with va_start() describing the action_area
79 *        buttons.
80 *
81 */
82static void
83dialog_create_action_areav (GtkDialog *dialog,
84			    va_list    args)
85{
86  GtkWidget *hbbox = NULL;
87  GtkWidget *button;
88
89  /*  action area variables  */
90  const gchar    *label;
91  GtkSignalFunc   callback;
92  gpointer        data;
93  GObject        *slot_object;
94  GtkWidget     **widget_ptr;
95  gboolean        default_action;
96  gboolean        connect_delete;
97
98  gboolean delete_connected = FALSE;
99
100  g_return_if_fail (dialog != NULL);
101  g_return_if_fail (GTK_IS_DIALOG (dialog));
102
103  /*  prepare the action_area  */
104  label = va_arg (args, const gchar *);
105
106  if (label)
107    {
108      gtk_container_set_border_width (GTK_CONTAINER (dialog->action_area), 2);
109      gtk_box_set_homogeneous (GTK_BOX (dialog->action_area), FALSE);
110
111      hbbox = gtk_hbutton_box_new ();
112      gtk_button_box_set_spacing (GTK_BUTTON_BOX (hbbox), 4);
113      gtk_box_pack_end (GTK_BOX (dialog->action_area), hbbox, FALSE, FALSE, 0);
114      gtk_widget_show (hbbox);
115    }
116
117  /*  the action_area buttons  */
118  while (label)
119    {
120      callback       = va_arg (args, GtkSignalFunc);
121      data           = va_arg (args, gpointer);
122      slot_object    = va_arg (args, GObject *);
123      widget_ptr     = va_arg (args, GtkWidget **);
124      default_action = va_arg (args, gboolean);
125      connect_delete = va_arg (args, gboolean);
126
127      button = gtk_button_new_with_label (label);
128      GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
129      gtk_box_pack_start (GTK_BOX (hbbox), button, FALSE, FALSE, 0);
130
131      if (slot_object == (GObject *) 1)
132	slot_object = G_OBJECT (dialog);
133
134      if (data == NULL)
135	data = dialog;
136
137      if (callback)
138	{
139	  if (slot_object)
140	    g_signal_connect_object (G_OBJECT (button), "clicked",
141				     G_CALLBACK (callback),
142				     slot_object, G_CONNECT_SWAPPED);
143	  else
144	    g_signal_connect (G_OBJECT (button), "clicked",
145			      G_CALLBACK (callback),
146			      data);
147	}
148
149      if (widget_ptr)
150	*widget_ptr = button;
151
152      if (connect_delete && callback && !delete_connected)
153	{
154	  gtk_object_set_data (GTK_OBJECT (dialog),
155			       "dialog_cancel_callback",
156			       (gpointer) callback);
157	  gtk_object_set_data (GTK_OBJECT (dialog),
158			       "dialog_cancel_widget",
159			       slot_object ? slot_object : G_OBJECT (button));
160
161	  /*  catch the WM delete event  */
162	  g_signal_connect (G_OBJECT (dialog), "delete_event",
163			    G_CALLBACK (dialog_delete_callback),
164			    data);
165
166	  delete_connected = TRUE;
167	}
168
169      if (default_action)
170	gtk_widget_grab_default (button);
171      gtk_widget_show (button);
172
173      label = va_arg (args, gchar *);
174    }
175}
176
177/**
178 * dialog_new:
179 * @title: The dialog's title which will be set with gtk_window_set_title().
180 * @position: The dialog's initial position which will be set with
181 *            gtk_window_set_position().
182 * @resizable: Is the dialog resizable?, ...
183 * @allow_grow: ... it't @allow_grow flag and ...
184 * @auto_shrink: ... it's @auto_shrink flag which will all be set with
185 *               gtk_window_set_policy().
186 * @...: A #NULL terminated @va_list destribing the action_area buttons.
187 *
188 * This function simply packs the action_area arguments passed in "..."
189 * into a @va_list variable and passes everything to dialog_newv().
190 *
191 * For a description of the format of the @va_list describing the
192 * action_area buttons see dialog_create_action_areav().
193 *
194 * Returns: A #GtkDialog.
195 *
196 */
197GtkWidget *
198stpui_dialog_new (const gchar       *title,
199		  GtkWindowPosition  position,
200		  gboolean           resizable,
201
202		  /* specify action area buttons as va_list:
203		   *  const gchar    *label,
204		   *  GtkSignalFunc   callback,
205		   *  gpointer        data,
206		   *  GObject      *slot_object,
207		   *  GtkWidget     **widget_ptr,
208		   *  gboolean        default_action,
209		   *  gboolean        connect_delete,
210		   */
211
212		  ...)
213{
214  GtkWidget *dialog;
215  va_list    args;
216
217  va_start (args, resizable);
218
219  g_return_val_if_fail (title != NULL, NULL);
220
221  dialog = gtk_dialog_new ();
222  gtk_window_set_title (GTK_WINDOW (dialog), title);
223  gtk_window_set_position (GTK_WINDOW (dialog), position);
224  gtk_window_set_resizable (GTK_WINDOW (dialog), resizable);
225
226  /*  prepare the action_area  */
227  dialog_create_action_areav (GTK_DIALOG (dialog), args);
228
229  va_end (args);
230
231  return dialog;
232}
233
234/*****************************************************************\
235*                                                                 *
236*             The following from libgimp/gimpwidgets.c            *
237*                                                                 *
238\*****************************************************************/
239
240/**
241 * option_menu_new:
242 * @menu_only: #TRUE if the function should return a #GtkMenu only.
243 * @...:       A #NULL terminated @va_list describing the menu items.
244 *
245 * Returns: A #GtkOptionMenu or a #GtkMenu (depending on @menu_only).
246 **/
247GtkWidget *
248stpui_option_menu_new(gboolean            menu_only,
249
250		      /* specify menu items as va_list:
251		       *  const gchar    *label,
252		       *  GtkSignalFunc   callback,
253		       *  gpointer        data,
254		       *  gpointer        user_data,
255		       *  GtkWidget     **widget_ptr,
256		       *  gboolean        active
257		       */
258
259		       ...)
260{
261  GtkWidget *menu;
262  GtkWidget *menuitem;
263
264  /*  menu item variables  */
265  const gchar    *label;
266  GtkSignalFunc   callback;
267  gpointer        data;
268  gpointer        user_data;
269  GtkWidget     **widget_ptr;
270  gboolean        active;
271
272  va_list args;
273  gint    i;
274  gint    initial_index;
275
276  menu = gtk_menu_new ();
277
278  /*  create the menu items  */
279  initial_index = 0;
280
281  va_start (args, menu_only);
282  label = va_arg (args, const gchar *);
283
284  for (i = 0; label; i++)
285    {
286      callback   = va_arg (args, GtkSignalFunc);
287      data       = va_arg (args, gpointer);
288      user_data  = va_arg (args, gpointer);
289      widget_ptr = va_arg (args, GtkWidget **);
290      active     = va_arg (args, gboolean);
291
292      if (strcmp (label, "---"))
293	{
294	  menuitem = gtk_menu_item_new_with_label (label);
295
296	  g_signal_connect (G_OBJECT (menuitem), "activate",
297			    callback,
298			    data);
299
300	  if (user_data)
301	    gtk_object_set_user_data (GTK_OBJECT (menuitem), user_data);
302	}
303      else
304	{
305	  menuitem = gtk_menu_item_new ();
306
307	  gtk_widget_set_sensitive (menuitem, FALSE);
308	}
309
310      gtk_menu_append (GTK_MENU (menu), menuitem);
311
312      if (widget_ptr)
313	*widget_ptr = menuitem;
314
315      gtk_widget_show (menuitem);
316
317      /*  remember the initial menu item  */
318      if (active)
319	initial_index = i;
320
321      label = va_arg (args, const gchar *);
322    }
323  va_end (args);
324
325  if (! menu_only)
326    {
327      GtkWidget *optionmenu;
328
329      optionmenu = gtk_option_menu_new ();
330      gtk_option_menu_set_menu (GTK_OPTION_MENU (optionmenu), menu);
331
332      /*  select the initial menu item  */
333      gtk_option_menu_set_history (GTK_OPTION_MENU (optionmenu), initial_index);
334
335      return optionmenu;
336    }
337
338  return menu;
339}
340
341/*****************************************************************\
342*                                                                 *
343*             The following from libgimp/gimpwidgets.c            *
344*                                                                 *
345\*****************************************************************/
346
347/**
348 * spin_button_new:
349 * @adjustment:     Returns the spinbutton's #GtkAdjustment.
350 * @value:          The initial value of the spinbutton.
351 * @lower:          The lower boundary.
352 * @upper:          The uppper boundary.
353 * @step_increment: The spinbutton's step increment.
354 * @page_increment: The spinbutton's page increment (mouse button 2).
355 * @page_size:      The spinbutton's page size.
356 * @climb_rate:     The spinbutton's climb rate.
357 * @digits:         The spinbutton's number of decimal digits.
358 *
359 * This function is a shortcut for gtk_adjustment_new() and a subsequent
360 * gtk_spin_button_new() and does some more initialisation stuff like
361 * setting a standard minimun horizontal size.
362 *
363 * Returns: A #GtkSpinbutton and it's #GtkAdjustment.
364 **/
365static GtkWidget *
366spin_button_new (GtkObject **adjustment,  /* return value */
367		      gfloat      value,
368		      gfloat      lower,
369		      gfloat      upper,
370		      gfloat      step_increment,
371		      gfloat      page_increment,
372		      gfloat      page_size,
373		      gfloat      climb_rate,
374		      guint       digits)
375{
376  GtkWidget *spinbutton;
377
378  *adjustment = gtk_adjustment_new (value, lower, upper,
379				    step_increment, page_increment, page_size);
380
381  spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (*adjustment),
382				    climb_rate, digits);
383  gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
384  gtk_widget_set_usize (spinbutton, 75, -1);
385
386  return spinbutton;
387}
388
389static void
390scale_entry_unconstrained_adjustment_callback (GtkAdjustment *adjustment,
391					       GtkAdjustment *other_adj)
392{
393  g_signal_handlers_block_matched (G_OBJECT (other_adj),
394				   G_SIGNAL_MATCH_DATA,
395				   0,
396				   0,
397				   NULL,
398				   NULL,
399				   adjustment);
400
401  gtk_adjustment_set_value (other_adj, adjustment->value);
402
403  g_signal_handlers_unblock_matched (G_OBJECT (other_adj),
404				     G_SIGNAL_MATCH_DATA,
405				     0,
406				     0,
407				     NULL,
408				     NULL,
409				     adjustment);
410}
411
412/**
413 * scale_entry_new:
414 * @table:               The #GtkTable the widgets will be attached to.
415 * @column:              The column to start with.
416 * @row:                 The row to attach the widgets.
417 * @text:                The text for the #GtkLabel which will appear
418 *                       left of the #GtkHScale.
419 * @scale_usize:         The minimum horizontal size of the #GtkHScale.
420 * @spinbutton_usize:    The minimum horizontal size of the #GtkSpinButton.
421 * @value:               The initial value.
422 * @lower:               The lower boundary.
423 * @upper:               The upper boundary.
424 * @step_increment:      The step increment.
425 * @page_increment:      The page increment.
426 * @digits:              The number of decimal digits.
427 * @constrain:           #TRUE if the range of possible values of the
428 *                       #GtkSpinButton should be the same as of the #GtkHScale.
429 * @unconstrained_lower: The spinbutton's lower boundary
430 *                       if @constrain == #FALSE.
431 * @unconstrained_upper: The spinbutton's upper boundary
432 *                       if @constrain == #FALSE.
433 * @tooltip:             A tooltip message for the scale and the spinbutton.
434 *
435 * This function creates a #GtkLabel, a #GtkHScale and a #GtkSpinButton and
436 * attaches them to a 3-column #GtkTable.
437 *
438 * Returns: The #GtkSpinButton's #GtkAdjustment.
439 **/
440GtkObject *
441stpui_scale_entry_new(GtkTable    *table,
442		      gint         column,
443		      gint         row,
444		      const gchar *text,
445		      gint         scale_usize,
446		      gint         spinbutton_usize,
447		      gfloat       value,
448		      gfloat       lower,
449		      gfloat       upper,
450		      gfloat       step_increment,
451		      gfloat       page_increment,
452		      guint        digits,
453		      gboolean     constrain,
454		      gfloat       unconstrained_lower,
455		      gfloat       unconstrained_upper,
456		      const gchar *tooltip)
457{
458  GtkWidget *label;
459  GtkWidget *scale;
460  GtkWidget *spinbutton;
461  GtkObject *adjustment;
462  GtkObject *return_adj;
463
464  label = gtk_label_new (text);
465  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
466  gtk_table_attach (GTK_TABLE (table), label,
467                    column + 1, column + 2, row, row + 1,
468                    GTK_FILL, GTK_FILL, 0, 0);
469  gtk_widget_show (label);
470
471  if (! constrain &&
472      unconstrained_lower <= lower &&
473      unconstrained_upper >= upper)
474    {
475      GtkObject *constrained_adj;
476
477      constrained_adj = gtk_adjustment_new (value, lower, upper,
478					    step_increment, page_increment,
479					    0.0);
480
481      spinbutton = spin_button_new (&adjustment, value,
482				    unconstrained_lower,
483				    unconstrained_upper,
484				    step_increment, page_increment, 0.0,
485				    1.0, digits);
486
487      g_signal_connect
488	(G_OBJECT (constrained_adj), "value_changed",
489	 G_CALLBACK (scale_entry_unconstrained_adjustment_callback),
490	 adjustment);
491
492      g_signal_connect
493	(G_OBJECT (adjustment), "value_changed",
494	 G_CALLBACK (scale_entry_unconstrained_adjustment_callback),
495	 constrained_adj);
496
497      return_adj = adjustment;
498
499      adjustment = constrained_adj;
500    }
501  else
502    {
503      spinbutton = spin_button_new (&adjustment, value, lower, upper,
504				    step_increment, page_increment, 0.0,
505				    1.0, digits);
506
507      return_adj = adjustment;
508    }
509
510  if (spinbutton_usize > 0)
511    gtk_widget_set_usize (spinbutton, spinbutton_usize, -1);
512
513  scale = gtk_hscale_new (GTK_ADJUSTMENT (adjustment));
514  if (scale_usize > 0)
515    gtk_widget_set_usize (scale, scale_usize, -1);
516  gtk_scale_set_digits (GTK_SCALE (scale), digits);
517  gtk_scale_set_draw_value (GTK_SCALE (scale), FALSE);
518  gtk_table_attach (GTK_TABLE (table), scale,
519		    column + 2, column + 3, row, row + 1,
520		    GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
521  gtk_widget_show (scale);
522
523  gtk_table_attach (GTK_TABLE (table), spinbutton,
524		    column + 3, column + 4, row, row + 1,
525		    GTK_SHRINK, GTK_SHRINK, 0, 0);
526  gtk_widget_show (spinbutton);
527
528  if (tooltip)
529    {
530      stpui_set_help_data (scale, tooltip);
531      stpui_set_help_data (spinbutton, tooltip);
532    }
533
534  gtk_object_set_data (GTK_OBJECT (return_adj), "label", label);
535  gtk_object_set_data (GTK_OBJECT (return_adj), "scale", scale);
536  gtk_object_set_data (GTK_OBJECT (return_adj), "spinbutton", spinbutton);
537
538  return return_adj;
539}
540
541/**
542 * table_attach_aligned:
543 * @table:      The #GtkTable the widgets will be attached to.
544 * @column:     The column to start with.
545 * @row:        The row to attach the eidgets.
546 * @label_text: The text for the #GtkLabel which will be attached left of the
547 *              widget.
548 * @xalign:     The horizontal alignment of the #GtkLabel.
549 * @yalign:     The vertival alignment of the #GtkLabel.
550 * @widget:     The #GtkWidget to attach right of the label.
551 * @colspan:    The number of columns the widget will use.
552 * @left_align: #TRUE if the widget should be left-aligned.
553 *
554 * Note that the @label_text can be #NULL and that the widget will be attached
555 * starting at (@column + 1) in this case, too.
556 **/
557void
558stpui_table_attach_aligned (GtkTable    *table,
559			    gint         column,
560			    gint         row,
561			    const gchar *label_text,
562			    gfloat       xalign,
563			    gfloat       yalign,
564			    GtkWidget   *widget,
565			    gint         colspan,
566			    gboolean     left_align)
567{
568  if (label_text)
569    {
570      GtkWidget *label;
571
572      label = gtk_label_new (label_text);
573      gtk_misc_set_alignment (GTK_MISC (label), xalign, yalign);
574      gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
575      gtk_table_attach (table, label, column, column + 1, row, row + 1,
576			GTK_FILL, GTK_FILL, 0, 0);
577      gtk_widget_show (label);
578    }
579
580  gtk_widget_show (widget);
581  if (left_align)
582    {
583      GtkWidget *alignment;
584
585      alignment = gtk_alignment_new (0.0, 0.5, 0.0, 0.0);
586      gtk_container_add (GTK_CONTAINER (alignment), widget);
587
588      widget = alignment;
589    }
590
591  gtk_table_attach (table, widget, column + 1, column + 1 + colspan,
592		    row, row + 1,
593		    GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
594
595  gtk_widget_show (widget);
596}
597
598/*****************************************************************\
599*                                                                 *
600*        The following borrowed from libgimp/gimphelpui.c         *
601*                                                                 *
602\*****************************************************************/
603
604static GtkTooltips * tool_tips  = NULL;
605
606/**
607 * stpui_help_init:
608 *
609 * This function initializes GIMP's help system.
610 *
611 * Currently it only creates a #GtkTooltips object with gtk_tooltips_new()
612 * which will be used by help_stpui_set_help_data().
613 **/
614void
615stpui_help_init (void)
616{
617  tool_tips = gtk_tooltips_new ();
618}
619
620/**
621 * stpui_help_free:
622 *
623 * This function frees the memory used by the #GtkTooltips created by
624 * stpui_help_init().
625 **/
626void
627stpui_help_free (void)
628{
629  gtk_object_destroy (GTK_OBJECT (tool_tips));
630  gtk_object_unref   (GTK_OBJECT (tool_tips));
631}
632
633/**
634 * help_enable_tooltips:
635 *
636 * This function calls gtk_tooltips_enable().
637 **/
638void
639stpui_enable_help (void)
640{
641  gtk_tooltips_enable (tool_tips);
642}
643
644/**
645 * help_disable_tooltips:
646 *
647 * This function calls gtk_tooltips_disable().
648 **/
649
650void
651stpui_disable_help (void)
652{
653  gtk_tooltips_disable (tool_tips);
654}
655
656void
657stpui_set_help_data (GtkWidget *widget, const gchar *tooltip)
658{
659  g_return_if_fail (widget != NULL);
660  g_return_if_fail (GTK_IS_WIDGET (widget));
661
662  if (tooltip)
663    {
664      gtk_tooltips_set_tip (tool_tips, widget, tooltip, NULL);
665    }
666}
667
668/*****************************************************************\
669*                                                                 *
670*                     The below is new code                       *
671*                                                                 *
672\*****************************************************************/
673
674
675void
676stpui_set_image_filename(const char *name)
677{
678  if (name && name == image_filename)
679    return;
680  if (image_filename)
681    g_free(image_filename);
682  if (name)
683    image_filename = g_strdup(name);
684  else
685    image_filename = g_strdup("");
686}
687
688const char *
689stpui_get_image_filename(void)
690{
691  stpui_set_image_filename(image_filename);
692  return(image_filename);
693}
694
695static void
696default_errfunc(void *data, const char *buffer, size_t bytes)
697{
698  fwrite(buffer, 1, bytes, data ? data : stderr);
699}
700
701void
702stpui_set_errfunc(stp_outfunc_t wfunc)
703{
704  the_errfunc = wfunc;
705}
706
707stp_outfunc_t
708stpui_get_errfunc(void)
709{
710  return the_errfunc;
711}
712
713void
714stpui_set_errdata(void *errdata)
715{
716  the_errdata = errdata;
717}
718
719void *
720stpui_get_errdata(void)
721{
722  return the_errdata;
723}
724
725void
726stpui_set_thumbnail_func(get_thumbnail_func_t func)
727{
728  thumbnail_func = func;
729}
730
731get_thumbnail_func_t
732stpui_get_thumbnail_func(void)
733{
734  return thumbnail_func;
735}
736
737void
738stpui_set_thumbnail_data(void *data)
739{
740  thumbnail_private_data = data;
741}
742
743void *
744stpui_get_thumbnail_data(void)
745{
746  return thumbnail_private_data;
747}
748
749GtkWidget *
750stpui_create_entry(GtkWidget *table, int hpos, int vpos, const char *text,
751		   const char *help, GCallback callback)
752{
753  GtkWidget *entry = gtk_entry_new();
754  gtk_widget_set_usize(entry, 60, 0);
755  stpui_table_attach_aligned(GTK_TABLE(table), hpos, vpos, text,
756			     0.0, 0.5, entry, 1, TRUE);
757  stpui_set_help_data(entry, help);
758  g_signal_connect(G_OBJECT(entry), "activate",
759		   G_CALLBACK(callback), NULL);
760  return entry;
761}
762
763GSList *
764stpui_create_radio_button(radio_group_t *radio, GSList *group,
765			  GtkWidget *table, int hpos, int vpos,
766			  GCallback callback)
767{
768  radio->button = gtk_radio_button_new_with_label(group, gettext(radio->name));
769  group = gtk_radio_button_group(GTK_RADIO_BUTTON(radio->button));
770  stpui_table_attach_aligned(GTK_TABLE(table), hpos, vpos, NULL, 0.5, 0.5,
771			     radio->button, 1, FALSE);
772  stpui_set_help_data(radio->button, gettext(radio->help));
773  g_signal_connect(G_OBJECT(radio->button), "toggled",
774		   G_CALLBACK(callback), (gpointer) radio->value);
775  return group;
776}
777
778void
779stpui_set_adjustment_tooltip (GtkObject *adj, const gchar *tip)
780{
781  stpui_set_help_data (GTK_WIDGET (SCALE_ENTRY_SCALE (adj)), tip);
782  stpui_set_help_data (GTK_WIDGET (SCALE_ENTRY_SPINBUTTON (adj)), tip);
783}
784
785static GtkWidget *
786table_label(GtkTable *table, gint column, gint row)
787{
788  GList *children = table->children;;
789  while (children)
790    {
791      GtkTableChild *child = (GtkTableChild *)children->data;
792      if (child->left_attach == column + 1 && child->top_attach == row)
793	return child->widget;
794      children = children->next;
795    }
796  return NULL;
797}
798
799void
800stpui_create_new_combo(option_t *option, GtkWidget *table,
801		       int hpos, int vpos, gboolean is_optional)
802{
803  GtkWidget *event_box = gtk_event_box_new();
804  GtkWidget *combo = gtk_combo_new();
805
806  option->checkbox = gtk_check_button_new();
807  gtk_table_attach(GTK_TABLE(table), option->checkbox,
808		   hpos, hpos + 1, vpos, vpos + 1,
809		   GTK_EXPAND|GTK_FILL, GTK_FILL, 0, 0);
810
811  option->info.list.combo = combo;
812  gtk_container_add(GTK_CONTAINER(event_box), combo);
813  gtk_widget_show(combo);
814  gtk_widget_show(event_box);
815  stpui_set_help_data(event_box, gettext(option->fast_desc->help));
816  stpui_table_attach_aligned
817    (GTK_TABLE(table), hpos + 1, vpos, gettext(option->fast_desc->text),
818     0.0, 0.5, event_box, 2, TRUE);
819  option->info.list.label = table_label(GTK_TABLE(table), hpos, vpos);
820}
821
822const char *
823stpui_combo_get_name(GtkWidget   *combo,
824		     const stp_string_list_t *options)
825{
826  if (options)
827    {
828      gint   i;
829      gint num_options = stp_string_list_count(options);
830      const gchar *text = (gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(combo)->entry)));
831
832      if (text == NULL)
833	return (NULL);
834
835      if (num_options == 0)
836	return ((const char *)text);
837
838      for (i = 0; i < num_options; i ++)
839	if (strcmp(stp_string_list_param(options, i)->text, text) == 0)
840	  return (stp_string_list_param(options, i)->name);
841    }
842  return (NULL);
843}
844
845void
846stpui_create_scale_entry(option_t    *opt,
847			 GtkTable    *table,
848			 gint         column,
849			 gint         row,
850			 const gchar *text,
851			 gint         scale_usize,
852			 gint         spinbutton_usize,
853			 gfloat       value,
854			 gfloat       lower,
855			 gfloat       upper,
856			 gfloat       step_increment,
857			 gfloat       page_increment,
858			 guint        digits,
859			 gboolean     constrain,
860			 gfloat       unconstrained_lower,
861			 gfloat       unconstrained_upper,
862			 const gchar *tooltip,
863			 gboolean     is_optional)
864{
865  opt->checkbox = gtk_check_button_new();
866  gtk_table_attach(GTK_TABLE(table), opt->checkbox,
867		   column, column + 1, row, row + 1,
868		   GTK_EXPAND|GTK_FILL, GTK_FILL, 0, 0);
869  opt->info.flt.adjustment =
870    stpui_scale_entry_new(table, column, row, text, scale_usize,
871			  spinbutton_usize, value, lower, upper,
872			  step_increment, page_increment, digits, constrain,
873			  unconstrained_lower, unconstrained_upper, tooltip);
874}
875