/*   EXTRAITS DE LA LICENCE
	Copyright CEA, contributeurs : Luc BILLARD, Damien
	CALISTE, Olivier D'Astier, laboratoire L_Sim, (2001-2005)
  
	Adresses mèl :
	BILLARD, non joignable par mèl ;
	CALISTE, damien P caliste AT cea P fr.
	D'ASTIER, dastier AT iie P cnam P fr.

	Ce logiciel est un programme informatique servant à visualiser des
	structures atomiques dans un rendu pseudo-3D. 

	Ce logiciel est régi par la licence CeCILL soumise au droit français et
	respectant les principes de diffusion des logiciels libres. Vous pouvez
	utiliser, modifier et/ou redistribuer ce programme sous les conditions
	de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA 
	sur le site "http://www.cecill.info".

	Le fait que vous puissiez accéder à cet en-tête signifie que vous avez 
	pris connaissance de la licence CeCILL, et que vous en avez accepté les
	termes (cf. le fichier Documentation/licence.fr.txt fourni avec ce logiciel).
*/

/*   LICENCE SUM UP
	Copyright CEA, contributors : Luc BILLARD and Damien
	CALISTE and Olivier D'Astier, laboratoire L_Sim, (2001-2005)

	E-mail addresses :
	BILLARD, not reachable any more ;
	CALISTE, damien P caliste AT cea P fr.
	D'ASTIER, dastier AT iie P cnam P fr.

	This software is a computer program whose purpose is to visualize atomic
	configurations in 3D.

	This software is governed by the CeCILL  license under French law and
	abiding by the rules of distribution of free software.  You can  use, 
	modify and/ or redistribute the software under the terms of the CeCILL
	license as circulated by CEA, CNRS and INRIA at the following URL
	"http://www.cecill.info". 

	The fact that you are presently reading this means that you have had
	knowledge of the CeCILL license and that you accept its terms. You can
	find a copy of this licence shipped with this software at Documentation/licence.en.txt.
*/

#include "panelMap.h"
#include "panelPlanes.h"
#include "panelSurfaces.h"

#include <support.h>
#include <visu_extension.h>
#include <visu_gtk.h>
#include <gtk_main.h>
#include <extensions/mapset.h>
#include <coreTools/toolMatrix.h>
#include <extraFunctions/plane.h>
#include <extraFunctions/scalarFields.h>
#include <extraGtkFunctions/gtk_toolPanelWidget.h>
#include <extraGtkFunctions/gtk_shadeComboBoxWidget.h>
#include <extraGtkFunctions/gtk_colorComboBoxWidget.h>
#include <extraGtkFunctions/gtk_numericalEntryWidget.h>
#include <openGLFunctions/objectList.h>
#include <openGLFunctions/view.h>
#include <openGLFunctions/text.h>

/**
 * SECTION:panelMap
 * @short_description: The widget to create coloured map.
 *
 * <para>This is the user interface for the coloured maps. For a
 * plane, a scalar field and a shade, it is possible to draw one
 * coloured plane. The available planes are taken from the #panelVisuPlane
 * subpanel and the scalar field for the #panelSurfaces.</para>
 */

/* Local variables. */
static GtkWidget *panelMap;
static GtkWidget *comboVisuPlane;
static GtkWidget *comboField;
static GtkWidget *comboMap;
static GtkWidget *rdLinear, *rdLog, *rdZero;
static GtkWidget *ckColour, *cbColour;
static GtkWidget *removeButton, *buildButton, *exportButton;
static GtkWidget *radioNormalized, *radioMinMax;
static GtkWidget *entryDataMax, *entryDataMin;
static gboolean isMapInitialised;
static GtkWidget *warnLabel;
static gulong comboMap_signal;

/* String used to labelled planes, dist. means 'distance' and
   norm. means 'normal' (50 chars max). */
#define LABEL_PLANE _("<span size=\"small\">plane (%2d;%2d;%2d - %4.1f)</span>")
enum
  {
    MAP_OBJ,
    MAP_SIG,
    MAP_N_COLUMNS
  };
static GtkListStore *maps;

/* Local methods. */
static void createGtkInterface(VisuUiPanel *panel);
static void updateInterface(gboolean selectFirst);

/* Local callbacks. */
static void onMapAdded(GtkListStore *model, VisuMap *map);
static void onMapRemoved(GtkListStore *model, VisuMap *map);
static void onBuildClicked(GtkButton *button, gpointer data);
static void onRemoveClicked(GtkButton *button, gpointer data);
static void onExportClicked(GtkButton *button, gpointer data);
static void onFieldChanged(GtkComboBox *combo, gpointer data);
static void onToolShadeChanged(VisuUiShadeCombobox *combo, ToolShade *shade, gpointer data);
static void onPlaneChanged(GtkComboBox *combo, gpointer data);
static void onComboMapChanged(GtkComboBox *combo, gpointer data);
static void onMapEnter(VisuUiPanel *map, gpointer data);
static void onPlaneDeleted(GtkTreeModel *tree_model, GtkTreePath  *path,
			   gpointer user_data);
static void onSourceField(VisuGlExtMapSet *mapSet, GParamSpec *pspec, gpointer data);
static void onMapChanged(VisuMap *map, gpointer data);

static void onMapAdded(GtkListStore *model, VisuMap *map)
{
  GtkTreeIter iter;
  gulong sig;

  sig = g_signal_connect_object(G_OBJECT(map), "changed",
                                G_CALLBACK(onMapChanged), (gpointer)model, 0);

  gtk_list_store_append(model, &iter);
  gtk_list_store_set(model, &iter, MAP_OBJ, map, MAP_SIG, sig, -1);
  gtk_combo_box_set_active_iter(GTK_COMBO_BOX(comboMap), &iter);

  /* We update the interface. */
  DBG_fprintf(stderr, "Panel Map: update interface.\n");
  updateInterface(FALSE);
}
static void onMapRemoved(GtkListStore *model, VisuMap *map)
{
  gboolean valid;
  GtkTreeIter iter;
  VisuMap *map_;
  gulong sig;

  DBG_fprintf(stderr, "Panel Map: %p removed.\n", (gpointer)map);
  for (valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
       valid; valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter))
    {
      /* We stop the listener on the move signal. */
      gtk_tree_model_get(GTK_TREE_MODEL(maps), &iter, MAP_OBJ, &map_, MAP_SIG, &sig, -1);
      if (map == map_)
        {
          g_signal_handler_disconnect(G_OBJECT(map), sig);
          g_object_unref(G_OBJECT(map_));
          gtk_list_store_remove(model, &iter);
          return;
        }
      g_object_unref(G_OBJECT(map_));
    }
}

static void _planeColor(GtkCellLayout *layout _U_, GtkCellRenderer *cell, GtkTreeModel *model,
                        GtkTreeIter *iter, gpointer data _U_)
{
  VisuPlane *plane;
  GdkPixbuf *pix;

  gtk_tree_model_get(model, iter, VISU_UI_PANEL_PLANES_POINTER, &plane, -1);
  pix = tool_color_get_stamp(visu_plane_getColor(plane), TRUE);
  g_object_set(cell, "pixbuf", pix, NULL);
  g_object_unref(pix);
  g_object_unref(plane);
}
static void _planeDesc(GtkCellLayout *layout _U_, GtkCellRenderer *cell, GtkTreeModel *model,
                       GtkTreeIter *iter, gpointer data _U_)
{
  VisuPlane *plane;
  gchar str[256];
  float vect[3];

  gtk_tree_model_get(model, iter, VISU_UI_PANEL_PLANES_POINTER, &plane, -1);

  visu_plane_getNVectUser(plane, vect);
  sprintf(str, _("<b>norm.</b>: (%3d;%3d;%3d) <b>dist.</b>: %6.2f"),
	  (int)vect[0], (int)vect[1], (int)vect[2], visu_plane_getDistanceFromOrigin(plane));
  g_object_set(cell, "markup", str, NULL);
  g_object_unref(plane);
}
static gboolean scaleToRadio(GBinding *binding _U_, const GValue *source_value,
                             GValue *target_value, gpointer user_data)
{
  if ((guint)GPOINTER_TO_INT(user_data) != g_value_get_uint(source_value))
    return FALSE;

  g_value_set_boolean(target_value, TRUE);
  return TRUE;
}
static gboolean radioToScale(GBinding *binding _U_, const GValue *source_value,
                             GValue *target_value, gpointer user_data)
{
  if (!g_value_get_boolean(source_value))
    return FALSE;

  g_value_set_uint(target_value, GPOINTER_TO_INT(user_data));
  return TRUE;
}

static gboolean colorToCk(GBinding *binding _U_, const GValue *source_value,
                          GValue *target_value, gpointer user_data _U_)
{
  g_value_set_boolean(target_value, (g_value_get_boxed(source_value) != (gpointer)0));
  return TRUE;
}
static gboolean ckToColor(GBinding *binding _U_, const GValue *source_value,
                          GValue *target_value, gpointer user_data)
{
  ToolColor *color;

  if (g_value_get_boolean(source_value))
    {
      g_object_get(G_OBJECT(user_data), "color", &color, NULL);
      g_return_val_if_fail(color, FALSE);
      g_value_take_boxed(target_value, color);
    }
  else
    g_value_set_boxed(target_value, (gconstpointer)0);
  return TRUE;
}
static gboolean colorToCombo(GBinding *binding _U_, const GValue *source_value,
                             GValue *target_value, gpointer user_data _U_)
{
  if (!g_value_get_boxed(source_value))
    return FALSE;

  g_value_set_boxed(target_value, g_value_get_boxed(source_value));
  return TRUE;
}
static gboolean comboToColor(GBinding *binding, const GValue *source_value,
                             GValue *target_value, gpointer user_data _U_)
{
  ToolColor *color = (ToolColor*)0;

  g_object_get(g_binding_get_source(binding), "line-color", &color, NULL);
  if (!color)
    return FALSE;
  g_boxed_free(TOOL_TYPE_COLOR, color);

  g_value_set_boxed(target_value, g_value_get_boxed(source_value));
  return TRUE;
}

static gboolean activeToVisible(GBinding *binding _U_, const GValue *source_value,
                                GValue *target_value, gpointer user_data _U_)
{
  g_value_set_boolean(target_value, g_value_get_int(source_value) < 0);
  return TRUE;
}

static void displayPlane(GtkCellLayout *cell _U_, GtkCellRenderer *renderer,
                         GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
{
  VisuMap *map;
  VisuPlane *plane;
  gchar str[256];
  float vect[3], dist;

  gtk_tree_model_get(model, iter, MAP_OBJ, &map, -1);
  plane = visu_gl_ext_map_set_getPlane(VISU_GL_EXT_MAP_SET(data), map);
  g_object_unref(map);

  if (plane)
    {
      visu_plane_getNVectUser(plane, vect);
      dist = visu_plane_getDistanceFromOrigin(plane);
      sprintf(str, LABEL_PLANE, (int)vect[0], (int)vect[1], (int)vect[2], dist);
      g_object_set(renderer, "markup", str, NULL);
    }
  else
    g_object_set(renderer, "markup", _("<i>not plane based</i>"), NULL);
}

static void createGtkInterface(VisuUiPanel *panel)
{
  GtkWidget *vbox, *hbox, *label, *combo, *wd;
  GtkListStore *list;
  GtkCellRenderer *renderer;
  ToolColor *color;
  float black[4] = {0.f, 0.f, 0.f, 1.f};
  int pos;
  VisuGlExtMapsIter iter;
  VisuGlNodeScene *scene;
  VisuGlExtMapSet *extMaps;

  scene = visu_ui_rendering_window_getGlScene(visu_ui_main_class_getDefaultRendering());
  extMaps = visu_gl_node_scene_addMaps(scene, (VisuGlExtShade**)0);

  /* We create the list of maps. */
  maps = gtk_list_store_new(MAP_N_COLUMNS,
                            VISU_TYPE_MAP,
                            G_TYPE_ULONG);
  g_signal_connect_object(extMaps, "added", G_CALLBACK(onMapAdded),
                          maps, G_CONNECT_SWAPPED);
  g_signal_connect_object(extMaps, "removed", G_CALLBACK(onMapRemoved),
                          maps, G_CONNECT_SWAPPED);
  g_signal_connect_swapped(panel, "destroy", G_CALLBACK(g_object_unref), maps);

  vbox = gtk_vbox_new(FALSE, 0);
  gtk_widget_set_margin_start(vbox, 5);
  gtk_widget_set_margin_end(vbox, 5);
  gtk_container_add(GTK_CONTAINER(panel), vbox);
  
  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
  label = gtk_label_new(_("<b>Map sources</b>"));
  gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
  gtk_widget_set_name(label, "label_head");
  gtk_label_set_xalign(GTK_LABEL(label), 0.);
  gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
  warnLabel = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_end(GTK_BOX(hbox), warnLabel, FALSE, FALSE, 0);
  wd = gtk_image_new_from_icon_name("dialog-warning",
                                    GTK_ICON_SIZE_BUTTON);
  gtk_box_pack_start(GTK_BOX(warnLabel), wd, FALSE, FALSE, 0);
  label = gtk_label_new(_("<span size=\"smaller\">missing elements</span>"));
  gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
  gtk_box_pack_start(GTK_BOX(warnLabel), label, FALSE, FALSE, 0);
  
  /* The plane selector. */
  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
  label = gtk_label_new(_("Cutting plane:"));
  gtk_widget_set_margin_start(label, 5);
  gtk_label_set_xalign(GTK_LABEL(label), 0.);
  gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
  label = gtk_label_new(_("<span size='small'>"
			  "<i>Create elements in the 'planes' tab</i></span>"));
  gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
  gtk_widget_set_margin_start(label, 5);
  gtk_label_set_xalign(GTK_LABEL(label), 1.);
  gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 0);
  
  hbox = gtk_hbox_new(FALSE, 5);
  gtk_widget_set_margin_bottom(hbox, 15);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
  list = visu_ui_panel_planes_getList();
  g_signal_connect_object(G_OBJECT(list), "row-deleted",
                          G_CALLBACK(onPlaneDeleted), extMaps, 0);
  combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(list));
  renderer = gtk_cell_renderer_pixbuf_new();
  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, FALSE);
  g_object_set(G_OBJECT(renderer), "xpad", 10, NULL);
  gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(combo), renderer, _planeColor,
                                     (gpointer)0, (GDestroyNotify)0);
  renderer = gtk_cell_renderer_text_new();
  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, FALSE);
  g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
  gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(combo), renderer, _planeDesc,
                                     (gpointer)0, (GDestroyNotify)0);
  gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
  comboVisuPlane = combo;
  g_signal_connect_object(G_OBJECT(combo), "changed",
                          G_CALLBACK(onPlaneChanged), extMaps, 0);
  wd = gtk_image_new_from_icon_name("dialog-warning",
                                    GTK_ICON_SIZE_BUTTON);
  gtk_widget_set_no_show_all(wd, TRUE);
  g_object_bind_property_full(comboVisuPlane, "active", wd, "visible",
                              G_BINDING_SYNC_CREATE, activeToVisible,
                              (GBindingTransformFunc)0,
                              (gpointer)0, (GDestroyNotify)0);
  gtk_box_pack_end(GTK_BOX(hbox), wd, FALSE, FALSE, 0);

  /* The scalar field selector. */
  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
  label = gtk_label_new(_("Scalar field:"));
  gtk_widget_set_margin_start(label, 5);
  gtk_label_set_xalign(GTK_LABEL(label), 0.);
  gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
  label = gtk_label_new(_("<span size='small'>"
			  "<i>Import fields in the 'isosurfaces' tab</i></span>"));
  gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
  gtk_widget_set_margin_start(label, 5);
  gtk_label_set_xalign(GTK_LABEL(label), 1.);
  gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 0);
  
  hbox = gtk_hbox_new(FALSE, 5);
  gtk_widget_set_margin_bottom(hbox, 15);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
  list = visu_ui_panel_surfaces_getFields();
  combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(list));
  renderer = gtk_cell_renderer_text_new();
  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, FALSE);
  g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
  gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(combo), renderer,
                                "markup", VISU_UI_SURFACES_FIELD_LABEL);
  gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
  comboField = combo;
  g_signal_connect_object(G_OBJECT(combo), "changed", G_CALLBACK(onFieldChanged),
                          extMaps, 0);
  g_signal_connect_object(extMaps, "notify::field",
                          G_CALLBACK(onSourceField), (gpointer)combo, 0);
  wd = gtk_image_new_from_icon_name("dialog-warning",
                                    GTK_ICON_SIZE_BUTTON);
  gtk_widget_set_no_show_all(wd, TRUE);
  g_object_bind_property_full(comboVisuPlane, "active", wd, "visible",
                              G_BINDING_SYNC_CREATE, activeToVisible,
                              (GBindingTransformFunc)0,
                              (gpointer)0, (GDestroyNotify)0);
  gtk_box_pack_end(GTK_BOX(hbox), wd, FALSE, FALSE, 0);

  /* The shade selector. */
  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
  label = gtk_label_new(_("Shade:"));
  gtk_widget_set_margin_start(label, 5);
  gtk_label_set_xalign(GTK_LABEL(label), 0.);
  gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
  wd = gtk_check_button_new_with_mnemonic(_("with _transparency"));
  g_object_bind_property(extMaps, "transparent", wd, "active",
                         G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
  gtk_box_pack_end(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  
  combo = visu_ui_shade_combobox_new(TRUE, TRUE);
  gtk_combo_box_set_active(GTK_COMBO_BOX(combo), -1);
  gtk_widget_set_margin_bottom(combo, 15);
  gtk_box_pack_start(GTK_BOX(vbox), combo, FALSE, FALSE, 2);
  g_object_bind_property(extMaps, "shade", combo, "shade",
                         G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
  g_signal_connect(G_OBJECT(combo), "shade-selected",
		   G_CALLBACK(onToolShadeChanged), (gpointer)0);

  /* ToolOptions. */
  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);

  label = gtk_label_new(_("<b>Options</b>"));
  gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
  gtk_widget_set_name(label, "label_head");
  gtk_label_set_xalign(GTK_LABEL(label), 0.);
  gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
  
  label = gtk_label_new("%");
  gtk_widget_set_margin_start(label, 5);
  gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 0);
  wd = gtk_spin_button_new_with_range(0, 200, 5);
  gtk_entry_set_width_chars(GTK_ENTRY(wd), 3);
  gtk_spin_button_set_digits(GTK_SPIN_BUTTON(wd), 0);
  g_object_bind_property(extMaps, "precision", wd, "value",
                         G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
  gtk_box_pack_end(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  label = gtk_label_new(_("Precision:"));
  gtk_label_set_xalign(GTK_LABEL(label), 0.);
  gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 5);

  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);

  label = gtk_label_new(_("Scale:"));
  gtk_label_set_xalign(GTK_LABEL(label), 0.);
  gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 5);

  rdLinear = gtk_radio_button_new_with_mnemonic((GSList*)0, _("_linear"));
  g_object_bind_property_full
    (extMaps, "scale",
     rdLinear, "active", G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
     scaleToRadio, radioToScale, GINT_TO_POINTER(TOOL_MATRIX_SCALING_LINEAR),
     (GDestroyNotify)0);
  gtk_box_pack_start(GTK_BOX(hbox), rdLinear, FALSE, FALSE, 0);
  rdLog = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(rdLinear),
							 _("lo_g."));
  g_object_bind_property_full
    (extMaps, "scale",
     rdLog, "active", G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
     scaleToRadio, radioToScale, GINT_TO_POINTER(TOOL_MATRIX_SCALING_LOG),
     (GDestroyNotify)0);
  gtk_box_pack_start(GTK_BOX(hbox), rdLog, FALSE, FALSE, 0);
  rdZero = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(rdLinear),
							  _("_zero centred log."));
  g_object_bind_property_full
    (extMaps, "scale",
     rdZero, "active", G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
     scaleToRadio, radioToScale, GINT_TO_POINTER(TOOL_MATRIX_SCALING_ZERO_CENTRED_LOG),
     (GDestroyNotify)0);
  gtk_box_pack_start(GTK_BOX(hbox), rdZero, FALSE, FALSE, 0);

  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);

  label = gtk_label_new(_("Number of isolines:"));
  gtk_label_set_xalign(GTK_LABEL(label), 0.);
  gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
  wd = gtk_spin_button_new_with_range(0, 20, 1);
  gtk_spin_button_set_digits(GTK_SPIN_BUTTON(wd), 0);
  gtk_entry_set_width_chars(GTK_ENTRY(wd), 2);
  g_object_bind_property(extMaps, "n-lines",
                         wd, "value", G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);

  ckColour = gtk_check_button_new_with_mnemonic(_("_colour:"));
  gtk_box_pack_start(GTK_BOX(hbox), ckColour, TRUE, TRUE, 0);

  cbColour = visu_ui_color_combobox_new(FALSE);
  visu_ui_color_combobox_setPrintValues(VISU_UI_COLOR_COMBOBOX(cbColour), FALSE);
  color = tool_color_addFloatRGBA(black, &pos);
  visu_ui_color_combobox_setSelection(VISU_UI_COLOR_COMBOBOX(cbColour), color);
  g_object_bind_property_full
    (extMaps, "line-color",
     cbColour, "color", G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
     colorToCombo, comboToColor, (gpointer)0, (GDestroyNotify)0);
  g_object_bind_property_full
    (extMaps, "line-color",
     ckColour, "active", G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
     colorToCk, ckToColor, (gpointer)cbColour, (GDestroyNotify)0);
  gtk_box_pack_start(GTK_BOX(hbox), cbColour, FALSE, FALSE, 5);

  /* The normalisation. */
  hbox = gtk_hbox_new (FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);

  label = gtk_label_new(_("Normalise:"));
  gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);

  radioNormalized = gtk_radio_button_new_with_mnemonic(NULL, _("auto"));
  g_object_bind_property(extMaps, "use-manual-range",
                         radioNormalized, "active",
                         G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL | G_BINDING_INVERT_BOOLEAN);
  gtk_box_pack_start(GTK_BOX(hbox), radioNormalized, FALSE, FALSE, 0);

  radioMinMax = gtk_radio_button_new_with_mnemonic_from_widget
    (GTK_RADIO_BUTTON(radioNormalized), _("manual"));
  g_object_bind_property(extMaps, "use-manual-range",
                         radioMinMax, "active",
                         G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
  gtk_box_pack_start(GTK_BOX(hbox), radioMinMax, FALSE, FALSE, 0);

  wd = gtk_hbox_new (FALSE, 0);
  g_object_bind_property(radioMinMax, "active", wd, "sensitive",
                         G_BINDING_SYNC_CREATE);
  gtk_box_pack_end(GTK_BOX(hbox), wd, FALSE, FALSE, 0);

  label = gtk_label_new("[");
  gtk_label_set_xalign(GTK_LABEL(label), 1);
  gtk_box_pack_start(GTK_BOX(wd), label, TRUE, TRUE, 0);
  entryDataMin = visu_ui_numerical_entry_new(-1.);
  gtk_entry_set_width_chars(GTK_ENTRY(entryDataMin), 5);
  gtk_box_pack_start(GTK_BOX(wd), entryDataMin, FALSE, FALSE, 0);
  g_object_bind_property(extMaps, "manual-range-min",
                         entryDataMin, "value", G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);

  label = gtk_label_new(";");
  gtk_label_set_xalign(GTK_LABEL(label), 1);
  gtk_box_pack_start(GTK_BOX(wd), label, FALSE, FALSE, 0);
  entryDataMax = visu_ui_numerical_entry_new(1.);
  gtk_entry_set_width_chars(GTK_ENTRY(entryDataMax), 5);
  gtk_box_pack_start(GTK_BOX(wd), entryDataMax, FALSE, FALSE, 0);
  g_object_bind_property(extMaps, "manual-range-max",
                         entryDataMax, "value", G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);

  label = gtk_label_new("]");
  gtk_box_pack_start(GTK_BOX(wd), label, FALSE, FALSE, 0);

  /* The action buttons. */
  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);

  wd = gtk_label_new(_("List of maps:"));
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 5);

  comboMap = gtk_combo_box_new_with_model(GTK_TREE_MODEL(maps));
  gtk_widget_set_sensitive(comboMap, FALSE);
  renderer = gtk_cell_renderer_text_new();
  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(comboMap), renderer, FALSE);
  gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(comboMap), renderer, displayPlane,
                                     extMaps, (GDestroyNotify)0);
  gtk_box_pack_start(GTK_BOX(hbox), comboMap, TRUE, FALSE, 0);
  comboMap_signal = g_signal_connect_object(G_OBJECT(comboMap), "changed",
                                            G_CALLBACK(onComboMapChanged), extMaps, 0);

  buildButton = gtk_button_new();
  wd = gtk_image_new_from_icon_name("list-add", GTK_ICON_SIZE_MENU);
  gtk_container_add(GTK_CONTAINER(buildButton), wd);
  gtk_box_pack_end(GTK_BOX(hbox), buildButton, FALSE, FALSE, 2);
  g_signal_connect_object(G_OBJECT(buildButton), "clicked",
                          G_CALLBACK(onBuildClicked), extMaps, 0);

  removeButton = gtk_button_new();
  wd = gtk_image_new_from_icon_name("list-remove", GTK_ICON_SIZE_MENU);
  gtk_container_add(GTK_CONTAINER(removeButton), wd);
  gtk_widget_set_sensitive(removeButton, FALSE);
  gtk_box_pack_end(GTK_BOX(hbox), removeButton, FALSE, FALSE, 2);
  g_signal_connect_object(G_OBJECT(removeButton), "clicked",
                          G_CALLBACK(onRemoveClicked), extMaps, 0);

  exportButton = gtk_button_new();
  wd = gtk_image_new_from_icon_name("document-save-as", GTK_ICON_SIZE_MENU);
  gtk_container_add(GTK_CONTAINER(exportButton), wd);
  gtk_widget_set_sensitive(exportButton, FALSE);
  gtk_box_pack_end(GTK_BOX(hbox), exportButton, FALSE, FALSE, 2);
  g_signal_connect_object(G_OBJECT(exportButton), "clicked",
                          G_CALLBACK(onExportClicked), extMaps, 0);

  gtk_widget_show_all(vbox);
  gtk_widget_hide(warnLabel);

  isMapInitialised = TRUE;

  for (visu_gl_ext_maps_iter_new(VISU_GL_EXT_MAPS(extMaps), &iter);
       iter.valid; visu_gl_ext_maps_iter_next(&iter))
    onMapAdded(maps, iter.map);
  onSourceField(extMaps, (GParamSpec*)0, comboField);
}

static void onMapChanged(VisuMap *map, gpointer data)
{
  gboolean valid;
  GtkTreeIter iter;
  VisuMap *map_;
  GtkTreePath *path;

  for (valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(data), &iter);
       valid; valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(data), &iter))
    {
      gtk_tree_model_get(GTK_TREE_MODEL(data), &iter, MAP_OBJ, &map_, -1);
      g_object_unref(G_OBJECT(map_));
      if (map == map_)
        {
          path = gtk_tree_model_get_path(GTK_TREE_MODEL(data), &iter);
          gtk_tree_model_row_changed(GTK_TREE_MODEL(data), path, &iter);
          gtk_tree_path_free(path);
          return;
        }
    }
}
static void onBuildClicked(GtkButton *button _U_, gpointer data)
{
  GtkTreeModel *model;
  GtkTreeIter iter;
  gboolean valid;
  VisuPlane *plane;

  /* We get the plane to listen to its signals. */
  model = gtk_combo_box_get_model(GTK_COMBO_BOX(comboVisuPlane));
  valid = gtk_combo_box_get_active_iter(GTK_COMBO_BOX(comboVisuPlane), &iter);
  if (!model || !valid)
    return;

  gtk_tree_model_get(model, &iter, VISU_UI_PANEL_PLANES_POINTER, &plane, -1);
  visu_gl_ext_map_set_addFromPlane(VISU_GL_EXT_MAP_SET(data), plane);
  g_object_unref(plane);
}

static void onToolShadeChanged(VisuUiShadeCombobox *combo _U_, ToolShade *shade _U_, gpointer data _U_)
{
  gtk_widget_hide(warnLabel);
}
static void onFieldChanged(GtkComboBox *combo, gpointer data)
{
  GtkTreeIter iter;
  VisuScalarField *field;

  gtk_widget_hide(warnLabel);

  if (!gtk_combo_box_get_active_iter(combo, &iter))
    return;

  field = visu_ui_panel_surfaces_fieldsAt(gtk_combo_box_get_model(combo), &iter);
  visu_gl_ext_map_set_setField(VISU_GL_EXT_MAP_SET(data), field);
  g_object_unref(field);
}
static void onSourceField(VisuGlExtMapSet *mapSet, GParamSpec *pspec _U_, gpointer data)
{
  GtkTreeModel *model;
  GtkTreeIter iter;
  gboolean valid;
  VisuScalarField *field, *field_;

  model = gtk_combo_box_get_model(GTK_COMBO_BOX(data));
  if (!model)
    return;

  g_object_get(mapSet, "field", &field, NULL);
  if (!field)
    return;
  
  for (valid = gtk_tree_model_get_iter_first(model, &iter);
       valid; valid = gtk_tree_model_iter_next(model, &iter))
    {
      field_ = visu_ui_panel_surfaces_fieldsAt(model, &iter);
      g_object_unref(field_);
      if (field == field_)
        {
          gtk_combo_box_set_active_iter(GTK_COMBO_BOX(data), &iter);
          break;
        }
    }
  g_object_unref(field);
}

static void onPlaneChanged(GtkComboBox *combo, gpointer data)
{
  GtkTreeIter iter;
  VisuPlane *plane;
  VisuMap *map;

  gtk_widget_hide(warnLabel);

  if (!gtk_combo_box_get_active_iter(combo, &iter))
    return;
  gtk_tree_model_get(gtk_combo_box_get_model(combo), &iter,
                     VISU_UI_PANEL_PLANES_POINTER, &plane, -1);

  if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(comboMap), &iter))
    {
      g_object_unref(plane);
      return;
    }
  gtk_tree_model_get(GTK_TREE_MODEL(maps), &iter, MAP_OBJ, &map, -1);  
  visu_gl_ext_map_set_setPlane(VISU_GL_EXT_MAP_SET(data), map, plane);
  g_object_unref(plane);
  g_object_unref(map);
}

static void onComboMapChanged(GtkComboBox *combo, gpointer data)
{
  GtkTreeIter iter;
  gboolean valid;
  VisuMap *map;
  VisuPlane *plane, *tmpVisuPlane;
  GtkTreeModel *model;

  DBG_fprintf(stderr, "Panel Map: set map list to %d.\n",
	      gtk_combo_box_get_active(combo));
  valid = gtk_combo_box_get_active_iter(combo, &iter);
  if (!valid)
    return;

  gtk_tree_model_get(GTK_TREE_MODEL(maps), &iter, MAP_OBJ, &map, -1);
  plane = visu_gl_ext_map_set_getPlane(VISU_GL_EXT_MAP_SET(data), map);
  g_object_unref(map);

  visu_gl_ext_setActive(VISU_GL_EXT(data), FALSE);
  model = gtk_combo_box_get_model(GTK_COMBO_BOX(comboVisuPlane));
  for (valid = gtk_tree_model_get_iter_first(model, &iter);
       valid; valid = gtk_tree_model_iter_next(model, &iter))
    {
      gtk_tree_model_get(model, &iter, VISU_UI_PANEL_PLANES_POINTER, &tmpVisuPlane, -1);
      g_object_unref(G_OBJECT(tmpVisuPlane));
      if (plane == tmpVisuPlane)
        {
          gtk_combo_box_set_active_iter(GTK_COMBO_BOX(comboVisuPlane), &iter);
          break;
        }
    }
  visu_gl_ext_setActive(VISU_GL_EXT(data), TRUE);
}

static void onPlaneDeleted(GtkTreeModel *tree_model, GtkTreePath  *path _U_,
			   gpointer user_data)
{
  gboolean valid, valid2, found;
  GtkTreeIter iterMap, iterVisuPlane;
  VisuMap *map;
  VisuPlane *plane, *tmpVisuPlane;

  DBG_fprintf(stderr, "Panel Map: plane deleted.\n");

  /* We try to find the plane in maps that is not existing anymore
     in planes. */
  for (valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(maps), &iterMap);
       valid; valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(maps), &iterMap))
    {
      gtk_tree_model_get(GTK_TREE_MODEL(maps), &iterMap, MAP_OBJ, &map, -1);
      tmpVisuPlane = visu_gl_ext_map_set_getPlane(VISU_GL_EXT_MAP_SET(user_data), map);
      found = FALSE;
      for (valid2 = gtk_tree_model_get_iter_first(tree_model, &iterVisuPlane);
	   valid2 && !found && tmpVisuPlane;
	   valid2 = gtk_tree_model_iter_next(tree_model, &iterVisuPlane))
	{
	  gtk_tree_model_get(tree_model, &iterVisuPlane,
			     VISU_UI_PANEL_PLANES_POINTER, &plane, -1);
	  g_object_unref(G_OBJECT(plane));
	  
	  found = found || (plane == tmpVisuPlane);
	}
      if (!found && tmpVisuPlane)
	{
          DBG_fprintf(stderr, "Panel Map: removing map %p after plane %p deletion.\n",
                      (gpointer)map, (gpointer)plane);
          visu_gl_ext_maps_remove(VISU_GL_EXT_MAPS(user_data), map);
          g_object_unref(G_OBJECT(map));
	  updateInterface(TRUE);
	  break;
	}
      g_object_unref(G_OBJECT(map));
    }
}

static void onRemoveClicked(GtkButton *button _U_, gpointer data)
{
  gboolean valid;
  GtkTreeIter iter;
  VisuMap *map;

  /* We get the current map, if no, we do nothing. */
  valid = gtk_combo_box_get_active_iter(GTK_COMBO_BOX(comboMap), &iter);
  if (!valid)
    return;
  
  gtk_tree_model_get(GTK_TREE_MODEL(maps), &iter, MAP_OBJ, &map, -1);
  visu_gl_ext_maps_remove(VISU_GL_EXT_MAPS(data), map);
  g_object_unref(map);

  updateInterface(TRUE);
}

static void updateInterface(gboolean selectFirst)
{
  gboolean valid;

  /* Update the interface. */
  valid = (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(maps), (GtkTreeIter*)0) > 0);
  DBG_fprintf(stderr, "Panel Map: status of maps %d.\n", valid);

  gtk_widget_set_sensitive(removeButton, valid);
  gtk_widget_set_sensitive(exportButton, valid);
  gtk_widget_set_sensitive(comboMap, valid);
  if (valid && selectFirst)
    gtk_combo_box_set_active(GTK_COMBO_BOX(comboMap), 0);
}

static void onExportClicked(GtkButton *button _U_, gpointer data)
{
  GtkWidget *dialog;
  gchar *name, *filename;
  const gchar *directory;
  GtkFileFilter *filterPDF, *filterSVG, *filter;
  VisuData *dataObj;
  VisuMapExportFormat format;
  gboolean valid;
  GError *error;
  GtkTreeIter iter;
  VisuMap *map;

  dialog = gtk_file_chooser_dialog_new
    (_("Export to SVG or PDF."),
     visu_ui_panel_getContainerWindow(VISU_UI_PANEL(panelMap)),
     GTK_FILE_CHOOSER_ACTION_SAVE,
     TOOL_ICON_CANCEL, GTK_RESPONSE_CANCEL,
     TOOL_ICON_SAVE, GTK_RESPONSE_ACCEPT,
     NULL);
#if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION > 7
  gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER (dialog), TRUE);
#endif

  filterPDF = filter = gtk_file_filter_new();
  gtk_file_filter_set_name(filter, _("PDF document (*.pdf)"));
  gtk_file_filter_add_pattern(filter, "*.pdf");
  gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
  filterSVG = filter = gtk_file_filter_new();
  gtk_file_filter_set_name(filter, _("SVG document (*.svg)"));
  gtk_file_filter_add_pattern(filter, "*.svg");
  gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);

  /* We set the suggested filename, either a previous one
     or the one given. */
  dataObj = visu_ui_panel_getData(VISU_UI_PANEL(panelMap));
  directory = visu_ui_main_getLastOpenDirectory(visu_ui_main_class_getCurrentPanel());
  if (directory)
    gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), directory);
  filename = (gchar*)0;
  if (dataObj)
    filename = g_object_get_data(G_OBJECT(dataObj), "exportMap_filename");
  if (!filename)
    filename = _("map.pdf");
  gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), filename);

  if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
    {
      filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
      name = g_path_get_basename(filename);
      g_object_set_data_full(G_OBJECT(dataObj), "exportMap_filename",
			     (gpointer)name, g_free);
      
      /* We detect the format. */
      filter = gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(dialog));
      if (filter == filterPDF)
	format = VISU_MAP_EXPORT_PDF;
      else if (filter == filterSVG)
	format = VISU_MAP_EXPORT_SVG;
      else
	format = VISU_MAP_EXPORT_PDF;
      /* We get the data. */
      valid = gtk_combo_box_get_active_iter(GTK_COMBO_BOX(comboMap), &iter);
      g_return_if_fail(valid);
      gtk_tree_model_get(GTK_TREE_MODEL(maps), &iter, MAP_OBJ, &map, -1);
      g_return_if_fail(map);
      /* We export. */
      error = (GError*)0;
      if (!visu_gl_ext_map_set_export(VISU_GL_EXT_MAP_SET(data), map,
                                      filename, format, &error) && error)
	{
	  visu_ui_raiseWarning(_("Export a coloured map"),
			       error->message,
			       GTK_WINDOW(dialog));
	  g_error_free(error);
	}
      g_object_unref(G_OBJECT(map));
      g_free(filename);
    }
  gtk_widget_destroy(dialog);
}

static void onMapEnter(VisuUiPanel *map, gpointer data _U_)
{
  DBG_fprintf(stderr, "Panel Map: caught the 'page-entered' signal %d.\n",
	      isMapInitialised);
  if (!isMapInitialised)
    createGtkInterface(map);

  if (gtk_tree_model_iter_n_children
      (gtk_combo_box_get_model(GTK_COMBO_BOX(comboVisuPlane)), (GtkTreeIter*)0) > 0 &&
      gtk_combo_box_get_active(GTK_COMBO_BOX(comboVisuPlane)) < 0)
    gtk_combo_box_set_active(GTK_COMBO_BOX(comboVisuPlane), 0);

  updateInterface(FALSE);
}
/**
 * visu_ui_panel_map_init: (skip)
 *
 * Should be used in the list declared in externalModules.h to be loaded by
 * V_Sim on start-up. This routine will create the #VisuUiPanel where
 * the coloured map stuff can be done, such as choosing a plane,
 * associating a scalar field...
 *
 * Returns: a newly created #VisuUiPanel object.
 */
VisuUiPanel* visu_ui_panel_map_init(VisuUiMain *ui _U_)
{
  /* Long description */
  char *cl = _("Map projections");
  /* Short description */
  char *tl = _("Maps");

  panelMap = visu_ui_panel_newWithIconFromPath("Panel_map", cl, tl, "stock-map_20.png");
  g_return_val_if_fail(panelMap, (VisuUiPanel*)0);

  isMapInitialised = FALSE;
  g_signal_connect(G_OBJECT(panelMap), "page-entered",
		   G_CALLBACK(onMapEnter), (gpointer)0);

  visu_ui_panel_setDockable(VISU_UI_PANEL(panelMap), TRUE);

  return VISU_UI_PANEL(panelMap);
}
