/*   EXTRAITS DE LA LICENCE
	Copyright CEA, contributeurs : Luc BILLARD et Damien
	CALISTE, laboratoire L_Sim, (2001-2005)
  
	Adresse mèl :
	BILLARD, non joignable par mèl ;
	CALISTE, damien P caliste AT cea 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 et Damien
	CALISTE, laboratoire L_Sim, (2001-2005)

	E-mail address:
	BILLARD, not reachable any more ;
	CALISTE, damien P caliste AT cea 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 <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <string.h>

#include "gtk_toolPanelWidget.h"

#include <support.h>
#include <visu_basic.h>
#include <visu_tools.h>
#include <visu_configFile.h>
#include <gtk_main.h>
#include <visu_gtk.h>
#include <gtk_renderingWindowWidget.h>

/**
 * SECTION:gtk_toolPanelWidget
 * @short_description: Defines a widget that hold a set of V_Sim panel.
 * 
 * <para>This widget is a complex association of a #GtkComboBox and a
 * #GtkNotebook with V_Sim panels. It can have its own window or be
 * attached into the main command panel.</para>
 *
 * <para>This widget also has a built-in menu to exchange V_Sim panel
 * between different instances of a #VisuUiPanel. A #VisuUiPanel is
 * refered by its name and has a position and a size. These values can
 * be stored in the parameter files.</para>
 */

enum {
  NOTEBOOK_ENTERED,
  LAST_SIGNAL
};

enum
  {
    TOOL_LIST_ICON,            /* The icon shown */
    TOOL_LIST_STOCK,           /* or the stock icon shown */
    TOOL_LIST_NAME,            /* The label shown */
    TOOL_LIST_POINTER_TO_DATA, /* Pointer to the tool panel. */
    TOOL_LIST_N_COLUMNS
  };

/**
 * VisuUiDockWindow:
 *
 * Short name for the structure of containers of #VisuUiPanel.
 */
struct _VisuUiDockWindow
{
  guint refCount;

  /* The name of the dock window. */
  gchar *name;
  /* The window holding the notebook of tools.
     Maybe NULL if the VisuUiDockWindow is integrated
     in something else.*/
  GtkWidget *window;
  /* The top container widget for the VisuUiDockWindow. */
  GtkWidget *vbox;
  /* The header line with the combobox. */
  GtkWidget *hbox;
  /* The notebook holding the tools. */
  GtkWidget *notebook;
  /* The combo list of accessible tool panels. */
  GtkWidget *combo;
  /* The list store associated to the combo. */
  GtkListStore *list;
  /* Handler on the change signals. */
  gulong notebookChanged, comboChanged;
  /* TRUE if the dock is visible. */
  gboolean show;
};

/**
 * VisuUiPanel
 *
 * Short form for a #VisuUiPanel_struct structure.
 */
struct _VisuUiPanel
{
  GtkFrame alignment;

  /* Set if the visu_ui_panel can be separate from the main
     window and be hosted in its own window. */
  gboolean dockable;

  /* An id, untranslated, used to identify the panel
     in a configuration file. It should not contain
     any space character. */
  gchar *id;

  /* Value that is shown in the combo box, which
     is a "long" label. */
  gchar *comboLabel;

  /* Value that appears in te tab of the
     GtkNoteBook */
  gchar *tabLabel;

  /* An image representing the tool. */
  GtkWidget *icon;
  gchar *stockIcon;

  /* The hosting container. */
  VisuUiDockWindow *container;

  /* Internal widgets. */
  GtkWidget *headerWidget;

  /* Memory gestion. */
  gboolean dispose_has_run;
};
/**
 * VisuUiPanelClass
 *
 * Opaque structure.
 */
struct _VisuUiPanelClass
{
  GtkFrameClass parent_class;

  void (*visu_ui_panel) (VisuUiPanel *tool);

  /* This list holds pointer on active hosting windows. */
  GList* hostingWindows;

  /* This list holds tools than have no containers. */
  GList* orphanVisuUiPanel;

  /* This is the VisuUiDockWindow of the command panel. */
  VisuUiDockWindow *commandPanel;

  /* Give a quick access from id of tool panels to pointers. */
  GHashTable *allVisuUiPanels;

  /* Pointer on current handled VisuData.
     No reference is hold. */
  VisuData *dataObj;
  VisuGlView *viewObj;

  /* If TRUE, the labels are always shown in the tabs. */
  gboolean showHeader;
};

/**
 * visu_ui_panel_get_type
 *
 * #GType are unique numbers to identify objects.
 *
 * Returns: the #GType associated with #VisuUiPanel objects.
 */
G_DEFINE_TYPE(VisuUiPanel, visu_ui_panel, GTK_TYPE_FRAME)

#define MAIN_PANEL_NAME _("Command panel")

#define FLAG_PARAMETER_TABVIEW_CONFIG   "config_subPanelTabView"
#define DESC_PARAMETER_TABVIEW_CONFIG   "See or not the labels on tabs ; boolean 0 or 1"
#define PARAMETER_CONFIG_TABVIEW_DEFAULT   FALSE
static gboolean _tabView = PARAMETER_CONFIG_TABVIEW_DEFAULT;

/* Local variables. */
static VisuUiPanelClass *local_class = NULL;
static GQuark CURRENT_TOOLPANEL_POINTER;
static guint visu_ui_panel_signals[LAST_SIGNAL] = { 0 };

/* Local methods. */
static void visu_ui_panel_class_init(VisuUiPanelClass *klass);
static void visu_ui_panel_init(VisuUiPanel *visu_ui_panel);
static void visu_ui_panel_dispose(GObject *visu_ui_panel);
static void visu_ui_panel_finalize(GObject *obj);
static GtkWidget* buildDockMenu(VisuUiPanel *visu_ui_panel, GList *listOfDocks);
static GtkWidget* buildMainMenu(VisuUiDockWindow *window);
static VisuUiDockWindow* dock_window_new(gchar *name, gboolean withWindow);
static VisuUiDockWindow* dock_window_ref(VisuUiDockWindow *dock);
static void dock_window_unref(VisuUiDockWindow *dock);
static void exportParameters(GString *data, VisuData* dataObj);

/* Local callbacks. */
static gboolean onHomePressed(GtkWidget *widget _U_, GdkEventKey *event,
			      gpointer data);
static void onDockButtonClicked(VisuUiPanel *visu_ui_panel, gpointer data);
static void onMainButtonClicked(GtkButton *button, gpointer data);
static void onRaiseButtonClicked(GtkButton *button, gpointer data);
static void onDockMenuClicked(GtkMenuItem *menuitem, gpointer user_data);
static void onDockMenuNewClicked(GtkMenuItem *menuitem, gpointer user_data);
static void onDockMenuHideClicked(GtkMenuItem *menuitem, gpointer user_data);
static void onDockMenuSelected(GtkMenuShell *menushell, gpointer user_data);
static void onMainMenuClicked(GtkMenuItem *menuitem, gpointer user_data);
static void onMainMenuShowClicked(GtkMenuItem *menuitem, gpointer user_data);
static void onMainMenuHideClicked(GtkMenuItem *menuitem, gpointer user_data);
static void onMainMenuSelected(GtkMenuShell *menushell, gpointer user_data);
static gboolean onKillVisuUiDockWindowEvent(GtkWidget *widget, GdkEvent *event, gpointer user_data);
static void onPageChanged(GtkNotebook *book, GtkWidget *child,
			  gint num, gpointer data);
static void onComboChanged(GtkComboBox *combo, gpointer data);
static void onEntryTabview(VisuConfigFile *obj, VisuConfigFileEntry *entry, VisuUiPanelClass *klass);

static void visu_ui_panel_class_init(VisuUiPanelClass *klass)
{
  DBG_fprintf(stderr, "Gtk VisuUiPanel : creating the class of the widget.\n");

  local_class  = klass;

  DBG_fprintf(stderr, "                     - adding new signals ;\n");
  /**
   * VisuUiPanel::page-entered:
   * @panel: the #VisuUiPanel that emits the signal.
   *
   * This signal is emitted when a page of the #GtkNotebook with all
   * the V_Sim panels is entered.
   *
   * Since: 3.3
   */
  visu_ui_panel_signals[NOTEBOOK_ENTERED] =
    g_signal_new ("page-entered",
		  G_TYPE_FROM_CLASS(klass),
		  G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
		  G_STRUCT_OFFSET(VisuUiPanelClass, visu_ui_panel),
		  NULL, 
		  NULL,                
		  g_cclosure_marshal_VOID__VOID,
		  G_TYPE_NONE, 0, NULL);

  DBG_fprintf(stderr, "                     - initializing the list of hosting windows.\n");
  klass->hostingWindows = (GList*)0;
  klass->orphanVisuUiPanel = (GList*)0;
  klass->commandPanel = (VisuUiDockWindow*)0;
  klass->dataObj = (VisuData*)0;
  klass->viewObj = (VisuGlView*)0;
  klass->allVisuUiPanels = g_hash_table_new_full(g_str_hash, g_str_equal,
					       NULL, NULL);
  klass->showHeader = PARAMETER_CONFIG_TABVIEW_DEFAULT;

  visu_config_file_addBooleanEntry(VISU_CONFIG_FILE_PARAMETER,
                                   FLAG_PARAMETER_TABVIEW_CONFIG,
                                   DESC_PARAMETER_TABVIEW_CONFIG,
                                   &_tabView, FALSE);
  g_signal_connect(VISU_CONFIG_FILE_PARAMETER, "parsed::" FLAG_PARAMETER_TABVIEW_CONFIG,
                   G_CALLBACK(onEntryTabview), klass);
  visu_config_file_addExportFunction(VISU_CONFIG_FILE_PARAMETER,
                                     exportParameters);

  CURRENT_TOOLPANEL_POINTER =
    g_quark_from_static_string("VisuUiPanel_currentVisuUiPanel");

  /* Set the pixmap directory for the Glade stuff. */
  add_pixmap_directory(visu_basic_getPixmapsDir());

  /* Connect freeing methods. */
  G_OBJECT_CLASS(klass)->dispose = visu_ui_panel_dispose;
  G_OBJECT_CLASS(klass)->finalize = visu_ui_panel_finalize;
}

/* This method can be called several times.
   It should unref all of its reference to
   GObjects. */
static void visu_ui_panel_dispose(GObject *visu_ui_panel)
{
  DBG_fprintf(stderr, "Gtk VisuUiPanel : dispose object %p (%s).\n",
	      (gpointer)visu_ui_panel, VISU_UI_PANEL(visu_ui_panel)->id);

  if (VISU_UI_PANEL(visu_ui_panel)->dispose_has_run)
    return;

  VISU_UI_PANEL(visu_ui_panel)->dispose_has_run = TRUE;
  /* Chain up to the parent class */
  G_OBJECT_CLASS(visu_ui_panel_parent_class)->dispose(visu_ui_panel);
}
/* This method is called once only. */
static void visu_ui_panel_finalize(GObject *obj)
{
  VisuUiPanel *visu_ui_panel;

  g_return_if_fail(obj);

  DBG_fprintf(stderr, "Gtk VisuUiPanel : finalize object %p.\n", (gpointer)obj);

  visu_ui_panel = VISU_UI_PANEL(obj);

  /* Remove me from the list of visu_ui_panels. */
  g_hash_table_remove(local_class->allVisuUiPanels, visu_ui_panel->id);

  if (visu_ui_panel->comboLabel)
    g_free(visu_ui_panel->comboLabel);
  if (visu_ui_panel->tabLabel)
    g_free(visu_ui_panel->tabLabel);
  if (visu_ui_panel->id)
    g_free(visu_ui_panel->id);
  if (visu_ui_panel->stockIcon)
    g_free(visu_ui_panel->stockIcon);
  if (visu_ui_panel->headerWidget)
    g_object_unref(visu_ui_panel->headerWidget);

  /* Chain up to the parent class */
  G_OBJECT_CLASS(visu_ui_panel_parent_class)->finalize(obj);

  DBG_fprintf(stderr, "Gtk VisuUiPanel : freeing ... OK.\n");
}


static void visu_ui_panel_init(VisuUiPanel *visu_ui_panel)
{
  DBG_fprintf(stderr, "Gtk VisuUiPanel : initializing new object (%p).\n",
	      (gpointer)visu_ui_panel);

  visu_ui_panel->dispose_has_run = FALSE;

  visu_ui_panel->dockable     = FALSE;
  visu_ui_panel->id           = (gchar*)0;
  visu_ui_panel->comboLabel   = (gchar*)0;
  visu_ui_panel->tabLabel     = (gchar*)0;
  visu_ui_panel->icon         = (GtkWidget*)0;
  visu_ui_panel->stockIcon    = (gchar*)0;
  visu_ui_panel->headerWidget = (GtkWidget*)0;
  visu_ui_panel->container    = (VisuUiDockWindow*)0;

  gtk_frame_set_shadow_type(GTK_FRAME(visu_ui_panel), GTK_SHADOW_NONE);
}

/**
 * visu_ui_panel_new:
 * @id: (type filename): a string without space and non internationalised ;
 * @name: a string in UTF-8 that can be internationalised ;
 * @tabName: a shorter name than @name, in UTF-8 that can be
 * internationalised.
 *
 * Create a new #VisuUiPanel with the given @id, displaying @name in the
 * combo box of a #VisuUiDockWindow and @tabName in the tab of the page
 * notebook.
 *
 * Returns: a newly created widget.
 */
GtkWidget* visu_ui_panel_new(gchar *id, gchar* name, gchar *tabName)
{
  VisuUiPanel *visu_ui_panel;

  g_return_val_if_fail(id && id[0] && !strstr(id, " "), (GtkWidget*)0);
  g_return_val_if_fail(name && name[0], (GtkWidget*)0);
  g_return_val_if_fail(tabName && tabName[0], (GtkWidget*)0);

  visu_ui_panel = VISU_UI_PANEL(g_object_new(visu_ui_panel_get_type(), NULL));

  DBG_fprintf(stderr, "Gtk VisuUiPanel : creating new object '%s' : %p.\n",
	      name, (gpointer)visu_ui_panel);

  visu_ui_panel->id         = g_strdup(id);
  visu_ui_panel->comboLabel = g_strdup(name);
  visu_ui_panel->tabLabel   = g_strdup(tabName);

  /* Add me to the list of visu_ui_panels. */
  g_hash_table_insert(local_class->allVisuUiPanels, visu_ui_panel->id,
		      (gpointer)visu_ui_panel);

  return GTK_WIDGET(visu_ui_panel);
}
/**
 * visu_ui_panel_newWithIconFromPath:
 * @id: a string without space and non internationalised ;
 * @name: a string in UTF-8 that can be internationalised ;
 * @tabName: a shorter name than @name, in UTF-8 that can be
 * internationalised ;
 * @iconPath: a path to an icon (should be 20x20).
 *
 * Create a new #VisuUiPanel with the given @id, displaying @name in the
 * combo box of a #VisuUiDockWindow and @tabName in the tab of the page
 * notebook. The displayed icon will be read from the @iconPath.
 *
 * Returns: (transfer full): a newly created widget.
 */
GtkWidget* visu_ui_panel_newWithIconFromPath(gchar *id, gchar* name, gchar *tabName,
					 const gchar* iconPath)
{
  VisuUiPanel *visu_ui_panel;

  visu_ui_panel = VISU_UI_PANEL(visu_ui_panel_new(id, name, tabName));
  if (!visu_ui_panel)
    return (GtkWidget*)0;

  /* TODO : mettre un truc qui limite la taille des images
     effichées à 20x20 par exemple en chargeant de GdkImage et en
     regardant sa taille. */
  visu_ui_panel->icon = create_pixmap((GtkWidget*)0, iconPath);
  return GTK_WIDGET(visu_ui_panel);
}
/**
 * visu_ui_panel_newWithIconFromIconName:
 * @id: a string without space and non internationalised ;
 * @name: a string in UTF-8 that can be internationalised ;
 * @tabName: a shorter name than @name, in UTF-8 that can be
 * internationalised ;
 * @icon: the name of a stock icon.
 *
 * Create a new #VisuUiPanel with the given @id, displaying @name in the
 * combo box of a #VisuUiDockWindow and @tabName in the tab of the page
 * notebook. The displayed icon will be taken from the @stock.
 *
 * Returns: (transfer full): a newly created widget.
 */
GtkWidget* visu_ui_panel_newWithIconFromIconName(gchar *id, gchar* name, gchar *tabName,
                                                 const gchar* icon)
{
  VisuUiPanel *visu_ui_panel;

  visu_ui_panel = VISU_UI_PANEL(visu_ui_panel_new(id, name, tabName));
  if (!visu_ui_panel)
    return (GtkWidget*)0;

  visu_ui_panel->stockIcon = g_strdup(icon);
  visu_ui_panel->icon = gtk_image_new_from_icon_name(icon, GTK_ICON_SIZE_MENU);
  return GTK_WIDGET(visu_ui_panel);
}
/**
 * visu_ui_panel_getHeaderWidget:
 * @visu_ui_panel: a #VisuUiPanel.
 *
 * The #VisuUiPanel should be used in a page of a #GtkNotebook. This
 * routine is used to get the widget that should be used in the
 * tab. This widget is a container with an icon and a label.
 *
 * Returns: (transfer none): a container widget that should be put in the tab of a
 * #GtkNotebook.
 */
GtkWidget* visu_ui_panel_getHeaderWidget(VisuUiPanel *visu_ui_panel)
{
  GtkWidget *label, *wd, *image;
#if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 12
  GtkTooltips *tooltips;
#endif

  g_return_val_if_fail(visu_ui_panel, (GtkWidget*)0);

  /* If the header has never been created,
     we do it now. */
  if (!visu_ui_panel->headerWidget)
    {
#if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 12
      tooltips = gtk_tooltips_new ();
#endif

      visu_ui_panel->headerWidget = gtk_hbox_new(FALSE, 0);
      wd = gtk_event_box_new();
      gtk_event_box_set_visible_window(GTK_EVENT_BOX(wd), FALSE);
      gtk_box_pack_start(GTK_BOX(visu_ui_panel->headerWidget),
			 wd, FALSE, FALSE, 0);
      gtk_widget_set_tooltip_text(wd, visu_ui_panel->comboLabel);
      if (visu_ui_panel->icon)
	gtk_container_add(GTK_CONTAINER(wd), visu_ui_panel->icon);
      else
	gtk_container_add(GTK_CONTAINER(wd),
			  gtk_image_new_from_icon_name("image-missing",
                                                       GTK_ICON_SIZE_MENU));
      label = gtk_label_new(visu_ui_panel->tabLabel);
      gtk_widget_set_margin_start(label, 2);
      gtk_box_pack_start(GTK_BOX(visu_ui_panel->headerWidget), label, FALSE, FALSE, 0);

      if (visu_ui_panel->dockable)
	{
	  wd = gtk_button_new();
          gtk_widget_set_no_show_all(wd, TRUE);
	  gtk_box_pack_start(GTK_BOX(visu_ui_panel->headerWidget), wd, TRUE, TRUE, 0);
	  gtk_button_set_relief(GTK_BUTTON(wd), GTK_RELIEF_NONE);
	  g_signal_connect_swapped(G_OBJECT(wd), "clicked",
				   G_CALLBACK(onDockButtonClicked), (gpointer)visu_ui_panel);
	  image = create_pixmap((GtkWidget*)0, "stock-menu-detach.png");
	  gtk_container_add(GTK_CONTAINER(wd), image);

	  gtk_widget_set_tooltip_text(wd,
			       _("Manage this subpanel: attach/detach"
				 " or hide it."));
          gtk_widget_show(image);
	}

      g_object_ref(G_OBJECT(visu_ui_panel->headerWidget));
      gtk_widget_show_all(visu_ui_panel->headerWidget);
      gtk_widget_hide(label);
    }

  return visu_ui_panel->headerWidget;
}
/**
 * visu_ui_panel_getLabel:
 * @visu_ui_panel: a #VisuUiPanel.
 *
 * The #VisuUiPanel has two label, a short one, used in the tab of a
 * #GtkNotebook and one longer. This routine gets the longer.
 *
 * Returns: an UTF-8 internationalised name (property of V_Sim, should
 * not be freed).
 */
const gchar* visu_ui_panel_getLabel(VisuUiPanel *visu_ui_panel)
{
  g_return_val_if_fail(visu_ui_panel, (gchar*)0);

  return visu_ui_panel->comboLabel;
}
/**
 * visu_ui_panel_getId:
 * @visu_ui_panel: a #VisuUiPanel.
 *
 * The #VisuUiPanel can be identifyed by an id (a string without space,
 * usually using ASCII characters only).
 *
 * Returns: a constant string identifying this #VisuUiPanel.
 */
const gchar* visu_ui_panel_getId(VisuUiPanel *visu_ui_panel)
{
  g_return_val_if_fail(visu_ui_panel, (gchar*)0);

  return visu_ui_panel->id;
}
/**
 * visu_ui_panel_setDockable:
 * @visu_ui_panel: a #VisuUiPanel ;
 * @value: a boolean.
 *
 * A #VisuUiPanel can be moved between different #VisuUiDockWindow or
 * not. This ability is controlled by the dockable flag. Change it
 * with this method. If @visu_ui_panel is set dockable, then, it can be
 * hidden or moved to another or a new #VisuUiDockWindow with the pop-up
 * memu that is triggered by a small button in the header widget (see
 * visu_ui_panel_getHeaderWidget()).
 */
void visu_ui_panel_setDockable(VisuUiPanel *visu_ui_panel, gboolean value)
{
  g_return_if_fail(visu_ui_panel);

  visu_ui_panel->dockable = value;
}
/**
 * visu_ui_panel_getContainer:
 * @visu_ui_panel: a #VisuUiPanel.
 *
 * Return the #VisuUiDockWindow that the given @visu_ui_panel is attached to or
 * NULL if the @visu_ui_panel is currently dettached.
 *
 * Returns: a #VisuUiDockWindow object.
 */
VisuUiDockWindow* visu_ui_panel_getContainer(VisuUiPanel *visu_ui_panel)
{
  g_return_val_if_fail(VISU_IS_UI_PANEL(visu_ui_panel), (VisuUiDockWindow*)0);

  return visu_ui_panel->container;
}
/**
 * visu_ui_panel_getContainerWindow:
 * @visu_ui_panel: a #VisuUiPanel.
 *
 * Return the #GtkWindow that the given @visu_ui_panel is rendered in or
 * NULL if the @visu_ui_panel is currently dettached.
 *
 * Returns: (transfer none): a #GtkWindow object.
 */
GtkWindow* visu_ui_panel_getContainerWindow(VisuUiPanel *visu_ui_panel)
{
  g_return_val_if_fail(VISU_IS_UI_PANEL(visu_ui_panel), (GtkWindow*)0);

  if (visu_ui_panel->container)
    {
      if (visu_ui_panel->container->window)
	return GTK_WINDOW(visu_ui_panel->container->window);
      else
	return GTK_WINDOW(visu_ui_main_class_getCurrentPanel());
    }
  else
    return (GtkWindow*)0;
}
/**
 * visu_ui_panel_setContainer:
 * @visu_ui_panel: a #VisuUiPanel ;
 * @window: a #VisuUiDockWindow.
 *
 * Change the container of a visu_ui_panel. If it is currently attached to
 * a #VisuUiDockWindow, it firstly detachs it.
 */
void visu_ui_panel_setContainer(VisuUiPanel *visu_ui_panel, VisuUiDockWindow *container)
{
  g_return_if_fail(VISU_IS_UI_PANEL(visu_ui_panel));

  if (container == visu_ui_panel->container)
    return;

  /* If the panel is already attached, we detach it. */
  if (visu_ui_panel->container)
    {
      DBG_fprintf(stderr, "Gtk VisuUiPanel : detaching panel '%s' (%p).\n",
		  visu_ui_panel->tabLabel, (gpointer)visu_ui_panel);
      g_object_ref(G_OBJECT(visu_ui_panel));
      visu_ui_panel_detach(visu_ui_panel);
    }

  /* If a new container is given, we attach it. */
  if (container)
    {
      DBG_fprintf(stderr, "Gtk VisuUiPanel : attaching panel '%s' (%p) to %p.\n",
		  visu_ui_panel->tabLabel, (gpointer)visu_ui_panel, (gpointer)container);
      visu_ui_panel_attach(visu_ui_panel, container);
      g_object_unref(G_OBJECT(visu_ui_panel));
    }
}
/**
 * visu_ui_panel_getContainerId:
 * @visu_ui_panel: a #VisuUiPanel.
 *
 * Return the identifying string of the #VisuUiDockWindow that the given
 * @visu_ui_panel is attached to or NULL if the @visu_ui_panel is currently dettached.
 *
 * Returns: a string owned by V_Sim.
 */
const gchar* visu_ui_panel_getContainerId(VisuUiPanel *visu_ui_panel)
{
  g_return_val_if_fail(VISU_IS_UI_PANEL(visu_ui_panel), (gchar*)0);

  if (!visu_ui_panel->container)
    return "None";

  if (visu_ui_panel->container == VISU_UI_PANEL_CLASS(G_OBJECT_GET_CLASS(visu_ui_panel))->commandPanel)
    return "Main";

  return visu_ui_panel->container->name;
}
/**
 * visu_ui_panel_setContainerId:
 * @visu_ui_panel: a #VisuUiPanel ;
 * @id: a #VisuUiDockWindow identifier.
 *
 * Change the container of a visu_ui_panel using the given @id. If it is
 * currently attached to a #VisuUiDockWindow, it firstly detachs it.
 */
void visu_ui_panel_setContainerId(VisuUiPanel *visu_ui_panel, const gchar *id)
{
  g_return_if_fail(VISU_IS_UI_PANEL(visu_ui_panel) && id && id[0]);

  visu_ui_panel_setContainer(visu_ui_panel, visu_ui_panel_class_getDockById(id));
}
/**
 * visu_ui_panel_getData:
 * @visu_ui_panel: a #VisuUiPanel.
 *
 * The @visu_ui_panel is supposed to work on a #VisuData, this routine can
 * be used to get it.
 *
 * Returns: (transfer none): the currently focused #VisuData (can be NULL if none).
 */
VisuData* visu_ui_panel_getData(VisuUiPanel *visu_ui_panel)
{
  g_return_val_if_fail(VISU_IS_UI_PANEL(visu_ui_panel), (VisuData*)0);
  
  return VISU_UI_PANEL_CLASS(G_OBJECT_GET_CLASS(visu_ui_panel))->dataObj;
}
/**
 * visu_ui_panel_getFocused:
 * @visu_ui_panel: a #VisuUiPanel.
 *
 * Retrieves the currently focused #VisuBoxed object in the default #VisuUiRenderingWindow.
 *
 * Since: 3.7
 *
 * Returns: (transfer none) (allow-none): the currently focused
 * #VisuBoxed (can be %NULL if none).
 */
VisuBoxed* visu_ui_panel_getFocused(VisuUiPanel *visu_ui_panel)
{
  g_return_val_if_fail(VISU_IS_UI_PANEL(visu_ui_panel), (VisuBoxed*)0);
  
  return VISU_BOXED(VISU_UI_PANEL_CLASS(G_OBJECT_GET_CLASS(visu_ui_panel))->dataObj);
}
/**
 * visu_ui_panel_getView:
 * @visu_ui_panel: a #VisuUiPanel object.
 * 
 * Convenient routine to get the current #VisuGlView, see
 * visu_ui_panel_class_setCurrent().
 *
 * Since: 3.7
 *
 * Returns: (transfer none): the current #VisuGlView, or NULL.
 */
VisuGlView* visu_ui_panel_getView(VisuUiPanel *visu_ui_panel)
{
  g_return_val_if_fail(VISU_IS_UI_PANEL(visu_ui_panel), (VisuGlView*)0);
  
  return VISU_UI_PANEL_CLASS(G_OBJECT_GET_CLASS(visu_ui_panel))->viewObj;
}
/**
 * visu_ui_panel_getVisible:
 * @visu_ui_panel: a #VisuUiPanel.
 *
 * This is used to retrieve if the @visu_ui_panel is currently realised
 * and the visualised page of the #GtkNotebook it is associated to.
 *
 * Returns: TRUE if the given @visu_ui_panel is potentialy visible to the user.
 */
gboolean visu_ui_panel_getVisible(VisuUiPanel *visu_ui_panel)
{
  g_return_val_if_fail(VISU_IS_UI_PANEL(visu_ui_panel), FALSE);

  /* If the tool panel is not shown, we return fFALSE. */
  if (!visu_ui_panel->container || !visu_ui_panel->container->show)
    return FALSE;

  /* If the tool panel is attached to a visible dock, we
     test if the current page of the notebook is our tool panel. */
  return (visu_ui_panel == VISU_UI_PANEL(gtk_notebook_get_nth_page
	  (GTK_NOTEBOOK(visu_ui_panel->container->notebook),
	   gtk_notebook_get_current_page
	   (GTK_NOTEBOOK(visu_ui_panel->container->notebook)))));
}


/*****************/
/* Menu gestion. */
/*****************/
static void onDockButtonClicked(VisuUiPanel *visu_ui_panel, gpointer data)
{
  GtkWidget *wd;

  g_return_if_fail(VISU_IS_UI_PANEL(visu_ui_panel));

  wd = buildDockMenu(visu_ui_panel, VISU_UI_PANEL_CLASS(G_OBJECT_GET_CLASS(visu_ui_panel))->hostingWindows);
  g_signal_connect(G_OBJECT(wd), "selection-done",
		   G_CALLBACK(onDockMenuSelected), (gpointer)0);

  gtk_widget_show_all(wd);
#if GTK_MAJOR_VERSION < 2 || (GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION < 22)
  gtk_menu_popup(GTK_MENU(wd), NULL, NULL, NULL, NULL, 
		 1, gtk_get_current_event_time());
  (void)data;
#else
  gtk_menu_popup_at_widget(GTK_MENU(wd), GTK_WIDGET(data), GDK_GRAVITY_SOUTH, GDK_GRAVITY_NORTH, NULL);
#endif
}

static GtkWidget* buildDockMenu(VisuUiPanel *visu_ui_panel, GList *listOfDocks)
{
  GtkWidget *menu, *item;
  VisuUiDockWindow *window;
  gchar *lbl;

  menu = gtk_menu_new();
  
  /* All dock windows. */
  while (listOfDocks)
    {
      window = (VisuUiDockWindow*)listOfDocks->data;
      if (window->show)
	{
	  lbl = g_strdup_printf(_("Send to '%s'"), window->name);
	  item = gtk_menu_item_new_with_label(lbl);
	  g_free(lbl);
	  g_signal_connect(G_OBJECT(item), "activate",
			   G_CALLBACK(onDockMenuClicked), listOfDocks->data);
	  g_object_set_qdata_full(G_OBJECT(item), CURRENT_TOOLPANEL_POINTER,
				  (gpointer)visu_ui_panel, NULL);
	  gtk_widget_set_sensitive(item, (visu_ui_panel->container != window));
	  gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
	}
      listOfDocks = g_list_next(listOfDocks);
    }
  /* Separator. */
  item = gtk_separator_menu_item_new();
  gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
  /* Create a new dock window. */
  item = gtk_menu_item_new_with_label(_("New dock"));
  g_signal_connect(G_OBJECT(item), "activate",
		   G_CALLBACK(onDockMenuNewClicked), (gpointer)visu_ui_panel);
  gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
  /* Remove the current tool panel. */
  lbl = g_strdup_printf(_("Hide tool '%s'"), visu_ui_panel->tabLabel);
  item = gtk_menu_item_new_with_label(lbl);
  g_free(lbl);
  g_signal_connect(G_OBJECT(item), "activate",
		   G_CALLBACK(onDockMenuHideClicked), (gpointer)visu_ui_panel);
  gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
  
  DBG_fprintf(stderr, "Gtk VisuUiPanel : create the dock menu %p.\n", (gpointer)menu);

  return menu;
}

static void onDockMenuClicked(GtkMenuItem *menuitem, gpointer user_data)
{
  VisuUiPanel *visu_ui_panel;

  visu_ui_panel = (VisuUiPanel*)0;
  visu_ui_panel = VISU_UI_PANEL(g_object_get_qdata(G_OBJECT(menuitem),
					    CURRENT_TOOLPANEL_POINTER));
  g_return_if_fail(VISU_IS_UI_PANEL(visu_ui_panel));

  visu_ui_panel_setContainer(visu_ui_panel, (VisuUiDockWindow*)user_data);
}
static void onDockMenuNewClicked(GtkMenuItem *menuitem _U_, gpointer user_data)
{
  GList **lst;
  VisuUiPanel *visu_ui_panel;
  VisuUiDockWindow *window;
  guint nb;
  gchar *name;

  lst = &local_class->hostingWindows;
  nb = g_list_length(*lst);
  name = g_strdup_printf(_("Dock window (%d)"), nb);
  window = dock_window_new(name, TRUE);
  *lst = g_list_append(*lst, (gpointer)window);
  window->show = TRUE;
  gtk_widget_show_all(window->window);

  visu_ui_panel = VISU_UI_PANEL(user_data);
  if (visu_ui_panel)
    visu_ui_panel_setContainer(visu_ui_panel, window);
}
static void onDockMenuHideClicked(GtkMenuItem *menuitem _U_, gpointer user_data)
{
  DBG_fprintf(stderr, "Gtk VisuUiPanel : hide the panel %p.\n", (gpointer)user_data);
  visu_ui_panel_setContainer(VISU_UI_PANEL(user_data), (VisuUiDockWindow*)0);
}

static void onDockMenuSelected(GtkMenuShell *menushell, gpointer user_data _U_)
{
  DBG_fprintf(stderr, "Gtk VisuUiPanel : destroy the dock menu %p.\n", (gpointer)menushell);
  gtk_widget_destroy(GTK_WIDGET(menushell));
}

static gboolean emitPageEnter(gpointer data)
{
  g_return_val_if_fail(VISU_IS_UI_PANEL(data), FALSE);

  DBG_fprintf(stderr, "Gtk VisuUiPanel: '%s' (%p) emits 'page-entered' signal.\n",
	      VISU_UI_PANEL(data)->id, data);
  g_signal_emit(G_OBJECT(data), visu_ui_panel_signals[NOTEBOOK_ENTERED],
		0, NULL);
  
  return FALSE;
}

static void changeHeaderVisibility(VisuUiPanel *panel, gboolean status)
{
  GList *list, *tmpLst;
  
  g_return_if_fail(VISU_IS_UI_PANEL(panel) && panel->headerWidget);

  list = gtk_container_get_children(GTK_CONTAINER(panel->headerWidget));
  tmpLst = list->next;
  while(tmpLst)
    {
      if (status)
	gtk_widget_show(GTK_WIDGET(tmpLst->data));
      else
	gtk_widget_hide(GTK_WIDGET(tmpLst->data));
      tmpLst = g_list_next(tmpLst);
    }
  g_list_free(list);
}

static gboolean onHomePressed(GtkWidget *widget _U_, GdkEventKey *event,
			      gpointer data)
{
  DBG_fprintf(stderr, "Gtk VisuUiPanel: get key pressed.\n");
  if(event->keyval == GDK_KEY_Home)
    {
      onRaiseButtonClicked((GtkButton*)0, data);
      return TRUE;
    }
  return FALSE;
}
static void onPageChanged(GtkNotebook *book, GtkWidget *child,
			  gint num, gpointer data)
{
  VisuUiDockWindow *window;
  int numOld;
#if GTK_MAJOR_VERSION == 2
  child = gtk_notebook_get_nth_page(book, num);
#endif

  window = (VisuUiDockWindow*)data;
  g_return_if_fail(window);
  g_return_if_fail(VISU_IS_UI_PANEL(child));

  DBG_fprintf(stderr, "Gtk VisuUiPanel: caught 'page-switch' signal, jump to %d.\n", num);
  /* Change the visibility of the header labels. */
  if (!local_class->showHeader)
    {
      numOld = gtk_combo_box_get_active(GTK_COMBO_BOX(window->combo));
      if ((gint)num != numOld)
	{
	  /* We don't come from the combo so we change the
	     visibility. */
	  changeHeaderVisibility
	    (VISU_UI_PANEL(gtk_notebook_get_nth_page(book, numOld)), FALSE);
	  changeHeaderVisibility
	    (VISU_UI_PANEL(gtk_notebook_get_nth_page(book, num)), TRUE);
	}
    }
  
  /* Change the combo to the right one. */
  g_signal_handler_block(G_OBJECT(window->combo), window->comboChanged);
  gtk_combo_box_set_active(GTK_COMBO_BOX(window->combo), num);
  g_signal_handler_unblock(G_OBJECT(window->combo), window->comboChanged);

  g_idle_add(emitPageEnter, child);
}

static void onComboChanged(GtkComboBox *combo, gpointer data)
{
  int index, numOld;
  VisuUiDockWindow *window;
  GtkNotebook *book;
  
  window = (VisuUiDockWindow*)data;
  g_return_if_fail(window);

  index = gtk_combo_box_get_active(combo);

  /* Change the visibility of the header labels. */
  if (!local_class->showHeader)
    {
      book = GTK_NOTEBOOK(window->notebook);
      numOld = gtk_notebook_get_current_page(book);
      changeHeaderVisibility
	(VISU_UI_PANEL(gtk_notebook_get_nth_page(book, numOld)), FALSE);
      changeHeaderVisibility
	(VISU_UI_PANEL(gtk_notebook_get_nth_page(book, index)), TRUE);
    }

  /* Change the page to the right one. */
  gtk_notebook_set_current_page(GTK_NOTEBOOK(window->notebook), index);
}


static gboolean onKillVisuUiDockWindowEvent(GtkWidget *widget, GdkEvent *event _U_,
				      gpointer user_data)
{
  g_return_val_if_fail(user_data, TRUE);

  DBG_fprintf(stderr, "Gtk VisuUiPanel: delete or destroy event cancelled.\n");
  gtk_widget_hide(widget);
  ((VisuUiDockWindow*)user_data)->show = FALSE;
  return TRUE;
}

/**
 * visu_ui_panel_attach:
 * @visu_ui_panel: a #VisuUiPanel ;
 * @dock: a #VisuUiDockWindow.
 *
 * Put the given @visu_ui_panel in the given @dock window. It adds in this
 * dock window a new page in the #GtkNotebook using as tab header the
 * widget returned by visu_ui_panel_getHeaderWidget().
 */
void visu_ui_panel_attach(VisuUiPanel *visu_ui_panel, VisuUiDockWindow *dock)
{
  GtkTreeIter iter;
  VisuUiPanelClass *klass;
  GdkPixbuf *pixbuf;

  DBG_fprintf(stderr, "Gtk VisuUiPanel: attach a visu_ui_panel (%p) to a dock"
	      " window (%s).\n", (gpointer)visu_ui_panel, dock->name);

  g_return_if_fail(dock && visu_ui_panel);

  if (visu_ui_panel->icon &&
      gtk_image_get_storage_type(GTK_IMAGE(visu_ui_panel->icon)) == GTK_IMAGE_PIXBUF)
    pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(visu_ui_panel->icon));
  else
    pixbuf = (GdkPixbuf*)0;

  /* We add the window in the list store. */
  gtk_list_store_append(dock->list, &iter);
  gtk_list_store_set(dock->list, &iter,
		     TOOL_LIST_ICON, pixbuf,
                     TOOL_LIST_STOCK, visu_ui_panel->stockIcon,
		     TOOL_LIST_NAME, visu_ui_panel->comboLabel,
		     TOOL_LIST_POINTER_TO_DATA, visu_ui_panel,
		     -1);

  gtk_notebook_append_page(GTK_NOTEBOOK(dock->notebook), GTK_WIDGET(visu_ui_panel),
			   visu_ui_panel_getHeaderWidget(visu_ui_panel));
  gtk_widget_show(GTK_WIDGET(visu_ui_panel));
  visu_ui_panel->container = dock;

  if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(dock->notebook)) > 1)
    gtk_widget_show(dock->hbox);
  else
    gtk_widget_hide(dock->hbox);

  klass = VISU_UI_PANEL_CLASS(G_OBJECT_GET_CLASS(visu_ui_panel));
  klass->orphanVisuUiPanel = g_list_remove(klass->orphanVisuUiPanel,
					 (gpointer)visu_ui_panel);

}
/**
 * visu_ui_panel_detach:
 * @visu_ui_panel: a #VisuUiPanel.
 *
 * Remove the given @visu_ui_panel from its current container and add it
 * to the list of hidden tool panels. It can be added again using the
 * pop-up menu of any #VisuUiDockWindow.
 */
void visu_ui_panel_detach(VisuUiPanel *visu_ui_panel)
{
  gint page;
  VisuUiDockWindow *window;
  GtkTreeIter iter;
  GtkTreePath* path;
  gboolean valid;
  VisuUiPanelClass *klass;

  g_return_if_fail(visu_ui_panel && visu_ui_panel->container);

  window = visu_ui_panel->container;

  /* Get the id. */
  page = gtk_notebook_page_num(GTK_NOTEBOOK(window->notebook), GTK_WIDGET(visu_ui_panel));

  DBG_fprintf(stderr, "Gtk VisuUiPanel : detach a visu_ui_panel (%s) from page %d of dock"
	      " (%s).\n", visu_ui_panel->tabLabel, page, window->name);

  /* We remove the page for the notebook. */
  gtk_notebook_remove_page(GTK_NOTEBOOK(window->notebook), page);

  if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(window->notebook)) < 2)
    gtk_widget_hide(window->hbox);

  /* We remove the tool from the combo list. */
  path = gtk_tree_path_new_from_indices(page, -1);
  valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(window->list), &iter, path);
  if (valid)
    gtk_list_store_remove(window->list, &iter);
  gtk_tree_path_free(path);

  /* If there is no more tool in this window, we delete it. */
  if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(window->notebook)) == 0 &&
      window != VISU_UI_PANEL_CLASS(G_OBJECT_GET_CLASS(visu_ui_panel))->commandPanel)
    {
      DBG_fprintf(stderr, "Gtk VisuUiPanel: destroying dock window %p (%s).\n",
		  (gpointer)window, window->name);
      gtk_widget_destroy(window->window);
      VISU_UI_PANEL_CLASS(G_OBJECT_GET_CLASS(visu_ui_panel))->hostingWindows =
	g_list_remove(VISU_UI_PANEL_CLASS(G_OBJECT_GET_CLASS(visu_ui_panel))->hostingWindows,
		      (gconstpointer)window);
      g_free(window->name);
      g_object_unref(window->list);
      g_free(window);
    }

  visu_ui_panel->container = (VisuUiDockWindow*)0;
  klass = VISU_UI_PANEL_CLASS(G_OBJECT_GET_CLASS(visu_ui_panel));
  klass->orphanVisuUiPanel = g_list_prepend(klass->orphanVisuUiPanel,
					  (gpointer)visu_ui_panel);
  DBG_fprintf(stderr, "Gtk VisuUiPanel: number of orphan tool panels %d.\n",
	      g_list_length(klass->orphanVisuUiPanel));
}

static void onMainButtonClicked(GtkButton *button, gpointer data)
{
  GtkWidget *wd;

  wd = buildMainMenu((VisuUiDockWindow*)data);
  g_signal_connect(G_OBJECT(wd), "selection-done",
		   G_CALLBACK(onMainMenuSelected), (gpointer)0);

  gtk_widget_show_all(wd);
#if GTK_MAJOR_VERSION < 2 || (GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION < 22)
  gtk_menu_popup(GTK_MENU(wd), NULL, NULL, NULL, NULL, 
		 1, gtk_get_current_event_time());
  (void)button;
#else
  gtk_menu_popup_at_widget(GTK_MENU(wd), GTK_WIDGET(button), GDK_GRAVITY_SOUTH, GDK_GRAVITY_NORTH, NULL);
#endif
}

static GtkWidget* buildMainMenu(VisuUiDockWindow *window)
{
  GtkWidget *menu, *item;
  GList* tmpLst;
  gchar *label;
  VisuUiPanel *panel;
  VisuUiDockWindow *dock;
  gboolean haveHiddenDock;

  menu = gtk_menu_new();
  
  tmpLst = local_class->orphanVisuUiPanel;
  /* All dock windows. */
  while (tmpLst)
    {
      panel = (VisuUiPanel*)tmpLst->data;
      label = g_strdup_printf(_("Show '%s'"), panel->comboLabel);
      item = gtk_menu_item_new_with_label(label);
      g_free(label);
      g_signal_connect(G_OBJECT(item), "activate",
		       G_CALLBACK(onMainMenuClicked), window);
      g_object_set_qdata_full(G_OBJECT(item), CURRENT_TOOLPANEL_POINTER,
			      (gpointer)panel, NULL);
      gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
      tmpLst = g_list_next(tmpLst);
    }
  if (!local_class->orphanVisuUiPanel)
    {
      item = gtk_menu_item_new_with_label(_("No hidden tool"));
      gtk_widget_set_sensitive(item, FALSE);
      gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
    }
  /* Separator. */
  item = gtk_separator_menu_item_new();
  gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
  /* Hidden dock. */
  haveHiddenDock = FALSE;
  tmpLst = local_class->hostingWindows;
  while (tmpLst)
    {
      dock = (VisuUiDockWindow*)tmpLst->data;
      if (!dock->show)
	{
	  haveHiddenDock = TRUE;
	  label = g_strdup_printf(_("Show '%s'"), dock->name);
	  item = gtk_menu_item_new_with_label(label);
	  g_free(label);
	  g_signal_connect(G_OBJECT(item), "activate",
			   G_CALLBACK(onMainMenuShowClicked), dock);
	  gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
	}
      tmpLst = g_list_next(tmpLst);
    }
  if (!haveHiddenDock)
    {
      item = gtk_menu_item_new_with_label(_("No hidden dock"));
      gtk_widget_set_sensitive(item, FALSE);
      gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
    }
  if (window->window)
    {
      /* Separator. */
      item = gtk_separator_menu_item_new();
      gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
      /* Hide action */
      item = gtk_menu_item_new_with_label(_("Hide dock"));
      g_signal_connect(G_OBJECT(item), "activate",
		       G_CALLBACK(onMainMenuHideClicked), window);
      gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
    }
  
  DBG_fprintf(stderr, "Gtk VisuUiPanel: create the main menu %p.\n", (gpointer)menu);

  return menu;
}
static void onMainMenuClicked(GtkMenuItem *menuitem, gpointer user_data)
{
  VisuUiPanel *visu_ui_panel;
  VisuUiDockWindow *window;

  visu_ui_panel = (VisuUiPanel*)0;
  visu_ui_panel = VISU_UI_PANEL(g_object_get_qdata(G_OBJECT(menuitem),
					    CURRENT_TOOLPANEL_POINTER));
  g_return_if_fail(VISU_IS_UI_PANEL(visu_ui_panel));

  window = (VisuUiDockWindow*)user_data;
  
  DBG_fprintf(stderr, "Gtk VisuUiPanel: attaching panel '%s' (%p) to %p.\n",
	      visu_ui_panel->tabLabel, (gpointer)visu_ui_panel, (gpointer)window);
  visu_ui_panel_attach(visu_ui_panel, window);
  g_object_unref(G_OBJECT(visu_ui_panel));
}
static void onMainMenuShowClicked(GtkMenuItem *menuitem _U_, gpointer user_data)
{
  VisuUiDockWindow *dock;

  g_return_if_fail(user_data);
  
  dock = (VisuUiDockWindow*)user_data;
  if (dock->window)
    gtk_widget_show(dock->window);
  dock->show = TRUE;
}
static void onMainMenuHideClicked(GtkMenuItem *menuitem _U_, gpointer user_data)
{
  VisuUiDockWindow *dock;

  g_return_if_fail(user_data);
  
  dock = (VisuUiDockWindow*)user_data;
  if (dock->window)
    gtk_widget_hide(dock->window);
  dock->show = FALSE;
}
static void onMainMenuSelected(GtkMenuShell *menushell, gpointer user_data _U_)
{
  DBG_fprintf(stderr, "Gtk VisuUiPanel: destroy the main menu %p.\n", (gpointer)menushell);
  gtk_widget_destroy(GTK_WIDGET(menushell));
}
static void onRaiseButtonClicked(GtkButton *button _U_, gpointer data _U_)
{
  GtkWindow *window;

  window = visu_ui_getRenderWindow();
  g_return_if_fail(window);
  /* We raised the rendering window, if required. */
  gtk_window_present(window);
}


/******************/
/* Class methods. */
/******************/

/**
 * visu_ui_panel_class_getCommandPanel:
 *
 * There is always a #VisuUiDockWindow that is inside the command
 * panel. This routine gets it.
 *
 * Returns: (transfer none):the #VisuUiDockWindow that is inside the command panel.
 */
VisuUiDockWindow* visu_ui_panel_class_getCommandPanel(void)
{
  if (!local_class)
    g_type_class_ref(VISU_TYPE_UI_PANEL);

  if (!local_class->commandPanel)
    {
      local_class->commandPanel = dock_window_new(MAIN_PANEL_NAME, FALSE);
      local_class->hostingWindows = g_list_prepend(local_class->hostingWindows,
						   local_class->commandPanel);
    }

  return local_class->commandPanel;
}
/**
 * visu_ui_panel_class_setCurrent:
 * @dataObj: a #VisuData object.
 * @view: a #VisuGlView object.
 *
 * Set the currently focussed #VisuData. It then can be retrieve using
 * visu_ui_panel_getData().
 *
 * Since: 3.7
 */
void visu_ui_panel_class_setCurrent(VisuData *dataObj, VisuGlView *view)
{
  if (!local_class)
    g_type_class_ref(VISU_TYPE_UI_PANEL);

  local_class->dataObj = dataObj;
  local_class->viewObj = view;
}
/**
 * visu_ui_panel_class_getPanelById:
 * @id: a #VisuUiPanel identifier.
 *
 * This routine associates a #VisuUiPanel identifier to the object pointer.
 *
 * Returns: (transfer none): the #VisuUiPanel that corresponds to this identifier or NULL
 * if none exists.
 */
VisuUiPanel* visu_ui_panel_class_getPanelById(const gchar *id)
{
  gpointer panel;

  if (!local_class)
    g_type_class_ref(VISU_TYPE_UI_PANEL);

  DBG_fprintf(stderr, "Gtk VisuUiPanel: search panel '%s'.\n", id);
  panel = g_hash_table_lookup(local_class->allVisuUiPanels, id);
  if (!panel)
    return (VisuUiPanel*)0;
  else
    return VISU_UI_PANEL(panel);
}
/**
 * visu_ui_panel_class_getDockById:
 * @id: a #VisuUiDockWindow identifier.
 *
 * This routine associates a #VisuUiDockWindow identifier to the object pointer.
 *
 * Returns: the #VisuUiDockWindow that corresponds to this identifier. The
 * dock window is built if not currently exist.
 */
VisuUiDockWindow* visu_ui_panel_class_getDockById(const gchar *id)
{
  VisuUiDockWindow *window;
  GList *tmplst;

  if (!local_class)
    g_type_class_ref(VISU_TYPE_UI_PANEL);

  if (!strcmp(id, "None"))
    return (VisuUiDockWindow*)0;

  if (!strcmp(id, "Main"))
    return local_class->commandPanel;
  
  tmplst = local_class->hostingWindows;
  while (tmplst)
    {
      if (!strcmp(((VisuUiDockWindow*)tmplst->data)->name, id))
	return (VisuUiDockWindow*)tmplst->data;
      tmplst = g_list_next(tmplst);
    };
  /* No matching name found, we create a new window. */
  window = dock_window_new(g_strdup(id), TRUE);
  local_class->hostingWindows = g_list_prepend(local_class->hostingWindows,
					      (gpointer)window);

  return window;
}
/**
 * visu_ui_panel_class_getAllPanels:
 *
 * This routine can be used to know all existing #VisuUiPanel.
 *
 * Returns: (transfer full) (element-type VisuUiPanel*): a newly
 * created list (use g_list_free() on it after use).
 */
GList* visu_ui_panel_class_getAllPanels(void)
{
  GList *tmplst, *returnlst;
  gboolean valid;
  GtkTreeIter iter;
  VisuUiPanel *tool;
  GtkTreeModel *treeModel;

  if (!local_class)
    g_type_class_ref(VISU_TYPE_UI_PANEL);
  
  DBG_fprintf(stderr, "Gtk VisuUiPanel: create list of all panels.\n");
  returnlst = (GList*)0;
  tmplst = local_class->hostingWindows;
  while (tmplst)
    {
      treeModel = GTK_TREE_MODEL(((VisuUiDockWindow*)tmplst->data)->list);
      valid = gtk_tree_model_get_iter_first(treeModel, &iter);
      while (valid)
	{
	  gtk_tree_model_get(treeModel, &iter, TOOL_LIST_POINTER_TO_DATA, &tool, -1);
	  DBG_fprintf(stderr, " | add '%s'\n", tool->id);
	  returnlst = g_list_prepend(returnlst, (gpointer)tool);
	  valid = gtk_tree_model_iter_next(treeModel, &iter);
	}
      tmplst = g_list_next(tmplst);
    }
  returnlst = g_list_concat(returnlst, g_list_copy(local_class->orphanVisuUiPanel));
  return returnlst;
}
/**
 * visu_ui_panel_class_getAllWindows:
 *
 * This routine can be used to know all existing #VisuUiDockWindow.
 *
 * Returns: (transfer container) (element-type VisuUiDockWindow*): a
 * newly created list (use g_list_free() on it after use).
 */
GList* visu_ui_panel_class_getAllWindows(void)
{
  GList *returnlst;

  if (!local_class)
    g_type_class_ref(VISU_TYPE_UI_PANEL);
  
  DBG_fprintf(stderr, "Gtk VisuUiPanel : create list of all windows.\n");
  returnlst = g_list_copy(local_class->hostingWindows);
  returnlst = g_list_remove(returnlst, local_class->commandPanel);
  return returnlst;
}
/**
 * visu_ui_panel_class_setHeaderVisibility:
 * @status: a boolean.
 *
 * The header can be represented using the full image and label always
 * or only the image when the tab is not on top.
 */
void visu_ui_panel_class_setHeaderVisibility(gboolean status)
{
  GList *tmpLst;
  int num, i;
  GtkNotebook *notebook;

  if (!local_class)
    g_type_class_ref(VISU_TYPE_UI_PANEL);
  
  DBG_fprintf(stderr, "Gtk VisuUiPanel : set the header visibility.\n");
  if (local_class->showHeader == status)
    return;

  tmpLst = local_class->hostingWindows;
  while(tmpLst)
    {
      notebook = GTK_NOTEBOOK(((VisuUiDockWindow*)tmpLst->data)->notebook);
      num = gtk_notebook_get_current_page(notebook);
      for (i = 0; i < gtk_notebook_get_n_pages(notebook); i++)
	changeHeaderVisibility(VISU_UI_PANEL(gtk_notebook_get_nth_page(notebook, i)),
			       status || (i == num));
      tmpLst = g_list_next(tmpLst);
    }
  local_class->showHeader = status;
}
/**
 * visu_ui_panel_class_getHeaderVisibility:
 *
 * The header can be represented using the full image and label always
 * or only the image when the tab is not on top.
 *
 * Since: 3.8
 *
 * Returns: TRUE if the header uses text and label.
 **/
gboolean visu_ui_panel_class_getHeaderVisibility(void)
{
  if (!local_class)
    g_type_class_ref(VISU_TYPE_UI_PANEL);
  
  return local_class->showHeader;
}

/************************/
/* Dock window methods. */
/************************/
GType visu_ui_dock_window_get_type(void)
{
  static GType g_define_type_id = 0;

  if (g_define_type_id == 0)
    g_define_type_id =
      g_boxed_type_register_static("VisuUiDockWindow",
                                   (GBoxedCopyFunc)dock_window_ref,
                                   (GBoxedFreeFunc)dock_window_unref);
  return g_define_type_id;
}
static VisuUiDockWindow* dock_window_new(gchar *name, gboolean withWindow)
{
  VisuUiDockWindow *window;
  GtkWidget *wd, *image;
  GtkCellRenderer *renderer;
/*   GtkBindingSet *keys; */
#if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 12
  GtkTooltips *tooltips;

  tooltips = gtk_tooltips_new ();
#endif

  window = g_malloc(sizeof(VisuUiDockWindow));
  window->refCount = 1;

  DBG_fprintf(stderr, "Gtk VisuUiPanel: create a new dock window %p (%s).\n",
	      (gpointer)window, name);

  window->name = name;

  window->show = !strcmp(name, MAIN_PANEL_NAME);

  window->vbox = gtk_vbox_new(FALSE, 0);

  if (withWindow)
    {
      window->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
      gtk_window_set_title(GTK_WINDOW(window->window), window->name);
      gtk_window_set_default_size(GTK_WINDOW(window->window), 350, 350);
      gtk_window_set_type_hint(GTK_WINDOW(window->window), GDK_WINDOW_TYPE_HINT_UTILITY);
      gtk_window_set_skip_pager_hint(GTK_WINDOW(window->window), TRUE);
      g_signal_connect(G_OBJECT(window->window), "delete-event",
		       G_CALLBACK(onKillVisuUiDockWindowEvent), (gpointer)window);
      g_signal_connect(G_OBJECT(window->window), "destroy-event",
		       G_CALLBACK(onKillVisuUiDockWindowEvent), (gpointer)window);
      g_signal_connect(G_OBJECT(window->window), "key-press-event",
		       G_CALLBACK(onHomePressed), (gpointer)window);

      gtk_container_set_border_width(GTK_CONTAINER(window->window), 3);
      gtk_container_add(GTK_CONTAINER(window->window), window->vbox);
/*       gtk_widget_show(window->window); */
    }
  else
    window->window = (GtkWidget*)0;

  window->notebook = gtk_notebook_new();
  gtk_widget_set_margin_top(window->notebook, 5);
  gtk_notebook_set_scrollable(GTK_NOTEBOOK(window->notebook), TRUE);
  gtk_box_pack_end(GTK_BOX(window->vbox), window->notebook, TRUE, TRUE, 0);
  window->notebookChanged =
    g_signal_connect(G_OBJECT(window->notebook), "switch-page",
		     G_CALLBACK(onPageChanged), (gpointer)window);

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

  /* Define the key bindings. */
/*   keys = gtk_binding_set_new("commandPanel"); */
/*   gtk_binding_entry_add_signal(keys, GDK_KEY_space, 0, "clicked", 1, (gpointer)window); */

  /* Raise render area. */
  wd = gtk_button_new();
  g_object_set(G_OBJECT(wd), "can-default", TRUE, "can-focus", TRUE,
	       "has-default", FALSE, "has-focus", FALSE, NULL);
/*   gtk_button_set_relief(GTK_BUTTON(wd), GTK_RELIEF_NONE); */
  gtk_box_pack_start(GTK_BOX(window->hbox), wd, FALSE, FALSE, 0);
  g_signal_connect(G_OBJECT(wd), "clicked",
		   G_CALLBACK(onRaiseButtonClicked), (gpointer)window);
  image = gtk_image_new_from_icon_name("go-up", GTK_ICON_SIZE_MENU);
  gtk_container_add(GTK_CONTAINER(wd), image);
  gtk_widget_set_tooltip_text(wd, _("Raise the rendering window.\n"
				    "  Use <home> as key binding."));

  /* The Label to introduce the combo list. */
  wd = gtk_label_new(_("Tool: "));
  gtk_box_pack_start(GTK_BOX(window->hbox), wd, FALSE, FALSE, 2);

  /* We create the list store. */
  window->list = gtk_list_store_new(TOOL_LIST_N_COLUMNS,
				    GDK_TYPE_PIXBUF, G_TYPE_STRING,
				    G_TYPE_STRING, G_TYPE_POINTER);
  /* The combo list. */
  window->combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(window->list));
  renderer = gtk_cell_renderer_pixbuf_new();
  gtk_cell_renderer_set_fixed_size(renderer, 22, -1);
  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(window->combo), renderer, FALSE);
  gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(window->combo),
				renderer, "pixbuf", TOOL_LIST_ICON);
  gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(window->combo),
				renderer, "stock-id", TOOL_LIST_STOCK);
  renderer = gtk_cell_renderer_text_new();
  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(window->combo), renderer, FALSE);
  gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(window->combo),
				renderer, "text", TOOL_LIST_NAME);
  gtk_box_pack_start(GTK_BOX(window->hbox), window->combo, TRUE, TRUE, 0);
  window->comboChanged = g_signal_connect(G_OBJECT(window->combo), "changed",
					  G_CALLBACK(onComboChanged),
					  (gpointer)window);

  /* Add a menu button. */
  wd = gtk_button_new();
  gtk_button_set_relief(GTK_BUTTON(wd), GTK_RELIEF_NONE);
  gtk_box_pack_start(GTK_BOX(window->hbox), wd, FALSE, FALSE, 2);
  g_signal_connect(G_OBJECT(wd), "clicked",
		   G_CALLBACK(onMainButtonClicked), (gpointer)window);
  image = create_pixmap((GtkWidget*)0, "stock-menu-detach.png");
  gtk_container_add(GTK_CONTAINER(wd), image);
  gtk_widget_set_tooltip_text(wd,
		       _("Manage hidden subpanels and dock windows."));

  /* Add a help tooltips. */
  wd = gtk_event_box_new();
  gtk_event_box_set_visible_window(GTK_EVENT_BOX(wd), FALSE);
  gtk_widget_set_tooltip_text(wd,
		       _("Positions, sizes, names, contains... of dock" \
			 " windows are stored in the parameters file, " \
			 "see the 'Config. files' button on the command panel."));
  gtk_box_pack_end(GTK_BOX(window->hbox), wd, FALSE, FALSE, 2);
  image = gtk_image_new_from_icon_name("help-browser", GTK_ICON_SIZE_MENU);
  gtk_container_add(GTK_CONTAINER(wd), image);

  gtk_widget_show_all(window->vbox);
  return window;
}
static VisuUiDockWindow* dock_window_ref(VisuUiDockWindow *dock)
{
  dock->refCount += 1;
  return dock;
}
static void dock_window_unref(VisuUiDockWindow *dock)
{
  dock->refCount -= 1;
  if (!dock->refCount)
    {
      if (dock->window)
        gtk_widget_destroy(dock->window);
      else
        gtk_widget_destroy(dock->vbox);
    }
}
/**
 * visu_ui_dock_window_getCharacteristics:
 * @dock: a #VisuUiDockWindow object ;
 * @id: a location to store the identifier (owned by V_Sim) ;
 * @visibility: a location to store the status of the dock, hidden or
 * not ;
 * @x: a location to store its x position on the root window ;
 * @y: a location to store its y position on the root window ;
 * @width: a location to store its width ;
 * @height: a location to store its height.
 *
 * A routine to know everything about a #VisuUiDockWindow.
 */
void visu_ui_dock_window_getCharacteristics(VisuUiDockWindow *dock, gchar **id, 
					     gboolean *visibility, 
					     gint *x, gint *y, 
					     gint *width, gint *height)
{
  g_return_if_fail(dock);

  *id = dock->name;
  *visibility = dock->show;
  gtk_window_get_position(GTK_WINDOW(dock->window), x, y);
  gtk_window_get_size(GTK_WINDOW(dock->window), width, height);
}
/**
 * visu_ui_dock_window_getContainer:
 * @dock: a dock window descriptor.
 *
 * A dock window is a small utility window with a list of #VisuUiPanel.
 * This window can be embedded in a container or have its own
 * GtkWindow. This routine get the top-level container inside a given
 * #VisuUiDockWindow. If you prefer to get the GtkWindow containing the
 * #VisuUiDockWindow use visu_ui_dock_window_getWindow() instead.
 *
 * Returns: (transfer none): a top-level container (usually a #GtkVBox).
 */
GtkWidget* visu_ui_dock_window_getContainer(VisuUiDockWindow *dock)
{
  g_return_val_if_fail(dock, (GtkWidget*)0);
  
  return dock->vbox;
}
/**
 * visu_ui_dock_window_getNotebook:
 * @dock: a dock window descriptor.
 *
 * A dock window is a small utility window with a list of #VisuUiPanel.
 * All #VisuUiPanel are contained in a #GtkNotebook. This routine gets it.
 *
 * Returns: (transfer none): the #GtkNotebook containing the #VisuUiPanel of this
 * #VisuUiDockWindow.
 */
GtkWidget* visu_ui_dock_window_getNotebook(VisuUiDockWindow *dock)
{
  g_return_val_if_fail(dock, (GtkWidget*)0);
  
  return dock->notebook;
}
/**
 * visu_ui_dock_window_getWindow:
 * @dock: a dock window descriptor.
 *
 * A dock window is a small utility window with a list of #VisuUiPanel.
 * This window can be embedded in a container or have its own
 * GtkWindow. This routine get the #GtkWindow containing the
 * #VisuUiDockWindow in the case the dock is stand-alone. If not NULL is returned.
 *
 * Returns: (transfer none): a top-level container (usually a #GtkWindow) or NULL.
 */
GtkWidget* visu_ui_dock_window_getWindow(VisuUiDockWindow *dock)
{
  g_return_val_if_fail(dock, (GtkWidget*)0);
  
  if (dock->window)
    return dock->window;
  else
    return GTK_WIDGET(visu_ui_main_class_getCurrentPanel());
}
/**
 * visu_ui_dock_window_setSize:
 * @dock: a #VisuUiDockWindow object ;
 * @width: the requested width ;
 * @height: the requested height.
 *
 * Change the size of the given #VisuUiDockWindow. The size is possibly
 * adapted to avoid been out of screen.
 */
void visu_ui_dock_window_setSize(VisuUiDockWindow *dock, guint width, guint height)
{
  GdkScreen *screen;
  gint widthScreen, heightScreen;
  gint widthSet, heightSet;

  g_return_if_fail(dock && dock->window);
  g_return_if_fail(dock != visu_ui_panel_class_getCommandPanel());

  /* Before setting (width, height), we check that it is coherent with
     the screen size. */
  if (gtk_widget_is_drawable(dock->window))
    screen = gdk_window_get_screen
      (GDK_WINDOW(gtk_widget_get_window(dock->window)));
  else
    screen = gdk_screen_get_default();
#if GTK_MAJOR_VERSION < 3 || (GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION < 22)
  widthScreen = gdk_screen_get_width(screen);
  heightScreen = gdk_screen_get_height(screen);
#else
  (void)screen;
  widthScreen = 10240;
  heightScreen = 10240;
#endif

  widthSet = ((gint)width > widthScreen)?widthScreen:(gint)width;
  heightSet = ((gint)height > heightScreen)?heightScreen:(gint)height;
  if (widthSet < 0) widthSet = 50;
  if (heightSet < 0) heightSet = 50;
  DBG_fprintf(stderr, "Gtk VisuUiPanel: set window (%s) size %dx%d.\n",
	      dock->name, widthSet, heightSet);
  gtk_window_resize(GTK_WINDOW(dock->window), widthSet, heightSet);
}
/**
 * visu_ui_dock_window_setPosition:
 * @dock: a #VisuUiDockWindow object ;
 * @x: the requested x position ;
 * @y: the requested y position.
 *
 * Change the position of the given #VisuUiDockWindow. The position is possibly
 * adapted to avoid been out of screen.
 */
void visu_ui_dock_window_setPosition(VisuUiDockWindow *dock, guint x, guint y)
{
  GdkScreen *screen;
  gint xScreen, yScreen;
  gint xPosition, yPosition;
  gint width, height;

  g_return_if_fail(dock && dock->window);
  g_return_if_fail(dock != visu_ui_panel_class_getCommandPanel());

  /* Before setting (x, y), we check that it is coherent with
     the screen definition. */
  if (gtk_widget_is_drawable(dock->window))
    screen = gdk_window_get_screen
      (GDK_WINDOW(gtk_widget_get_window(dock->window)));
  else
    screen = gdk_screen_get_default();
#if GTK_MAJOR_VERSION < 3 || (GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION < 22)
  xScreen = gdk_screen_get_width(screen);
  yScreen = gdk_screen_get_height(screen);
#else
  (void)screen;
  xScreen = 10240;
  yScreen = 10240;
#endif
  gtk_window_get_size(GTK_WINDOW(dock->window), &width, &height);

  xPosition = ((gint)x + width > xScreen)?xScreen - width:(gint)x;
  yPosition = ((gint)y + height > yScreen)?yScreen - height:(gint)y;
  if (xPosition < 0) xPosition = 0;
  if (yPosition < 0) yPosition = 0;
  DBG_fprintf(stderr, "Gtk VisuUiPanel: set window (%s) position %dx%d.\n",
	      dock->name, xPosition, yPosition);
  gtk_window_move(GTK_WINDOW(dock->window), xPosition, yPosition);
}
/**
 * visu_ui_dock_window_setVisibility:
 * @dock: a #VisuUiDockWindow object ;
 * @visible: a boolean.
 *
 * Change the visibility of a #VisuUiDockWindow. If hidden, the dock is
 * added to the list of hidden #VisuUiDockWindow that can be shown again
 * using the pop-up menu on every visible #VisuUiDockWindow. The 'Main' dock
 * window can not be hidden.
 */
void visu_ui_dock_window_setVisibility(VisuUiDockWindow *dock, gboolean visible)
{
  g_return_if_fail(dock && dock->window);
  g_return_if_fail(dock != visu_ui_panel_class_getCommandPanel());

  dock->show = visible;
  if (!visible)
    gtk_widget_hide(dock->window);
  else
    gtk_widget_show(dock->window);
}

static void onEntryTabview(VisuConfigFile *obj _U_, VisuConfigFileEntry *entry _U_, VisuUiPanelClass *klass _U_)
{
  visu_ui_panel_class_setHeaderVisibility(_tabView);
}
/* These functions write all the element list to export there associated resources. */
static void exportParameters(GString *data, VisuData *dataObj _U_)
{
  g_string_append_printf(data, "# %s\n", DESC_PARAMETER_TABVIEW_CONFIG);
  g_string_append_printf(data, "%s[gtk]: %d\n\n", FLAG_PARAMETER_TABVIEW_CONFIG,
			 local_class->showHeader);
}
