/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * This file is part of the GNOME Devtools Libraries.
 *
 * Copyright (C) 2002 Gustavo Giráldez <gustavo.giraldez@gmx.net>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "gdl-i18n.h"
#include "gdl-switcher.h"

#include "gdl-tools.h"
#include "gdl-dock-notebook.h"
#include "gdl-dock-tablabel.h"


/* Private prototypes */

static void  gdl_dock_notebook_class_init    (GdlDockNotebookClass *klass);
static void  gdl_dock_notebook_instance_init (GdlDockNotebook      *notebook);
static void  gdl_dock_notebook_set_property  (GObject              *object,
                                              guint                 prop_id,
                                              const GValue         *value,
                                              GParamSpec           *pspec);
static void  gdl_dock_notebook_get_property  (GObject              *object,
                                              guint                 prop_id,
                                              GValue               *value,
                                              GParamSpec           *pspec);

static void  gdl_dock_notebook_destroy       (GtkObject    *object);

static void  gdl_dock_notebook_add           (GtkContainer *container,
					      GtkWidget    *widget);
static void  gdl_dock_notebook_forall        (GtkContainer *container,
					      gboolean      include_internals,
					      GtkCallback   callback,
					      gpointer      callback_data);
static GType gdl_dock_notebook_child_type    (GtkContainer *container);

static void  gdl_dock_notebook_dock          (GdlDockObject    *object,
                                              GdlDockObject    *requestor,
                                              GdlDockPlacement  position,
                                              GValue           *other_data);

static void  gdl_dock_notebook_switch_page_cb  (GtkNotebook     *nb,
                                                GtkNotebookPage *page,
                                                gint             page_num,
                                                gpointer         data);

static void  gdl_dock_notebook_set_orientation (GdlDockItem     *item,
                                                GtkOrientation   orientation);
					       
static gboolean gdl_dock_notebook_child_placement (GdlDockObject    *object,
                                                   GdlDockObject    *child,
                                                   GdlDockPlacement *placement);

static void     gdl_dock_notebook_present         (GdlDockObject    *object,
                                                   GdlDockObject    *child);

static gboolean gdl_dock_notebook_reorder         (GdlDockObject    *object,
                                                   GdlDockObject    *requestor,
                                                   GdlDockPlacement  new_position,
                                                   GValue           *other_data);


/* Class variables and definitions */

enum {
    PROP_0,
    PROP_PAGE
};


/* ----- Private functions ----- */

GDL_CLASS_BOILERPLATE (GdlDockNotebook, gdl_dock_notebook, GdlDockItem, GDL_TYPE_DOCK_ITEM) ;

static void
gdl_dock_notebook_class_init (GdlDockNotebookClass *klass)
{
    static gboolean style_initialized = FALSE;
    
    GObjectClass       *g_object_class;
    GtkObjectClass     *gtk_object_class;
    GtkWidgetClass     *widget_class;
    GtkContainerClass  *container_class;
    GdlDockObjectClass *object_class;
    GdlDockItemClass   *item_class;

    g_object_class = G_OBJECT_CLASS (klass);
    gtk_object_class = GTK_OBJECT_CLASS (klass);
    widget_class = GTK_WIDGET_CLASS (klass);
    container_class = GTK_CONTAINER_CLASS (klass);
    object_class = GDL_DOCK_OBJECT_CLASS (klass);
    item_class = GDL_DOCK_ITEM_CLASS (klass);

    g_object_class->set_property = gdl_dock_notebook_set_property;
    g_object_class->get_property = gdl_dock_notebook_get_property;
    
    gtk_object_class->destroy = gdl_dock_notebook_destroy;

    container_class->add = gdl_dock_notebook_add;
    container_class->forall = gdl_dock_notebook_forall;
    container_class->child_type = gdl_dock_notebook_child_type;
    
    object_class->is_compound = TRUE;
    object_class->dock = gdl_dock_notebook_dock;
    object_class->child_placement = gdl_dock_notebook_child_placement;
    object_class->present = gdl_dock_notebook_present;
    object_class->reorder = gdl_dock_notebook_reorder;
    
    item_class->has_grip = FALSE;
    item_class->set_orientation = gdl_dock_notebook_set_orientation;    
    
    g_object_class_install_property (
        g_object_class, PROP_PAGE,
        g_param_spec_int ("page", _("Page"),
                          _("The index of the current page"),
                          0, G_MAXINT,
                          0,
                          G_PARAM_READWRITE |
                          GDL_DOCK_PARAM_EXPORT | GDL_DOCK_PARAM_AFTER));

    if (!style_initialized) {
        style_initialized = TRUE;
        
        gtk_rc_parse_string (
            "style \"gdl-dock-notebook-default\" {\n"
            "xthickness = 2\n"
            "ythickness = 2\n"
            "}\n"
            "widget_class \"*.GtkNotebook.GdlDockItem\" "
            "style : gtk \"gdl-dock-notebook-default\"\n");
    }
}

static void 
gdl_dock_notebook_notify_cb (GObject    *g_object,
                             GParamSpec *pspec,
                             gpointer    user_data) 
{
    g_return_if_fail (user_data != NULL && GDL_IS_DOCK_NOTEBOOK (user_data));

    /* chain the notify signal */
    g_object_notify (G_OBJECT (user_data), pspec->name);
}

static gboolean 
gdl_dock_notebook_button_cb (GtkWidget      *widget,
                             GdkEventButton *event,
                             gpointer        user_data)
{
    if (event->type == GDK_BUTTON_PRESS)
        GDL_DOCK_ITEM_SET_FLAGS (user_data, GDL_DOCK_USER_ACTION);
    else
        GDL_DOCK_ITEM_UNSET_FLAGS (user_data, GDL_DOCK_USER_ACTION);

    return FALSE;
}
    
static void
gdl_dock_notebook_instance_init (GdlDockNotebook *notebook)
{
    GdlDockItem *item;

    item = GDL_DOCK_ITEM (notebook);

    /* create the container notebook */
    item->child = gdl_switcher_new ();
    gtk_widget_set_parent (item->child, GTK_WIDGET (notebook));
    gtk_notebook_set_tab_pos (GTK_NOTEBOOK (item->child), GTK_POS_BOTTOM);
    g_signal_connect (item->child, "switch-page",
                      (GCallback) gdl_dock_notebook_switch_page_cb, (gpointer) item);
    g_signal_connect (item->child, "notify::page",
                      (GCallback) gdl_dock_notebook_notify_cb, (gpointer) item);
    g_signal_connect (item->child, "button-press-event",
                      (GCallback) gdl_dock_notebook_button_cb, (gpointer) item);
    g_signal_connect (item->child, "button-release-event",
                      (GCallback) gdl_dock_notebook_button_cb, (gpointer) item);
    gtk_notebook_set_scrollable (GTK_NOTEBOOK (item->child), TRUE);
    gtk_widget_show (item->child);
}

static void 
gdl_dock_notebook_set_property (GObject      *object,
                                guint         prop_id,
                                const GValue *value,
                                GParamSpec   *pspec)
{
    GdlDockItem *item = GDL_DOCK_ITEM (object);

    switch (prop_id) {
        case PROP_PAGE:
            if (item->child && GTK_IS_NOTEBOOK (item->child)) {
                gtk_notebook_set_current_page (GTK_NOTEBOOK (item->child),
                                               g_value_get_int (value));
            }
            
            break;
        default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
            break;
    }
}

static void 
gdl_dock_notebook_get_property (GObject    *object,
                                guint       prop_id,
                                GValue     *value,
                                GParamSpec *pspec)
{
    GdlDockItem *item = GDL_DOCK_ITEM (object);

    switch (prop_id) {
        case PROP_PAGE:
            if (item->child && GTK_IS_NOTEBOOK (item->child)) {
                g_value_set_int (value, gtk_notebook_get_current_page
                                 (GTK_NOTEBOOK (item->child)));
            }
            
            break;
        default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
            break;
    }
}


static void
gdl_dock_notebook_destroy (GtkObject *object)
{
    GdlDockItem *item = GDL_DOCK_ITEM (object);

    /* we need to call the virtual first, since in GdlDockDestroy our
       children dock objects are detached */
    GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));

    /* after that we can remove the GtkNotebook */
    if (item->child) {
        gtk_widget_unparent (item->child);
        item->child = NULL;
    };
}

static void
gdl_dock_notebook_switch_page_cb (GtkNotebook     *nb,
                                  GtkNotebookPage *page,
                                  gint             page_num,
                                  gpointer         data)
{
    GdlDockNotebook *notebook;
    GtkWidget       *tablabel;
    
    notebook = GDL_DOCK_NOTEBOOK (data);

    /* deactivate old tablabel */
    if (nb->cur_page) {
        tablabel = gtk_notebook_get_tab_label (
            nb, gtk_notebook_get_nth_page (
                nb, gtk_notebook_get_current_page (nb)));
        if (tablabel && GDL_IS_DOCK_TABLABEL (tablabel))
            gdl_dock_tablabel_deactivate (GDL_DOCK_TABLABEL (tablabel));
    };

    /* activate new label */
    tablabel = gtk_notebook_get_tab_label (
        nb, gtk_notebook_get_nth_page (nb, page_num));
    if (tablabel && GDL_IS_DOCK_TABLABEL (tablabel))
        gdl_dock_tablabel_activate (GDL_DOCK_TABLABEL (tablabel));

    if (GDL_DOCK_ITEM_USER_ACTION (notebook) &&
        GDL_DOCK_OBJECT (notebook)->master)
        g_signal_emit_by_name (GDL_DOCK_OBJECT (notebook)->master,
                               "layout-changed");
}

static void
gdl_dock_notebook_add (GtkContainer *container,
		       GtkWidget    *widget)
{
    g_return_if_fail (container != NULL && widget != NULL);
    g_return_if_fail (GDL_IS_DOCK_NOTEBOOK (container));
    g_return_if_fail (GDL_IS_DOCK_ITEM (widget));

    gdl_dock_object_dock (GDL_DOCK_OBJECT (container),
                          GDL_DOCK_OBJECT (widget),
                          GDL_DOCK_CENTER,
                          NULL);
}

static void
gdl_dock_notebook_forall (GtkContainer *container,
			  gboolean      include_internals,
			  GtkCallback   callback,
			  gpointer      callback_data)
{
    GdlDockItem *item;

    g_return_if_fail (container != NULL);
    g_return_if_fail (GDL_IS_DOCK_NOTEBOOK (container));
    g_return_if_fail (callback != NULL);

    if (include_internals) {
        /* use GdlDockItem's forall */
        GDL_CALL_PARENT (GTK_CONTAINER_CLASS, forall, 
                           (container, include_internals, callback, callback_data));
    }
    else {
        item = GDL_DOCK_ITEM (container);
        if (item->child)
            gtk_container_foreach (GTK_CONTAINER (item->child), callback, callback_data);
    }
}

static GType
gdl_dock_notebook_child_type (GtkContainer *container)
{
    return GDL_TYPE_DOCK_ITEM;
}
    
static void
gdl_dock_notebook_dock_child (GdlDockObject *requestor,
                              gpointer       user_data)
{
    struct {
        GdlDockObject    *object;
        GdlDockPlacement  position;
        GValue           *other_data;
    } *data = user_data;

    gdl_dock_object_dock (data->object, requestor, data->position, data->other_data);
}

static void
gdl_dock_notebook_dock (GdlDockObject    *object,
                        GdlDockObject    *requestor,
                        GdlDockPlacement  position,
                        GValue           *other_data)
{
    g_return_if_fail (GDL_IS_DOCK_NOTEBOOK (object));
    g_return_if_fail (GDL_IS_DOCK_ITEM (requestor));

    /* we only add support for GDL_DOCK_CENTER docking strategy here... for the rest
       use our parent class' method */
    if (position == GDL_DOCK_CENTER) {
        /* we can only dock simple (not compound) items */
        if (gdl_dock_object_is_compound (requestor)) {
            struct {
                GdlDockObject    *object;
                GdlDockPlacement  position;
                GValue           *other_data;
            } data;

            gdl_dock_object_freeze (requestor);
            
            data.object = object;
            data.position = position;
            data.other_data = other_data;
             
            gtk_container_foreach (GTK_CONTAINER (requestor),
                                   (GtkCallback) gdl_dock_notebook_dock_child, &data);

            gdl_dock_object_thaw (requestor);
        }
        else {
            GdlDockItem *item = GDL_DOCK_ITEM (object);
            GdlDockItem *requestor_item = GDL_DOCK_ITEM (requestor);
            gchar       *long_name, *stock_id;
            GdkPixbuf   *pixbuf_icon;
            GtkWidget   *label;
            gint         position = -1;
            
            g_object_get (requestor_item, "long-name", &long_name,
                          "stock-id", &stock_id, "pixbuf-icon", &pixbuf_icon, NULL);
            label = gdl_dock_item_get_tablabel (requestor_item);
            if (!label) {
                label = gtk_label_new (long_name);
                gdl_dock_item_set_tablabel (requestor_item, label);
            }
#if 0
            if (GDL_IS_DOCK_TABLABEL (label)) {
                gdl_dock_tablabel_deactivate (GDL_DOCK_TABLABEL (label));
                /* hide the item grip, as we will use the tablabel's */
                gdl_dock_item_hide_grip (requestor_item);
            }
#endif

            if (other_data && G_VALUE_HOLDS (other_data, G_TYPE_INT))
                position = g_value_get_int (other_data);
            
            position = gdl_switcher_insert_page (GDL_SWITCHER (item->child), 
                                                 GTK_WIDGET (requestor), label,
                                                 long_name, long_name,
                                                 stock_id, pixbuf_icon, position);
            
            GDL_DOCK_OBJECT_SET_FLAGS (requestor, GDL_DOCK_ATTACHED);
            
            /* Set current page to the newly docked widget. set current page
             * really doesn't work if the page widget is not shown
             */
            gtk_widget_show (GTK_WIDGET (requestor));
            gtk_notebook_set_current_page (GTK_NOTEBOOK (item->child),
                                           position);
            g_free (long_name);
            g_free (stock_id);
        }
    }
    else
        GDL_CALL_PARENT (GDL_DOCK_OBJECT_CLASS, dock,
                           (object, requestor, position, other_data));
}

static void
gdl_dock_notebook_set_orientation (GdlDockItem    *item,
                                   GtkOrientation  orientation)
{
    if (item->child && GTK_IS_NOTEBOOK (item->child)) {
        if (orientation == GTK_ORIENTATION_HORIZONTAL)
            gtk_notebook_set_tab_pos (GTK_NOTEBOOK (item->child), GTK_POS_TOP);
        else
            gtk_notebook_set_tab_pos (GTK_NOTEBOOK (item->child), GTK_POS_LEFT);
    }

    GDL_CALL_PARENT (GDL_DOCK_ITEM_CLASS, set_orientation, (item, orientation));
}

static gboolean 
gdl_dock_notebook_child_placement (GdlDockObject    *object,
                                   GdlDockObject    *child,
                                   GdlDockPlacement *placement)
{
    GdlDockItem      *item = GDL_DOCK_ITEM (object);
    GdlDockPlacement  pos = GDL_DOCK_NONE;
    
    if (item->child) {
        GList *children, *l;

        children = gtk_container_get_children (GTK_CONTAINER (item->child));
        for (l = children; l; l = l->next) {
            if (l->data == (gpointer) child) {
                pos = GDL_DOCK_CENTER;
                break;
            }
        }
        g_list_free (children);
    }

    if (pos != GDL_DOCK_NONE) {
        if (placement)
            *placement = pos;
        return TRUE;
    }
    else
        return FALSE;
}

static void
gdl_dock_notebook_present (GdlDockObject *object,
                           GdlDockObject *child)
{
    GdlDockItem *item = GDL_DOCK_ITEM (object);
    int i;
    
    i = gtk_notebook_page_num (GTK_NOTEBOOK (item->child),
                               GTK_WIDGET (child));
    if (i >= 0)
        gtk_notebook_set_current_page (GTK_NOTEBOOK (item->child), i);

    GDL_CALL_PARENT (GDL_DOCK_OBJECT_CLASS, present, (object, child));
}

static gboolean 
gdl_dock_notebook_reorder (GdlDockObject    *object,
                           GdlDockObject    *requestor,
                           GdlDockPlacement  new_position,
                           GValue           *other_data)
{
    GdlDockItem *item = GDL_DOCK_ITEM (object);
    gint         current_position, new_pos = -1;
    gboolean     handled = FALSE;
    
    if (item->child && new_position == GDL_DOCK_CENTER) {
        current_position = gtk_notebook_page_num (GTK_NOTEBOOK (item->child),
                                                  GTK_WIDGET (requestor));
        if (current_position >= 0) {
            handled = TRUE;
    
            if (other_data && G_VALUE_HOLDS (other_data, G_TYPE_INT))
                new_pos = g_value_get_int (other_data);
            
            gtk_notebook_reorder_child (GTK_NOTEBOOK (item->child), 
                                        GTK_WIDGET (requestor),
                                        new_pos);
        }
    }
    return handled;
}

/* ----- Public interface ----- */

GtkWidget *
gdl_dock_notebook_new (void)
{
    GdlDockNotebook *notebook;

    notebook = GDL_DOCK_NOTEBOOK (g_object_new (GDL_TYPE_DOCK_NOTEBOOK, NULL));
    GDL_DOCK_OBJECT_UNSET_FLAGS (notebook, GDL_DOCK_AUTOMATIC);
    
    return GTK_WIDGET (notebook);
}

