#include <gtk/gtk.h>
#include "dw_embed_gtk.h"
#include "dw_gtk_viewport.h"


static void Dw_embed_gtk_init           (DwEmbedGtk *embed_gtk);
static void Dw_embed_gtk_class_init     (DwEmbedGtkClass *klass);

static void Dw_embed_gtk_destroy        (GtkObject *object);

static void Dw_embed_gtk_size_request   (DwWidget *widget,
                                         DwRequisition *requisition);
static gint Dw_embed_gtk_move_idle      (void *data);
static void Dw_embed_gtk_size_allocate  (DwWidget *widget,
                                         DwAllocation *allocation);
static void Dw_embed_gtk_draw           (DwWidget *widget,
                                         DwRectangle *area,
                                         GdkEventExpose *event);

static void Dw_embed_gtk_remove_gtk     (DwEmbedGtk *embed_gtk);

static GtkWidgetClass *parent_class;

/*
 * Standard Gtk+ function
 */
GtkType a_Dw_embed_gtk_get_type (void)
{
   static GtkType type = 0;

   if (!type) {
      GtkTypeInfo info = {
         "DwEmbedGtk",
         sizeof (DwEmbedGtk),
         sizeof (DwEmbedGtkClass),
         (GtkClassInitFunc) Dw_embed_gtk_class_init,
         (GtkObjectInitFunc) Dw_embed_gtk_init,
         (GtkArgSetFunc) NULL,
         (GtkArgGetFunc) NULL,
         (GtkClassInitFunc) NULL
      };

      type = gtk_type_unique (DW_TYPE_WIDGET, &info);
   }

   return type;
}


/*
 * Standard Gtk+ function
 */
DwWidget* a_Dw_embed_gtk_new (void)
{
   GtkObject *object;

   object = gtk_object_new (DW_TYPE_EMBED_GTK, NULL);
   return DW_WIDGET (object);
}


/*
 * Standard Gtk+ function
 */
static void Dw_embed_gtk_init (DwEmbedGtk *embed_gtk)
{
   embed_gtk->child = NULL;
   embed_gtk->idle_id = 0;
}


/*
 * Standard Gtk+ function
 */
static void Dw_embed_gtk_class_init (DwEmbedGtkClass *klass)
{
   GtkObjectClass *object_class;
   DwWidgetClass *widget_class;

   parent_class = gtk_type_class (DW_TYPE_WIDGET);

   object_class = GTK_OBJECT_CLASS (klass);
   object_class->destroy = Dw_embed_gtk_destroy;

   widget_class = (DwWidgetClass*) klass;
   widget_class->size_allocate = Dw_embed_gtk_size_allocate;
   widget_class->size_request = Dw_embed_gtk_size_request;
   widget_class->draw = Dw_embed_gtk_draw;
}


/*
 * Standard Gtk+ function
 */
static void Dw_embed_gtk_destroy (GtkObject *object)
{
   DwEmbedGtk *embed_gtk;

   embed_gtk = DW_EMBED_GTK (object);
   if (embed_gtk->child)
      gtk_object_destroy (GTK_OBJECT (embed_gtk->child));
   if (embed_gtk->idle_id != 0)
      gtk_idle_remove (embed_gtk->idle_id);

   GTK_OBJECT_CLASS(parent_class)->destroy (object);
}



/*
 * Standard Gtk+ function
 */
static void Dw_embed_gtk_size_request   (DwWidget *widget,
                                         DwRequisition *requisition)
{
   DwEmbedGtk *embed_gtk;
   GtkRequisition child_requisition;

   embed_gtk = DW_EMBED_GTK (widget);
   if (embed_gtk->child) {
      gtk_widget_size_request (embed_gtk->child, &child_requisition);

      requisition->width = child_requisition.width;
      requisition->ascent = child_requisition.height;
      requisition->descent = 0;
   } else {
      requisition->width = 0;
      requisition->ascent = 0;
      requisition->descent = 0;
   }
}


/*
 * Standard Gtk+ function
 */
static gint Dw_embed_gtk_move_idle (void *data)
{
   DwWidget *widget;
   DwEmbedGtk *embed_gtk;

   widget = DW_WIDGET (data);
   embed_gtk = DW_EMBED_GTK (data);

   if (embed_gtk->child) {
      if (embed_gtk->child->parent)
         gtk_layout_move (GTK_LAYOUT (widget->viewport), embed_gtk->child,
                          widget->allocation.x, widget->allocation.y);
      else
         gtk_layout_put (GTK_LAYOUT (widget->viewport), embed_gtk->child,
                         widget->allocation.x, widget->allocation.y);
   }

   embed_gtk->idle_id = 0;
   return FALSE;
}


/*
 * Standard Gtk+ function
 */
static void Dw_embed_gtk_size_allocate (DwWidget *widget,
                                        DwAllocation *allocation)
{
   DwEmbedGtk *embed_gtk;

   embed_gtk = DW_EMBED_GTK (widget);

   if (embed_gtk->child && widget->viewport) {
      if (allocation->width == 0 ||
          allocation->ascent + allocation->descent == 0)
         gtk_widget_hide (embed_gtk->child);
      else {
         gtk_widget_show (embed_gtk->child);

         if (allocation->width != widget->allocation.width ||
             allocation->ascent + allocation->descent
             != DW_WIDGET_HEIGHT(widget))
            /* todo: try implementing it by gtk_widget_size_allocate */
            gtk_widget_set_usize (embed_gtk->child,
                                  allocation->width,
                                  allocation->ascent + allocation->descent);

         if (allocation->x != widget->allocation.x ||
             allocation->y != widget->allocation.y) {
            /* A simple call to gtk_layout_move does not seem to work, so this
               strange idle function. */
            if (embed_gtk->idle_id == 0)
               embed_gtk->idle_id =
                  gtk_idle_add (Dw_embed_gtk_move_idle, (void*) embed_gtk);
         }
      }
   }
}


static void Dw_embed_gtk_draw (DwWidget *widget,
                               DwRectangle *area,
                               GdkEventExpose *event)
{
   /* This is the job of GtkDwViewport (resp. the base class GtkLayout). */
}


/*
 * This function is called when the embedded Gtk+ widget is destroyed.
 * Don't use this function directly!
 */
static void Dw_embed_gtk_remove_gtk (DwEmbedGtk *embed_gtk)
{
   embed_gtk->child = NULL;
   p_Dw_widget_queue_resize (DW_WIDGET (embed_gtk), 0, TRUE);
}


/*
 * This function is called when the embedded Gtk+ widget is focused and
 * will set the adjustments of the viewport.
 * Don't use this function directly!
 */
static int Dw_embed_gtk_child_focus_in (DwEmbedGtk *embed_gtk)
{
   DwWidget *widget;
   GtkLayout *layout;
   gint vx, vy, vw, vh, wx, wy, ww, wh;

   widget = DW_WIDGET (embed_gtk);
   layout = GTK_LAYOUT (widget->viewport);

   vx = layout->xoffset;
   vy = layout->yoffset;
   vw = GTK_WIDGET(layout)->allocation.width;
   vh = GTK_WIDGET(layout)->allocation.height;

   wx = widget->allocation.x;
   wy = widget->allocation.y;
   ww = widget->allocation.width;
   wh = DW_WIDGET_HEIGHT(widget);

   if (vx > wx)
      gtk_adjustment_set_value (layout->hadjustment, wx);
   else if (vx < wx + ww - vw)
      gtk_adjustment_set_value (layout->hadjustment, wx + ww - vw);

   if (vy > wy)
      gtk_adjustment_set_value (layout->vadjustment, wy);
   else if (vy < wy + wh - vh)
      gtk_adjustment_set_value (layout->vadjustment, wy + wh - vh);

   return FALSE;
}


/*
 * Add the Gtk+ widget to be embedded.
 * If there is already one, you have to destroy it before.
 */
void a_Dw_embed_gtk_add_gtk (DwEmbedGtk *embed_gtk,
                             GtkWidget *widget)
{
   /* todo: problems with reparent's? */

   g_return_if_fail (embed_gtk->child == NULL);

   embed_gtk->child = widget;

   if (DW_WIDGET(embed_gtk)->viewport)
      gtk_layout_put (GTK_LAYOUT (DW_WIDGET (embed_gtk)->viewport),
                      widget,
                      DW_WIDGET (embed_gtk)->allocation.x,
                      DW_WIDGET (embed_gtk)->allocation.y);

#if 0
   /* This is to recognize size changes of the embedded Gtk+ widget,
      but this simple implementation causes too much page rewraps, so
      it is currently deactivated */
   gtk_signal_connect_object (GTK_OBJECT (embed_gtk->child),
                              "size_request",
                              GTK_SIGNAL_FUNC (Dw_widget_queue_resize),
                              GTK_OBJECT (embed_gtk));
#endif

   /* for setting the adjustments */
   gtk_signal_connect_object (GTK_OBJECT (embed_gtk->child),
                              "focus_in_event",
                              GTK_SIGNAL_FUNC (Dw_embed_gtk_child_focus_in),
                              GTK_OBJECT (embed_gtk));

   /* todo: An idea: use "remove" signal of DwGtkContainer instead. */
   gtk_signal_connect_object (GTK_OBJECT (embed_gtk->child),
                              "destroy",
                              GTK_SIGNAL_FUNC (Dw_embed_gtk_remove_gtk),
                              GTK_OBJECT (embed_gtk));

   p_Dw_widget_queue_resize (DW_WIDGET (embed_gtk), 0, TRUE);
}
