view src/paranormal/cfg.c @ 2284:d19b53359b24

cleaned up the sndfile wav plugin, currently limiting it ONLY TO WAV PLAYBACK. if somebody is more experienced with it and wants to restore the other formats, go ahead (maybe change the name of the plugin too?).
author mf0102 <0102@gmx.at>
date Wed, 09 Jan 2008 15:41:22 +0100
parents b8da6a0b0da2
children 769e17da93dd
line wrap: on
line source

/*
 * paranormal: iterated pipeline-driven visualization plugin
 * Copyright (c) 2006, 2007 William Pitcock <nenolod@dereferenced.org>
 * Portions copyright (c) 2001 Jamie Gennis <jgennis@mindspring.com>
 *
 * 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; under version 2 of the License.
 *
 * 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
 */

/* FIXME: prevent the user from dragging something above the root
   actuator */

#include <config.h>

#include <glib.h>
#include <gtk/gtk.h>
#include <audacious/plugin.h>

#include <math.h>

#include "paranormal.h"
#include "actuators.h"
#include "containers.h"
#include "presets.h"

/* DON'T CALL pn_fatal_error () IN HERE!!! */

/* Actuator page stuffs */
static GtkWidget *cfg_dialog, *actuator_tree, *option_frame, *actuator_option_table;
static GtkWidget *actuator_add_opmenu, *actuator_add_button, *actuator_remove_button;
static GtkCTreeNode *selected_actuator_node;
static GtkTooltips *actuator_tooltips;

/* This is used so that actuator_row_data_destroyed_cb won't free
   the actuator associated w/ the node since we're going to be using it */
gboolean destroy_row_data = TRUE;

static void
actuator_row_data_destroyed_cb (struct pn_actuator *a)
{
  if (a && destroy_row_data)
    destroy_actuator (a);
}

static void
add_actuator (struct pn_actuator *a, GtkCTreeNode *parent, gboolean copy)
{
  GtkCTreeNode *node;
  GSList *l;

  g_assert (cfg_dialog);
  g_assert (actuator_tree);
  g_assert (actuator_option_table);

  node = gtk_ctree_insert_node (GTK_CTREE (actuator_tree), parent,
				NULL, (gchar**)&a->desc->dispname, 0,
				NULL, NULL, NULL, NULL,
				a->desc->flags & ACTUATOR_FLAG_CONTAINER
				? FALSE : TRUE,
				TRUE);

  if (a->desc->flags & ACTUATOR_FLAG_CONTAINER)
    for (l=*(GSList **)a->data; l; l = l->next)
      {
	add_actuator (l->data, node, copy);
      }

  if (copy)
    a = copy_actuator (a);
  else if (a->desc->flags & ACTUATOR_FLAG_CONTAINER)
    container_unlink_actuators (a);

  gtk_ctree_node_set_row_data_full (GTK_CTREE (actuator_tree), node, a,
				    ((GtkDestroyNotify) actuator_row_data_destroyed_cb));
}

static guchar
gdk_colour_to_paranormal_colour(gint16 colour)
{
  return (guchar) (colour / 255);
}

static gint16
paranormal_colour_to_gdk_colour(guchar colour)
{
  return (gint16) (colour * 255);
}

static void
int_changed_cb (GtkSpinButton *sb, int *i)
{
  *i = gtk_spin_button_get_value_as_int (sb);
}

static void
float_changed_cb (GtkSpinButton *sb, float *f)
{
  *f = gtk_spin_button_get_value_as_float (sb);
}

static void
string_changed_cb (GtkEditable *t, char **s)
{
  if (*s != gtk_object_get_data (GTK_OBJECT (t), "DEFAULT_OP_STRING"))
    g_free (*s);

  *s = gtk_editable_get_chars (t, 0, -1);
}

static void
color_changed_cb (GtkColorButton *cb, struct pn_color *c)
{
  GdkColor colour;

  gtk_color_button_get_color(cb, &colour);

  c->r = gdk_colour_to_paranormal_colour(colour.red);
  c->g = gdk_colour_to_paranormal_colour(colour.green);
  c->b = gdk_colour_to_paranormal_colour(colour.blue);
}

static void
boolean_changed_cb (GtkToggleButton *tb, gboolean *b)
{
  *b = gtk_toggle_button_get_active (tb);
}

static void
row_select_cb (GtkCTree *ctree, GtkCTreeNode *node,
	       gint column, gpointer data)
{
  struct pn_actuator *a;
  int opt_count = 0, i, j;
  GtkWidget *w;
  GtkObject *adj;

  a = (struct pn_actuator *)gtk_ctree_node_get_row_data (ctree, node);

  /* count the actuator's options (plus one) */
  if (a->desc->option_descs)
    while (a->desc->option_descs[opt_count++].name);
  else
    opt_count = 1;

  gtk_table_resize (GTK_TABLE (actuator_option_table), opt_count, 2);

  /* Actuator name */
  gtk_frame_set_label (GTK_FRAME (option_frame), a->desc->dispname);

  /* Actuator description */
  w = gtk_label_new (a->desc->doc);
  gtk_label_set_line_wrap (GTK_LABEL (w), TRUE);
  gtk_label_set_justify (GTK_LABEL (w), GTK_JUSTIFY_LEFT);
  gtk_misc_set_alignment (GTK_MISC (w), 0, .5);
  gtk_widget_show (w);
  gtk_table_attach (GTK_TABLE (actuator_option_table), w, 0, 2, 0, 1,
		    GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0,
		    3, 3);

  /* now add the options */
  for (i=1, j=0; i<opt_count; j++, i++)
    {
      w = gtk_label_new (a->desc->option_descs[j].name);
      gtk_widget_show (w);
      gtk_table_attach (GTK_TABLE (actuator_option_table), w,
			0, 1, i, i+1,
			GTK_SHRINK | GTK_FILL, 0,
			3, 3);
      switch (a->desc->option_descs[j].type)
	{
	case OPT_TYPE_INT:
	  adj = gtk_adjustment_new (a->options[j].val.ival,
				    G_MININT, G_MAXINT,
				    1, 2, 0);
	  w = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 1.0, 0);
	  gtk_signal_connect (GTK_OBJECT (w), "changed",
			      GTK_SIGNAL_FUNC (int_changed_cb),
			      &a->options[j].val.ival);
	  break;
	case OPT_TYPE_FLOAT:
	  adj = gtk_adjustment_new (a->options[j].val.fval,
				    -G_MAXFLOAT, G_MAXFLOAT,
				    1, 2, 0);
	  w = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 1.0, 5);
	  gtk_signal_connect (GTK_OBJECT (w), "changed",
			      GTK_SIGNAL_FUNC (float_changed_cb),
			      &a->options[j].val.fval);
	  break;
	case OPT_TYPE_STRING:
	  w = gtk_entry_new ();
	  gtk_widget_show (w);
	  gtk_entry_set_text (GTK_ENTRY (w), a->options[j].val.sval);
	  gtk_object_set_data (GTK_OBJECT (w), "DEFAULT_OP_STRING",
			       (gpointer)a->desc->option_descs[j].default_val.sval);
	  gtk_signal_connect (GTK_OBJECT (w), "changed",
			      GTK_SIGNAL_FUNC (string_changed_cb),
			      &a->options[j].val.sval);
	  break;
	case OPT_TYPE_COLOR:
	  {
	    /* FIXME: add some color preview */
            GdkColor *colour = g_new0(GdkColor, 1);

            colour->red = paranormal_colour_to_gdk_colour(a->options[j].val.cval.r);
            colour->green = paranormal_colour_to_gdk_colour(a->options[j].val.cval.g);
            colour->blue = paranormal_colour_to_gdk_colour(a->options[j].val.cval.b);

            w = gtk_color_button_new_with_color(colour);
	    g_signal_connect(G_OBJECT (w), "color-set",
			       G_CALLBACK (color_changed_cb),
			       &a->options[j].val.cval);
	    gtk_tooltips_set_tip (actuator_tooltips, GTK_WIDGET(w),
				  a->desc->option_descs[j].doc, NULL);
	  }
	  break;	  
	case OPT_TYPE_COLOR_INDEX:
	  adj = gtk_adjustment_new (a->options[j].val.ival,
				    0, 255,
				    1, 2, 0);
	  w = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 1.0, 0);
	  gtk_signal_connect (GTK_OBJECT (w), "changed",
			      GTK_SIGNAL_FUNC (int_changed_cb),
			      &a->options[j].val.ival);
	  break;
	case OPT_TYPE_BOOLEAN:
	  w = gtk_check_button_new ();
	  gtk_widget_show (w);
	  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w),
					a->options[j].val.bval);
	  gtk_signal_connect (GTK_OBJECT (w), "clicked",
			      GTK_SIGNAL_FUNC (boolean_changed_cb),
			      &a->options[j].val.bval);
	  break;
	}
      gtk_widget_show (w);
      gtk_tooltips_set_tip (actuator_tooltips, w,
			    a->desc->option_descs[j].doc, NULL);
      gtk_table_attach (GTK_TABLE (actuator_option_table), w,
			1, 2, i, i+1,
			GTK_EXPAND | GTK_SHRINK | GTK_FILL,
			0,
			3, 3);
    }

  gtk_widget_set_sensitive (actuator_remove_button, TRUE);
  gtk_widget_set_sensitive (actuator_add_button, a->desc->flags & ACTUATOR_FLAG_CONTAINER
			? TRUE : FALSE);

  selected_actuator_node = node;
}

static void
table_remove_all_cb (GtkWidget *widget, gpointer data)
{
  gtk_container_remove (GTK_CONTAINER (actuator_option_table),
			widget);
}

static void
row_unselect_cb (GtkCTree *ctree, GList *node, gint column,
		 gpointer user_data)
{
  gtk_frame_set_label (GTK_FRAME (option_frame), NULL);

  gtk_container_foreach (GTK_CONTAINER (actuator_option_table),
			 table_remove_all_cb, NULL);

  /* Can't remove something if nothing's selected */
  gtk_widget_set_sensitive (actuator_remove_button, FALSE);

  selected_actuator_node = NULL;
}

static void
add_actuator_cb (GtkButton *button, gpointer data)
{
  char *actuator_name;
  struct pn_actuator *a;
  
  gtk_label_get (GTK_LABEL (GTK_BIN (actuator_add_opmenu)->child),
                &actuator_name);

  a = create_actuator (actuator_name);
  g_assert (a);

  add_actuator (a, selected_actuator_node, FALSE);
}

static void
remove_actuator_cb (GtkButton *button, gpointer data)
{
  if (selected_actuator_node)
    gtk_ctree_remove_node (GTK_CTREE (actuator_tree),
			   selected_actuator_node);
}

/* Connect a node to its parent and replace the row data with
   a copy of the node */
static void
connect_actuators_cb (GtkCTree *ctree, GtkCTreeNode *node,
		      struct pn_actuator **root_ptr)
{
  struct pn_actuator *actuator, *parent, *copy;

  actuator = (struct pn_actuator *) gtk_ctree_node_get_row_data (ctree, node);
  if (GTK_CTREE_ROW (node)->parent)
    {
      /* Connect it to the parent */
      parent = (struct pn_actuator *)
	gtk_ctree_node_get_row_data (ctree, GTK_CTREE_ROW (node)->parent);
      container_add_actuator (parent, actuator);
    }
  else
    /* This is the root node; still gotta copy it, but we need to
       save the original to *root_ptr */
    *root_ptr = actuator;

  /* we don't want our copy getting destroyed */
  destroy_row_data = FALSE;

  copy = copy_actuator (actuator);
  gtk_ctree_node_set_row_data_full (ctree, node, copy,
				    ((GtkDestroyNotify)actuator_row_data_destroyed_cb));

  /* Ok, now you can destroy it */
  destroy_row_data = TRUE;
}

/* Extract (and connect) the actuators in the tree */
static struct pn_actuator *
extract_actuator (void)
{
  GtkCTreeNode *root, *selected;
  struct pn_actuator *root_actuator = NULL;

  root = gtk_ctree_node_nth (GTK_CTREE (actuator_tree), 0);
  if (root)
    gtk_ctree_post_recursive (GTK_CTREE (actuator_tree), root,
			      GTK_CTREE_FUNC (connect_actuators_cb),
			      &root_actuator);

  if (selected_actuator_node)
    {
      selected = selected_actuator_node;
      gtk_ctree_unselect (GTK_CTREE (actuator_tree), GTK_CTREE_NODE (selected));
      gtk_ctree_select (GTK_CTREE (actuator_tree), GTK_CTREE_NODE (selected));
    }

  return root_actuator;
}

/* If selector != NULL, then it's 'OK', otherwise it's 'Cancel' */
static void
load_sel_cb (GtkButton *button, GtkFileSelection *selector)
{
  if (selector)
    {
      static const char *fname;
      struct pn_actuator *a;
      GtkCTreeNode *root;
      ConfigDb *db;

      db = aud_cfg_db_open();
      fname = (char *) gtk_file_selection_get_filename (selector);
      a = load_preset (fname);
      aud_cfg_db_set_string(db, "paranormal", "last_path", (char*)fname);
      aud_cfg_db_close(db);
      if (! a)
	pn_error ("Unable to load file: \"%s\"", fname);
      else
	{
	  if ((root = gtk_ctree_node_nth (GTK_CTREE (actuator_tree), 0)))
	    gtk_ctree_remove_node (GTK_CTREE (actuator_tree), root);
	  add_actuator (a, NULL, FALSE);
	}
    }

  gtk_widget_set_sensitive (cfg_dialog, TRUE);
}

static void
load_button_cb (GtkButton *button, gpointer data)
{
  GtkWidget *selector;
  ConfigDb *db;
  gchar *last_path;

  db = aud_cfg_db_open();
  selector = gtk_file_selection_new ("Load Preset");
  if(aud_cfg_db_get_string(db, "paranormal", "last_path", &last_path)) {
     gtk_file_selection_set_filename(GTK_FILE_SELECTION(selector), last_path);
  }
  aud_cfg_db_close(db);

  gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (selector)->ok_button),
		      "clicked", GTK_SIGNAL_FUNC (load_sel_cb), selector);
  gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (selector)->cancel_button),
		      "clicked", GTK_SIGNAL_FUNC (load_sel_cb), NULL);

  gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (selector)->ok_button),
			     "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy),
			     (gpointer) selector);
  gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (selector)->cancel_button),
			     "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy),
			     (gpointer) selector);

  gtk_widget_set_sensitive (cfg_dialog, FALSE);
  gtk_widget_show (selector);
}

static void
save_sel_cb (GtkButton *button, GtkFileSelection *selector)
{
  if (selector)
    {
      const char *fname;
      struct pn_actuator *a;

      fname = (char *) gtk_file_selection_get_filename (selector);
      a = extract_actuator ();

      if (! save_preset (fname, a))
	pn_error ("unable to save preset to file: %s", fname);
    }

  gtk_widget_set_sensitive (cfg_dialog, TRUE);
}

static void
save_button_cb (GtkButton *button, gpointer data)
{
  GtkWidget *selector;

  selector = gtk_file_selection_new ("Save Preset");

  gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (selector)->ok_button),
		      "clicked", GTK_SIGNAL_FUNC (save_sel_cb), selector);
  gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (selector)->cancel_button),
		      "clicked", GTK_SIGNAL_FUNC (save_sel_cb), NULL);

  gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (selector)->ok_button),
			     "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy),
			     (gpointer) selector);
  gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (selector)->cancel_button),
			     "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy),
			     (gpointer) selector);

  gtk_widget_set_sensitive (cfg_dialog, FALSE);
  gtk_widget_show (selector);
}

static void
apply_settings (void)
{
  struct pn_rc rc;

  rc.actuator = extract_actuator ();

  pn_set_rc (&rc);
}

static void
apply_button_cb (GtkButton *button, gpointer data)
{
  apply_settings ();
}

static void
ok_button_cb (GtkButton *button, gpointer data)
{
  apply_settings ();
  gtk_widget_hide (cfg_dialog);
}

static void
cancel_button_cb (GtkButton *button, gpointer data)
{
  gtk_widget_destroy (cfg_dialog);
  cfg_dialog = NULL;
}

void
pn_configure (void)
{
  GtkWidget *notebook, *label, *scrollwindow, *menu, *menuitem;
  GtkWidget *paned, *vbox, *table, *bbox, *button;
  int i;
  

  if (! cfg_dialog)
    {
      /* The dialog */
      cfg_dialog = gtk_dialog_new ();
      gtk_window_set_title (GTK_WINDOW (cfg_dialog), "Paranormal Visualization Studio - Editor");
      gtk_widget_set_usize (cfg_dialog, 530, 370);
      gtk_container_border_width (GTK_CONTAINER (cfg_dialog), 8);
      gtk_signal_connect_object (GTK_OBJECT (cfg_dialog), "delete-event",
				 GTK_SIGNAL_FUNC (gtk_widget_hide),
				 GTK_OBJECT (cfg_dialog));

      /* The notebook */
      notebook = gtk_notebook_new ();
      gtk_widget_show (notebook);
      gtk_box_pack_start (GTK_BOX (GTK_DIALOG (cfg_dialog)->vbox), notebook,
			  TRUE, TRUE, 0);

      /* Actuator page */
      paned = gtk_hpaned_new ();
      gtk_widget_show (paned);
      label = gtk_label_new ("Actuators");
      gtk_widget_show (label);
      gtk_notebook_append_page (GTK_NOTEBOOK (notebook), paned, label);
      vbox = gtk_vbox_new (FALSE, 3);
      gtk_widget_show (vbox);
      gtk_paned_pack1 (GTK_PANED (paned), vbox, TRUE, FALSE);
      scrollwindow = gtk_scrolled_window_new (NULL, NULL);
      gtk_widget_show (scrollwindow);
      gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollwindow),
				      GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
      gtk_box_pack_start (GTK_BOX (vbox), scrollwindow, TRUE, TRUE, 3);
      actuator_tree = gtk_ctree_new (1, 0);
      gtk_widget_show (actuator_tree);
      gtk_ctree_set_reorderable (GTK_CTREE (actuator_tree), TRUE);
      gtk_signal_connect (GTK_OBJECT (actuator_tree), "tree-select-row",
			  GTK_SIGNAL_FUNC (row_select_cb), NULL);
      gtk_signal_connect (GTK_OBJECT (actuator_tree), "tree-unselect-row",
			  GTK_SIGNAL_FUNC (row_unselect_cb), NULL);
      gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrollwindow),
					     actuator_tree);
      table = gtk_table_new (3, 2, TRUE);
      gtk_widget_show (table);
      gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 3);      
      actuator_add_opmenu = gtk_option_menu_new ();
      gtk_widget_show (actuator_add_opmenu);
      menu = gtk_menu_new ();
      gtk_widget_show (menu);
      for (i=0; builtin_table[i]; i++)
	{
	  /* FIXME: Add actuator group support */
	  menuitem = gtk_menu_item_new_with_label (builtin_table[i]->dispname);
	  gtk_widget_show (menuitem);
	  gtk_menu_append (GTK_MENU (menu), menuitem);
	}
      gtk_option_menu_set_menu (GTK_OPTION_MENU (actuator_add_opmenu), menu);
      gtk_table_attach (GTK_TABLE (table), actuator_add_opmenu,
			0, 2, 0, 1,
			GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0,
			3, 3);
      actuator_add_button = gtk_button_new_from_stock(GTK_STOCK_ADD);
      gtk_widget_show (actuator_add_button);
      gtk_signal_connect (GTK_OBJECT (actuator_add_button), "clicked",
			  GTK_SIGNAL_FUNC (add_actuator_cb), NULL);
      gtk_table_attach (GTK_TABLE (table), actuator_add_button,
			0, 1, 1, 2,
			GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0,
			3, 3);
      actuator_remove_button = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
      gtk_widget_set_sensitive (actuator_remove_button, FALSE);
      gtk_widget_show (actuator_remove_button);
      gtk_signal_connect (GTK_OBJECT (actuator_remove_button), "clicked",
			  GTK_SIGNAL_FUNC (remove_actuator_cb), NULL);
      gtk_table_attach (GTK_TABLE (table), actuator_remove_button,
			1, 2, 1, 2,
			GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0,
			3, 3);
      button = gtk_button_new_from_stock(GTK_STOCK_OPEN);
      gtk_widget_show (button);
      gtk_signal_connect (GTK_OBJECT (button), "clicked",
			  GTK_SIGNAL_FUNC (load_button_cb), NULL);
      gtk_table_attach (GTK_TABLE (table), button,
			0, 1, 2, 3,
			GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0,
			3, 3);
      button = gtk_button_new_from_stock(GTK_STOCK_SAVE);
      gtk_widget_show (button);
      gtk_signal_connect (GTK_OBJECT (button), "clicked",
			  GTK_SIGNAL_FUNC (save_button_cb), NULL);
      gtk_table_attach (GTK_TABLE (table), button,
			1, 2, 2, 3,
			GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0,
			3, 3);

      /* Option table */
      option_frame = gtk_frame_new (NULL);
      gtk_widget_show (option_frame);
      gtk_container_set_border_width (GTK_CONTAINER (option_frame), 3);
      gtk_paned_pack2 (GTK_PANED (paned), option_frame, TRUE, TRUE);
      scrollwindow = gtk_scrolled_window_new (NULL, NULL);
      gtk_widget_show (scrollwindow);
      gtk_container_set_border_width (GTK_CONTAINER (scrollwindow), 3);
      gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollwindow),
				      GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
      gtk_container_add (GTK_CONTAINER (option_frame), scrollwindow);
      actuator_option_table = gtk_table_new (0, 2, FALSE);
      gtk_widget_show (actuator_option_table);
      gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrollwindow),
					     actuator_option_table);
      gtk_paned_set_position (GTK_PANED (paned), 0);
      actuator_tooltips = gtk_tooltips_new ();
      gtk_tooltips_enable (actuator_tooltips);

      /* Build the initial actuator actuator_tree */
      if (pn_rc->actuator)
	{
	  add_actuator (pn_rc->actuator, NULL, TRUE);
	  gtk_widget_set_sensitive (actuator_add_button, FALSE);
	}

      /* OK / Apply / Cancel */
      bbox = gtk_hbutton_box_new ();
      gtk_widget_show (bbox);
      gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
      gtk_button_box_set_spacing (GTK_BUTTON_BOX (bbox), 8);
      gtk_button_box_set_child_size (GTK_BUTTON_BOX (bbox), 64, 0);
      gtk_box_pack_start (GTK_BOX (GTK_DIALOG (cfg_dialog)->action_area),
			  bbox, FALSE, FALSE, 0);

      button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
      gtk_widget_show (button);
      gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NORMAL);
      gtk_signal_connect (GTK_OBJECT (button), "clicked",
			  GTK_SIGNAL_FUNC (cancel_button_cb), NULL);
      gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 0);

      button = gtk_button_new_from_stock (GTK_STOCK_APPLY);
      gtk_widget_show (button);
      gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NORMAL);
      gtk_signal_connect (GTK_OBJECT (button), "clicked",
			  GTK_SIGNAL_FUNC (apply_button_cb), NULL);
      gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 0);

      button = gtk_button_new_from_stock (GTK_STOCK_OK);
      gtk_widget_show (button);
      gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NORMAL);
      gtk_signal_connect (GTK_OBJECT (button), "clicked",
			  GTK_SIGNAL_FUNC (ok_button_cb), NULL);
      gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 0);
    }

  gtk_widget_show (cfg_dialog);
  gtk_widget_grab_focus (cfg_dialog);
}