/* The GIMP -- an image manipulation program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * 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.
 */
#include "config.h"

#include <string.h>
#include "appenv.h"
#include "gimprc.h"
#include "info_dialog.h"
#include "interface.h"
#include "session.h"

#include "libgimp/gimpintl.h"
#include "libgimp/gimpsizeentry.h"

/*  static functions  */
static void info_field_new (InfoDialog *, InfoFieldType, char *, GtkWidget *,
			    GtkObject *, void *, GtkSignalFunc, gpointer);
static void update_field   (InfoField *);
static gint info_dialog_delete_callback (GtkWidget *, GdkEvent *, gpointer);

static void
info_field_new (InfoDialog    *idialog,
		InfoFieldType  field_type,
		gchar         *title,
		GtkWidget     *widget,
		GtkObject     *obj,
		void          *value_ptr,
		GtkSignalFunc  callback,
		gpointer       client_data)
{
  GtkWidget *label;
  InfoField *field;
  gint       row;

  field = g_new (InfoField, 1);

  row = idialog->nfields + 1;
  gtk_table_resize (GTK_TABLE (idialog->info_table), 2, row);

  label = gtk_label_new (title);
  gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  gtk_table_attach (GTK_TABLE (idialog->info_table), label,
		    0, 1, row - 1, row,
		    GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
  gtk_widget_show (label);

  gtk_table_attach_defaults (GTK_TABLE (idialog->info_table), widget,
			     1, 2, row - 1, row);
  gtk_widget_show (widget);

  gtk_table_set_col_spacing (GTK_TABLE (idialog->info_table), 0, 6);
  gtk_table_set_row_spacings (GTK_TABLE (idialog->info_table), 2);

  field->field_type = field_type;
  if (obj == NULL)
    field->obj = GTK_OBJECT (widget);
  else
    field->obj = obj;
  field->value_ptr = value_ptr;
  field->callback = callback;
  field->client_data = client_data;

  idialog->field_list =
    g_slist_prepend (idialog->field_list, (void *) field);
  idialog->nfields++;
}

static void
update_field (InfoField *field)
{
  gchar *old_text;
  gint   num;
  gint   i;

  if (field->value_ptr == NULL)
    return;

  if (field->field_type != INFO_LABEL)
    gtk_signal_handler_block_by_data (GTK_OBJECT (field->obj),
				      field->client_data);

  switch (field->field_type)
    {
    case INFO_LABEL:
      gtk_label_get (GTK_LABEL (field->obj), &old_text);
      if (strcmp (old_text, (gchar*) field->value_ptr))
	gtk_label_set_text (GTK_LABEL (field->obj), (gchar*) field->value_ptr);
      break;

    case INFO_ENTRY:
      old_text = gtk_entry_get_text (GTK_ENTRY (field->obj));
      if (strcmp (old_text, (gchar*) field->value_ptr))
	gtk_entry_set_text (GTK_ENTRY (field->obj), (gchar*) field->value_ptr);
      break;

    case INFO_SCALE:
    case INFO_SPINBUTTON:
      gtk_adjustment_set_value (GTK_ADJUSTMENT (field->obj),
				*((gdouble*) field->value_ptr));
      break;

    case INFO_SIZEENTRY:
      num = GIMP_SIZE_ENTRY (field->obj)->number_of_fields;
      for (i = 0; i < num; i++)
	gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (field->obj), i,
				    ((gdouble*) field->value_ptr)[i]);
      break;

    default:
      g_warning ("Unknown info_dialog field type.");
      break;
    }

  if (field->field_type != INFO_LABEL)
    gtk_signal_handler_unblock_by_data (GTK_OBJECT (field->obj),
					field->client_data);
}

static gint
info_dialog_delete_callback (GtkWidget *widget,
			     GdkEvent  *event,
			     gpointer   data)
{
  info_dialog_popdown ((InfoDialog *) data);

  return TRUE;
}

static InfoDialog *
info_dialog_new_extended (gchar        *title,
			  GimpHelpFunc  help_func,
			  gpointer      help_data,
			  gboolean      in_notebook)
{
  InfoDialog *idialog;
  GtkWidget  *shell;
  GtkWidget  *vbox;
  GtkWidget  *info_table;
  GtkWidget  *info_notebook;

  idialog = g_new (InfoDialog, 1);
  idialog->field_list = NULL;
  idialog->nfields    = 0;

  shell = gtk_dialog_new ();
  gtk_window_set_wmclass (GTK_WINDOW (shell), "info_dialog", "Gimp");
  gtk_window_set_title (GTK_WINDOW (shell), title);
  session_set_window_geometry (shell, &info_dialog_session_info, FALSE );

  gtk_signal_connect (GTK_OBJECT (shell), "delete_event",
		      GTK_SIGNAL_FUNC (info_dialog_delete_callback),
		      idialog);

  vbox = gtk_vbox_new (FALSE, 0);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
  gtk_container_add (GTK_CONTAINER (GTK_DIALOG (shell)->vbox), vbox);

  info_table = gtk_table_new (2, 0, FALSE);

  if (in_notebook)
    {
      info_notebook = gtk_notebook_new ();
      gtk_notebook_append_page (GTK_NOTEBOOK (info_notebook),
				info_table,
				gtk_label_new (_("General")));
      gtk_box_pack_start (GTK_BOX (vbox), info_notebook, FALSE, FALSE, 0);
    }
  else
    {
      info_notebook = NULL;
      gtk_box_pack_start (GTK_BOX (vbox), info_table, FALSE, FALSE, 0);
    }

  idialog->shell = shell;
  idialog->vbox = vbox;
  idialog->info_table = info_table;
  idialog->info_notebook = info_notebook;

  if (in_notebook)
    gtk_widget_show (idialog->info_notebook);

  gtk_widget_show (idialog->info_table);
  gtk_widget_show (idialog->vbox);

  /*  Connect the "F1" help key  */
  gimp_help_connect_help_accel (idialog->shell, help_func, help_data);

  return idialog;
}

/*  public functions  */

InfoDialog *
info_dialog_notebook_new (gchar        *title,
			  GimpHelpFunc  help_func,
			  gpointer      help_data)
{
  return info_dialog_new_extended (title, help_func, help_data, TRUE);
}

InfoDialog *
info_dialog_new (gchar        *title,
		 GimpHelpFunc  help_func,
		 gpointer      help_data)
{
  return info_dialog_new_extended (title, help_func, help_data, FALSE);
}

void
info_dialog_free (InfoDialog *idialog)
{
  GSList *list;

  g_return_if_fail (idialog != NULL);

  /*  Free each item in the field list  */
  for (list = idialog->field_list; list; list = g_slist_next (list))
    g_free (list->data);

  /*  Free the actual field linked list  */
  g_slist_free (idialog->field_list);

  session_get_window_info (idialog->shell, &info_dialog_session_info);

  /*  Destroy the associated widgets  */
  gtk_widget_destroy (idialog->shell);

  /*  Free the info dialog memory  */
  g_free (idialog);
}

void
info_dialog_popup (InfoDialog *idialog)
{
  if (! idialog)
    return;

  if (! GTK_WIDGET_VISIBLE (idialog->shell))
    gtk_widget_show (idialog->shell);

}

void
info_dialog_popdown (InfoDialog *idialog)
{
  if (! idialog)
    return;
  
  if (GTK_WIDGET_VISIBLE (idialog->shell))
    gtk_widget_hide (idialog->shell);
}

void
info_dialog_update (InfoDialog *idialog)
{
  GSList *list;

  if (! idialog)
    return;

  for (list = idialog->field_list; list; list = g_slist_next (list))
    update_field ((InfoField *) list->data);
}

GtkWidget *
info_dialog_add_label (InfoDialog    *idialog,
		       char          *title,
		       char          *text_ptr)
{
  GtkWidget *label;

  g_return_val_if_fail (idialog != NULL, NULL);

  label = gtk_label_new (text_ptr);
  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);

  info_field_new (idialog, INFO_LABEL, title, label, NULL, (void*) text_ptr,
		  NULL, NULL);

  return label;
}

GtkWidget *
info_dialog_add_entry (InfoDialog    *idialog,
		       char          *title,
		       char          *text_ptr,
		       GtkSignalFunc  callback,
		       gpointer       data)
{
  GtkWidget *entry;

  g_return_val_if_fail (idialog != NULL, NULL);

  entry = gtk_entry_new ();
  gtk_widget_set_usize (entry, 50, 0);
  gtk_entry_set_text (GTK_ENTRY (entry), text_ptr ? text_ptr : "");

  if (callback)
    gtk_signal_connect (GTK_OBJECT (entry), "changed",
			GTK_SIGNAL_FUNC (callback), data);

  info_field_new (idialog, INFO_ENTRY, title, entry, NULL, (void*) text_ptr,
		  callback, data);

  return entry;
}

GtkWidget *
info_dialog_add_scale   (InfoDialog    *idialog,
			 gchar         *title,
			 gdouble       *value_ptr,
			 gfloat         lower,
			 gfloat         upper,
			 gfloat         step_increment,
			 gfloat         page_increment,
			 gfloat         page_size,
			 gint           digits,
			 GtkSignalFunc  callback,
			 gpointer       data)
{
  GtkObject *adjustment;
  GtkWidget *scale;

  g_return_val_if_fail (idialog != NULL, NULL);

  adjustment = gtk_adjustment_new (value_ptr ? *value_ptr : 0, lower, upper,
				   step_increment, page_increment, page_size);
  scale = gtk_hscale_new (GTK_ADJUSTMENT (adjustment));

  if (digits >= 0)
    gtk_scale_set_digits (GTK_SCALE (scale), MAX (digits, 6));
  else
    gtk_scale_set_draw_value (GTK_SCALE (scale), FALSE);

  if (callback)
    gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
			GTK_SIGNAL_FUNC (callback), data);

  info_field_new (idialog, INFO_SCALE, title, scale, adjustment,
		  (void*) value_ptr, callback, data);

  return scale;
}

GtkWidget *
info_dialog_add_spinbutton (InfoDialog    *idialog,
			    gchar         *title,
			    gdouble       *value_ptr,
			    gfloat         lower,
			    gfloat         upper,
			    gfloat         step_increment,
			    gfloat         page_increment,
			    gfloat         page_size,
			    gfloat         climb_rate,
			    gint           digits,
			    GtkSignalFunc  callback,
			    gpointer       data)
{
  GtkWidget *alignment;
  GtkObject *adjustment;
  GtkWidget *spinbutton;

  g_return_val_if_fail (idialog != NULL, NULL);

  alignment = gtk_alignment_new (0.0, 0.5, 0.0, 1.0);

  adjustment = gtk_adjustment_new (value_ptr ? *value_ptr : 0, lower, upper,
				   step_increment, page_increment, page_size);
  spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (adjustment),
				    climb_rate, MAX (MIN (digits, 6), 0));
  gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (spinbutton),
				   GTK_SHADOW_NONE);
  gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
  gtk_widget_set_usize (spinbutton, 75, 0);

  if (callback)
    gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
			GTK_SIGNAL_FUNC (callback), data);

  gtk_container_add (GTK_CONTAINER (alignment), spinbutton);
  gtk_widget_show (spinbutton);

  info_field_new (idialog, INFO_SPINBUTTON, title, alignment, adjustment,
		  (void*) value_ptr, callback, data);

  return spinbutton;
}

GtkWidget *
info_dialog_add_sizeentry (InfoDialog      *idialog,
			   gchar           *title,
			   gdouble         *value_ptr,
			   gint             nfields,
			   GUnit            unit,
			   gchar           *unit_format,
			   gboolean         menu_show_pixels,
			   gboolean         menu_show_percent,
			   gboolean         show_refval,
			   GimpSizeEntryUP  update_policy,
			   GtkSignalFunc    callback,
			   gpointer         data)
{
  GtkWidget *alignment;
  GtkWidget *sizeentry;
  gint       i;

  g_return_val_if_fail (idialog != NULL, NULL);

  alignment = gtk_alignment_new (0.0, 0.5, 0.0, 1.0);

  sizeentry = gimp_size_entry_new (nfields, unit, unit_format,
				   menu_show_pixels, menu_show_percent,
				   show_refval, 75,
				   update_policy);
  if (value_ptr)
    for (i = 0; i < nfields; i++)
      gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (sizeentry), i, value_ptr[i]);

  if (callback)
    gtk_signal_connect (GTK_OBJECT (sizeentry), "value_changed",
			GTK_SIGNAL_FUNC (callback), data);

  gtk_container_add (GTK_CONTAINER (alignment), sizeentry);
  gtk_widget_show (sizeentry);

  info_field_new (idialog, INFO_SIZEENTRY, title, alignment,
		  GTK_OBJECT (sizeentry),
		  (void*) value_ptr, callback, data);

  return sizeentry;
}
