/*   EXTRAITS DE LA LICENCE
	Copyright CEA, contributeurs : Damien
	CALISTE, laboratoire L_Sim, (2001-2013)
  
	Adresse 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 : Damien
	CALISTE, laboratoire L_Sim, (2001-2013)

	E-mail address:
	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 COPYING.
*/
#include "frame.h"

#include <string.h>

#include <GL/gl.h>

#include <coreTools/toolColor.h>
#include <openGLFunctions/text.h>

/**
 * SECTION:frame
 * @short_description: Draw a frame with the representation of a color frame.
 *
 * <para>This extension draws a frame on top of the rendering area
 * with a color frame. One can setup printed values and draw
 * additional marks inside the frame.</para>
 *
 * Since: 3.7
 */

/**
 * VisuGlExtFrameClass:
 * @parent: the parent class;
 * @draw: the draw method for the content of this frame.
 *
 * A short way to identify #_VisuGlExtFrameClass structure.
 *
 * Since: 3.7
 */
/**
 * VisuGlExtFrame:
 *
 * An opaque structure.
 *
 * Since: 3.7
 */
/**
 * VisuGlExtFramePrivate:
 *
 * Private fields for #VisuGlExtFrame objects.
 *
 * Since: 3.7
 */
struct _VisuGlExtFramePrivate
{
  gboolean dispose_has_run;

  /* Rendering parameters. */
  guint requisition[2];
  float scale;
  float xpos, ypos, xmargin, ymargin, xpad, ypad;
  float bgRGBA[4];
  gchar *title;

  /* Object dependencies. */
  VisuGlView *view;
  gulong widthHeight_signal;
};

enum
  {
    PROP_0,
    XPOS_PROP,
    YPOS_PROP,
    XPAD_PROP,
    YPAD_PROP,
    N_PROP
  };
static GParamSpec *properties[N_PROP];

static int refMask[4] = {TOOL_COLOR_MASK_R, TOOL_COLOR_MASK_G,
                         TOOL_COLOR_MASK_B, TOOL_COLOR_MASK_A};
static float bgRGBADefault[4] = {1.f, 1.f, 1.f, 0.4f};
static float fontRGBDefault[4] = {0.f, 0.f, 0.f};
static float xmarginDefault = 10.f, ymarginDefault = 10.f;
static float xpadDefault = 5.f, ypadDefault = 5.f;

static void visu_gl_ext_frame_finalize(GObject* obj);
static void visu_gl_ext_frame_dispose(GObject* obj);
static void visu_gl_ext_frame_get_property(GObject* obj, guint property_id,
                                           GValue *value, GParamSpec *pspec);
static void visu_gl_ext_frame_set_property(GObject* obj, guint property_id,
                                           const GValue *value, GParamSpec *pspec);
static void visu_gl_ext_frame_draw(VisuGlExt *ext);
static gboolean visu_gl_ext_frame_setGlView(VisuGlExt *frame, VisuGlView *view);

static void onViewChanged(VisuGlView *view, gpointer data);

G_DEFINE_TYPE_WITH_CODE(VisuGlExtFrame, visu_gl_ext_frame, VISU_TYPE_GL_EXT,
                        G_ADD_PRIVATE(VisuGlExtFrame))

static void visu_gl_ext_frame_class_init(VisuGlExtFrameClass *klass)
{
  DBG_fprintf(stderr, "Extension Frame: creating the class of the object.\n");
  /* DBG_fprintf(stderr, "                - adding new signals ;\n"); */

  /* DBG_fprintf(stderr, "                - adding new resources ;\n"); */
  klass->draw = NULL;

  /* Connect the overloading methods. */
  G_OBJECT_CLASS(klass)->dispose  = visu_gl_ext_frame_dispose;
  G_OBJECT_CLASS(klass)->finalize = visu_gl_ext_frame_finalize;
  G_OBJECT_CLASS(klass)->set_property = visu_gl_ext_frame_set_property;
  G_OBJECT_CLASS(klass)->get_property = visu_gl_ext_frame_get_property;
  VISU_GL_EXT_CLASS(klass)->draw = visu_gl_ext_frame_draw;
  VISU_GL_EXT_CLASS(klass)->setGlView = visu_gl_ext_frame_setGlView;

  /**
   * VisuGlExtFrame::x-pos:
   *
   * Store the position along x of the frame.
   *
   * Since: 3.8
   */
  properties[XPOS_PROP] = g_param_spec_float("x-pos", "x position",
                                             "position along x axis",
                                             0., 1., 0.,
                                             G_PARAM_READWRITE);
  g_object_class_install_property(G_OBJECT_CLASS(klass), XPOS_PROP,
				  properties[XPOS_PROP]);
  /**
   * VisuGlExtFrame::y-pos:
   *
   * Store the position along y of the frame.
   *
   * Since: 3.8
   */
  properties[YPOS_PROP] = g_param_spec_float("y-pos", "y position",
                                             "position along y axis",
                                             0., 1., 1.,
                                             G_PARAM_READWRITE);
  g_object_class_install_property(G_OBJECT_CLASS(klass), YPOS_PROP,
				  properties[YPOS_PROP]);
  /**
   * VisuGlExtFrame::x-padding:
   *
   * Store the padding along x of the frame.
   *
   * Since: 3.8
   */
  properties[XPAD_PROP] = g_param_spec_float("x-padding", "x padding",
                                             "padding along x axis",
                                             0., 30., xpadDefault,
                                             G_PARAM_READWRITE);
  g_object_class_install_property(G_OBJECT_CLASS(klass), XPAD_PROP,
				  properties[XPAD_PROP]);
  /**
   * VisuGlExtFrame::y-padding:
   *
   * Store the padding along y of the frame.
   *
   * Since: 3.8
   */
  properties[YPAD_PROP] = g_param_spec_float("y-padding", "y padding",
                                             "padding along y axis",
                                             0., 30., ypadDefault,
                                             G_PARAM_READWRITE);
  g_object_class_install_property(G_OBJECT_CLASS(klass), YPAD_PROP,
				  properties[YPAD_PROP]);
}

static void visu_gl_ext_frame_init(VisuGlExtFrame *obj)
{
  DBG_fprintf(stderr, "Extension Frame: initializing a new object (%p).\n",
	      (gpointer)obj);

  obj->width      = 0;
  obj->height     = 0;
  obj->fontRGB[0] = fontRGBDefault[0];
  obj->fontRGB[1] = fontRGBDefault[1];
  obj->fontRGB[2] = fontRGBDefault[2];
  
  obj->priv = visu_gl_ext_frame_get_instance_private(obj);
  obj->priv->dispose_has_run = FALSE;

  /* Private data. */
  obj->priv->requisition[0] = 0;
  obj->priv->requisition[1] = 0;
  obj->priv->scale      = 1.f;
  obj->priv->xpos       = 0.f;
  obj->priv->ypos       = 0.f;
  obj->priv->xmargin    = xmarginDefault;
  obj->priv->ymargin    = ymarginDefault;
  obj->priv->xpad       = xpadDefault;
  obj->priv->ypad       = ypadDefault;
  obj->priv->bgRGBA[0]  = bgRGBADefault[0];
  obj->priv->bgRGBA[1]  = bgRGBADefault[1];
  obj->priv->bgRGBA[2]  = bgRGBADefault[2];
  obj->priv->bgRGBA[3]  = bgRGBADefault[3];
  obj->priv->title      = g_strdup("");
  obj->priv->view               = (VisuGlView*)0;
  obj->priv->widthHeight_signal = 0;
}

/* This method can be called several times.
   It should unref all of its reference to
   GObjects. */
static void visu_gl_ext_frame_dispose(GObject* obj)
{
  VisuGlExtFrame *frame;

  DBG_fprintf(stderr, "Extension Frame: dispose object %p.\n", (gpointer)obj);

  frame = VISU_GL_EXT_FRAME(obj);
  if (frame->priv->dispose_has_run)
    return;
  frame->priv->dispose_has_run = TRUE;

  /* Disconnect signals. */
  visu_gl_ext_frame_setGlView(VISU_GL_EXT(obj), (VisuGlView*)0);

  /* Chain up to the parent class */
  G_OBJECT_CLASS(visu_gl_ext_frame_parent_class)->dispose(obj);
}
/* This method is called once only. */
static void visu_gl_ext_frame_finalize(GObject* obj)
{
  VisuGlExtFrame *frame;

  g_return_if_fail(obj);

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

  frame = VISU_GL_EXT_FRAME(obj);
  /* Free privs elements. */
  if (frame->priv)
    {
      DBG_fprintf(stderr, "Extension Frame: free private frame.\n");
      g_free(frame->priv->title);
    }

  /* Chain up to the parent class */
  DBG_fprintf(stderr, "Extension Frame: chain to parent.\n");
  G_OBJECT_CLASS(visu_gl_ext_frame_parent_class)->finalize(obj);
  DBG_fprintf(stderr, "Extension Frame: freeing ... OK.\n");
}

static void visu_gl_ext_frame_get_property(GObject* obj, guint property_id,
                                          GValue *value, GParamSpec *pspec)
{
  VisuGlExtFrame *self = VISU_GL_EXT_FRAME(obj);

  DBG_fprintf(stderr, "Extension Frame: get property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case XPOS_PROP:
      g_value_set_float(value, self->priv->xpos);
      DBG_fprintf(stderr, "%g.\n", self->priv->xpos);
      break;
    case YPOS_PROP:
      g_value_set_float(value, self->priv->ypos);
      DBG_fprintf(stderr, "%g.\n", self->priv->ypos);
      break;
    case XPAD_PROP:
      g_value_set_float(value, self->priv->xpad);
      DBG_fprintf(stderr, "%g.\n", self->priv->xpad);
      break;
    case YPAD_PROP:
      g_value_set_float(value, self->priv->ypad);
      DBG_fprintf(stderr, "%g.\n", self->priv->ypad);
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}
static void visu_gl_ext_frame_set_property(GObject* obj, guint property_id,
                                          const GValue *value, GParamSpec *pspec)
{
  VisuGlExtFrame *self = VISU_GL_EXT_FRAME(obj);

  DBG_fprintf(stderr, "Extension Frame: set property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case XPOS_PROP:
      visu_gl_ext_frame_setPosition(self, g_value_get_float(value), self->priv->ypos);
      DBG_fprintf(stderr, "%g.\n", self->priv->xpos);
      break;
    case YPOS_PROP:
      visu_gl_ext_frame_setPosition(self, self->priv->xpos, g_value_get_float(value));
      DBG_fprintf(stderr, "%g.\n", self->priv->ypos);
      break;
    case XPAD_PROP:
      DBG_fprintf(stderr, "%g.\n", g_value_get_float(value));
      visu_gl_ext_frame_setPadding(self, g_value_get_float(value), self->priv->ypad);
      break;
    case YPAD_PROP:
      DBG_fprintf(stderr, "%g.\n", g_value_get_float(value));
      visu_gl_ext_frame_setPadding(self, self->priv->xpad, g_value_get_float(value));
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}

static gboolean visu_gl_ext_frame_setGlView(VisuGlExt *frame, VisuGlView *view)
{
  VisuGlExtFramePrivate *priv = VISU_GL_EXT_FRAME(frame)->priv;

  if (priv->view == view)
    return FALSE;

  if (priv->view)
    {
      g_signal_handler_disconnect(G_OBJECT(priv->view), priv->widthHeight_signal);
      g_object_unref(priv->view);
    }
  if (view)
    {
      g_object_ref(view);
      priv->widthHeight_signal =
        g_signal_connect(G_OBJECT(view), "WidthHeightChanged",
                         G_CALLBACK(onViewChanged), (gpointer)frame);
    }
  else
    priv->widthHeight_signal = 0;
  priv->view = view;

  visu_gl_ext_setDirty(frame, TRUE);
  return TRUE;
}

/**
 * visu_gl_ext_frame_setPosition:
 * @frame: the #VisuGlExtFrame object to modify.
 * @xpos: the reduced y position (1 to the left).
 * @ypos: the reduced y position (1 to the bottom).
 *
 * Change the position of the frame representation.
 *
 * Since: 3.7
 *
 * Returns: TRUE if any position is actually changed.
 **/
gboolean visu_gl_ext_frame_setPosition(VisuGlExtFrame *frame, float xpos, float ypos)
{
  gboolean changed;

  g_return_val_if_fail(VISU_IS_GL_EXT_FRAME(frame), FALSE);

  xpos = CLAMP(xpos, 0.f, 1.f);
  ypos = CLAMP(ypos, 0.f, 1.f);
  changed = FALSE;

  g_object_freeze_notify(G_OBJECT(frame));
  if (xpos != frame->priv->xpos)
    {
      frame->priv->xpos = xpos;
      g_object_notify_by_pspec(G_OBJECT(frame), properties[XPOS_PROP]);
      changed = TRUE;
    }
  if (ypos != frame->priv->ypos)
    {
      frame->priv->ypos = ypos;
      g_object_notify_by_pspec(G_OBJECT(frame), properties[YPOS_PROP]);
      changed = TRUE;
    }
  if (changed)
    visu_gl_ext_setDirty(VISU_GL_EXT(frame), TRUE);
  g_object_thaw_notify(G_OBJECT(frame));

  return changed;
}
/**
 * visu_gl_ext_frame_setPadding:
 * @frame: the #VisuGlExtFrame object to modify.
 * @xpad: the padding along x.
 * @ypad: the padding along y.
 *
 * Change the padding of the frame representation.
 *
 * Since: 3.8
 *
 * Returns: TRUE if any position is actually changed.
 **/
gboolean visu_gl_ext_frame_setPadding(VisuGlExtFrame *frame, float xpad, float ypad)
{
  gboolean changed;

  g_return_val_if_fail(VISU_IS_GL_EXT_FRAME(frame), FALSE);

  xpad = CLAMP(xpad, 0.f, 30.f);
  ypad = CLAMP(ypad, 0.f, 30.f);
  changed = FALSE;

  g_object_freeze_notify(G_OBJECT(frame));
  if (xpad != frame->priv->xpad)
    {
      frame->priv->xpad = xpad;
      g_object_notify_by_pspec(G_OBJECT(frame), properties[XPAD_PROP]);
      changed = TRUE;
    }
  if (ypad != frame->priv->ypad)
    {
      frame->priv->ypad = ypad;
      g_object_notify_by_pspec(G_OBJECT(frame), properties[YPAD_PROP]);
      changed = TRUE;
    }
  if (changed)
    visu_gl_ext_setDirty(VISU_GL_EXT(frame), TRUE);
  g_object_thaw_notify(G_OBJECT(frame));

  return changed;
}
#define SET_RGBA_I(S, I, V, M) {if (M & refMask[I] && S[I] != V[I]) \
      {S[I] = V[I]; diff = TRUE;}}
#define SET_RGBA(S, V, M) {SET_RGBA_I(S, 0, V, M);                      \
    SET_RGBA_I(S, 1, V, M); SET_RGBA_I(S, 2, V, M); SET_RGBA_I(S, 3, V, M); }
#define SET_RGB(S, V, M) {SET_RGBA_I(S, 0, V, M);                      \
    SET_RGBA_I(S, 1, V, M); SET_RGBA_I(S, 2, V, M); }
/**
 * visu_gl_ext_frame_setBgRGBA:
 * @frame: the #VisuGlExtFrame to update.
 * @rgba: (array fixed-size=4): a four floats array with values (0 <= values <= 1) for the
 * red, the green, the blue color and the alpha channel. Only values
 * specified by the @mask are really relevant.
 * @mask: use #TOOL_COLOR_MASK_R, #TOOL_COLOR_MASK_G,
 * #TOOL_COLOR_MASK_B, #TOOL_COLOR_MASK_A or a combinaison to indicate
 * what values in the @rgba array must be taken into account.
 *
 * Change the colour to represent the background of the frame.
 *
 * Since: 3.7
 *
 * Returns: TRUE if value is actually changed.
 */
gboolean visu_gl_ext_frame_setBgRGBA(VisuGlExtFrame *frame, float rgba[4], int mask)
{
  gboolean diff = FALSE;

  g_return_val_if_fail(VISU_IS_GL_EXT_FRAME(frame), FALSE);
    
  SET_RGBA(frame->priv->bgRGBA, rgba, mask);
  if (!diff)
    return FALSE;

  visu_gl_ext_setDirty(VISU_GL_EXT(frame), TRUE);
  return TRUE;
}
/**
 * visu_gl_ext_frame_setFontRGB:
 * @frame: the #VisuGlExtFrame to update.
 * @rgb: (array fixed-size=3): a four floats array with values (0 <= values <= 1) for the
 * red, the green, the blue color. Only values specified by the @mask
 * are really relevant.
 * @mask: use #TOOL_COLOR_MASK_R, #TOOL_COLOR_MASK_G,
 * #TOOL_COLOR_MASK_B or a combinaison to indicate what values in the
 * @rgb array must be taken into account.
 *
 * Change the colour to represent the font of the frame.
 *
 * Since: 3.7
 *
 * Returns: TRUE if value is actually changed.
 */
gboolean visu_gl_ext_frame_setFontRGB(VisuGlExtFrame *frame, float rgb[3], int mask)
{
  gboolean diff = FALSE;

  g_return_val_if_fail(VISU_IS_GL_EXT_FRAME(frame), FALSE);
    
  SET_RGB(frame->fontRGB, rgb, mask);
  if (!diff)
    return FALSE;

  visu_gl_ext_setDirty(VISU_GL_EXT(frame), TRUE);
  return TRUE;
}
/**
 * visu_gl_ext_frame_setScale:
 * @frame: the #VisuGlExtFrame to update.
 * @scale: a positive value.
 *
 * Change the zoom level for the rendering of the legend.
 *
 * Since: 3.7
 *
 * Returns: TRUE if value is actually changed.
 **/
gboolean visu_gl_ext_frame_setScale(VisuGlExtFrame *frame, float scale)
{
  g_return_val_if_fail(VISU_IS_GL_EXT_FRAME(frame), FALSE);

  if (frame->priv->scale == scale)
    return FALSE;

  frame->priv->scale = CLAMP(scale, 0.01f, 10.f);

  visu_gl_ext_setDirty(VISU_GL_EXT(frame), TRUE);
  return TRUE;
}
/**
 * visu_gl_ext_frame_setTitle:
 * @frame: the #VisuGlExtFrame object to modify.
 * @title: a title.
 *
 * Change the title of the box legend.
 *
 * Since: 3.7
 *
 * Returns: TRUE if title is actually changed.
 **/
gboolean visu_gl_ext_frame_setTitle(VisuGlExtFrame *frame, const gchar *title)
{
  g_return_val_if_fail(VISU_IS_GL_EXT_FRAME(frame) && title, FALSE);

  if (!strcmp(title, frame->priv->title))
    return FALSE;
  g_free(frame->priv->title);
  frame->priv->title = g_strdup(title);

  visu_gl_ext_setDirty(VISU_GL_EXT(frame), TRUE);
  return TRUE;
}
/**
 * visu_gl_ext_frame_setRequisition:
 * @frame: the #VisuGlExtFrame object to modify.
 * @width: the desired width.
 * @height: the desired height.
 *
 * Set the size of the frame in pixels. Use
 * visu_gl_ext_frame_setScale() to adjust the size if necessary.
 *
 * Since: 3.7
 *
 * Returns: TRUE if requested size is changed.
 **/
gboolean visu_gl_ext_frame_setRequisition(VisuGlExtFrame *frame, guint width, guint height)
{
  g_return_val_if_fail(VISU_IS_GL_EXT_FRAME(frame), FALSE);

  if (frame->priv->requisition[0] == width &&
      frame->priv->requisition[1] == height)
    return FALSE;

  frame->priv->requisition[0] = width;
  frame->priv->requisition[1] = height;

  visu_gl_ext_setDirty(VISU_GL_EXT(frame), TRUE);
  return TRUE;
}

/**
 * visu_gl_ext_frame_getScale:
 * @frame: a #VisuGlExtFrame object.
 *
 * Frames are rendered with a scaling factor of 1. by default.
 *
 * Since: 3.7
 *
 * Returns: the scaling factor used to represent frames.
 **/
float visu_gl_ext_frame_getScale(const VisuGlExtFrame *frame)
{
  g_return_val_if_fail(VISU_IS_GL_EXT_FRAME(frame), 1.f);

  return frame->priv->scale;
}
/**
 * visu_gl_ext_frame_getPosition:
 * @frame: the #VisuGlExtFrame object to inquire.
 * @xpos: (out) (allow-none): a location to store the x position.
 * @ypos: (out) (allow-none): a location to store the y position.
 *
 * Inquire the position of the representation of the frame.
 *
 * Since: 3.7
 **/
void visu_gl_ext_frame_getPosition(const VisuGlExtFrame *frame,
                                   float *xpos, float *ypos)
{
  g_return_if_fail(VISU_IS_GL_EXT_FRAME(frame));

  if (xpos)
    *xpos = frame->priv->xpos;
  if (ypos)
    *ypos = frame->priv->ypos;
}

static void onViewChanged(VisuGlView *view _U_, gpointer data)
{
  visu_gl_ext_setDirty(VISU_GL_EXT(data), TRUE);
}

/**
 * visu_gl_ext_frame_draw:
 * @frame: a #VisuGlExtFrame object.
 *
 * Render the@frame and its child.
 *
 * Since: 3.7
 */
static void visu_gl_ext_frame_draw(VisuGlExt *ext)
{
  int viewport[4];
  float requisition[2];
  VisuGlExtFrame *frame;
  VisuGlExtFrameClass *klass;
  int xpos, ypos, dx, dy;

  g_return_if_fail(VISU_IS_GL_EXT_FRAME(ext));
  frame = VISU_GL_EXT_FRAME(ext);

  DBG_fprintf(stderr, "Visu GlExt Frame: drawing the extension '%p'.\n", (gpointer)frame);

  klass = VISU_GL_EXT_FRAME_GET_CLASS(frame);
  if (!klass->draw ||
      (klass->isValid && !klass->isValid(frame)))
    return;

  visu_gl_text_initFontList();

  visu_gl_ext_startDrawing(ext);

  /* We get the size of the current viewport. */
  glGetIntegerv(GL_VIEWPORT, viewport);
  DBG_fprintf(stderr, "Visu GlExt Frame: current viewport is %dx%d.\n",
              viewport[2], viewport[3]);

  /* We change the viewport. */
  DBG_fprintf(stderr, "Visu GlExt Frame: requisition is %dx%d.\n",
              frame->priv->requisition[0], frame->priv->requisition[1]);
  requisition[0] = (float)frame->priv->requisition[0];
  requisition[1] = (float)frame->priv->requisition[1] +
    ((frame->priv->title[0])?(frame->priv->ypad + 12.f):0.f);
  DBG_fprintf(stderr, "Visu GlExt Frame: requisition is %gx%g.\n",
              requisition[0], requisition[1]);
  frame->width = MIN((guint)(requisition[0] * frame->priv->scale),
                     viewport[2] - (guint)(2.f * frame->priv->xmargin));
  frame->height = MIN((guint)(requisition[1] * frame->priv->scale),
                      viewport[3] - (guint)(2.f * frame->priv->ymargin));
  xpos = frame->priv->xmargin +
    ((float)viewport[2] - 2.f * frame->priv->xmargin - frame->width) * frame->priv->xpos;
  ypos = frame->priv->ymargin +
    ((float)viewport[3] - 2.f * frame->priv->ymargin - frame->height) * frame->priv->ypos;
  glViewport(xpos, ypos, frame->width, frame->height);

  glDisable(GL_FOG);
  glDisable(GL_LIGHTING);
  glDisable(GL_DEPTH_TEST);
  /* glDisable(GL_ALPHA_TEST); */
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  /* We change the projection for a 2D one. */
  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity();
  glOrtho(0., (float)frame->width, 0., (float)frame->height, -50., +50.);
  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();

  /* We draw a big transparent square to do the back. */
  if (frame->priv->bgRGBA[3] > 0.f)
    {
      glColor4fv(frame->priv->bgRGBA);
      glRecti(0, 0, frame->width, frame->height);
    }

  /* We draw the title, if any. */
  if (frame->priv->title[0])
    {
      glColor3fv(frame->fontRGB);
      glRasterPos2f(frame->priv->xpad * frame->priv->scale,
                    frame->height - (frame->priv->ypad + 12.) * frame->priv->scale);
      visu_gl_text_drawChars(frame->priv->title, VISU_GL_TEXT_NORMAL); 
    }

  /* We change again the viewport to adjust to padding. */
  dx = frame->priv->xpad * frame->priv->scale;
  dy = frame->priv->ypad * frame->priv->scale;
  xpos += dx;
  ypos += dy;
  frame->width -= 2.f * dx * frame->priv->scale;
  frame->height -= 2.f * dy * frame->priv->scale;
  if (frame->priv->title[0])
    frame->height -= (frame->priv->ypad + 12.) * frame->priv->scale;
  if (frame->width > (guint)viewport[2])
    frame->width = 0;
  if (frame->height > (guint)viewport[3])
    frame->height = 0;
  DBG_fprintf(stderr, "Visu GlExt Frame: frame actual size is %dx%d.\n",
              frame->width, frame->height);
  glViewport(xpos, ypos, frame->width, frame->height);
  glPopMatrix();
  glMatrixMode(GL_PROJECTION);
  glPopMatrix();
  glPushMatrix();
  glLoadIdentity();
  glOrtho(0., (float)frame->width, 0., (float)frame->height, -50., +50.);  
  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();
  
  klass->draw(frame);

  /* We change the projection back. */
  glPopMatrix();
  glMatrixMode(GL_PROJECTION);
  glPopMatrix();
  glMatrixMode(GL_MODELVIEW);

  /* We set viewport back. */
  DBG_fprintf(stderr, "Visu GlExt Frame: put back viewport at %dx%d.\n",
              viewport[2], viewport[3]);
  glViewport(0, 0, viewport[2], viewport[3]);

  visu_gl_ext_completeDrawing(ext);
}
