/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */

/*  caja-side-pane.c
 *
 *  Copyright (C) 2002 Ximian Inc.
 *
 *  Caja is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License as
 *  published by the Free Software Foundation; either version 2 of the
 *  License, or (at your option) any later version.
 *
 *  Caja 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
 *  General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *  Author: Dave Camp <dave@ximian.com>
 */

#include <config.h>
#include "caja-side-pane.h"

#include <eel/eel-glib-extensions.h>
#include <eel/eel-gtk-macros.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#include <glib/gi18n.h>

typedef struct
{
    char *title;
    char *tooltip;
    GtkWidget *widget;
    GtkWidget *menu_item;
    GtkWidget *shortcut;
} SidePanel;

struct _CajaSidePaneDetails
{
    GtkWidget *notebook;
    GtkWidget *menu;

    GtkWidget *title_frame;
    GtkWidget *title_hbox;
    GtkWidget *title_label;
    GtkWidget *shortcut_box;
    GList *panels;
};

static void caja_side_pane_class_init (CajaSidePaneClass *klass);
static void caja_side_pane_init       (GObject *object);
static void caja_side_pane_dispose    (GObject *object);
static void caja_side_pane_finalize   (GObject *object);

enum
{
    CLOSE_REQUESTED,
    SWITCH_PAGE,
    LAST_SIGNAL
};

static guint signals[LAST_SIGNAL];

EEL_CLASS_BOILERPLATE (CajaSidePane, caja_side_pane, GTK_TYPE_VBOX)

static SidePanel *
panel_for_widget (CajaSidePane *side_pane, GtkWidget *widget)
{
    GList *l;
    SidePanel *panel;

    for (l = side_pane->details->panels; l != NULL; l = l->next)
    {
        panel = l->data;
        if (panel->widget == widget)
        {
            return panel;
        }
    }

    return NULL;
}

static void
side_panel_free (SidePanel *panel)
{
    g_free (panel->title);
    g_free (panel->tooltip);
    g_slice_free (SidePanel, panel);
}

static void
switch_page_callback (GtkWidget *notebook,
                      GtkWidget *page,
                      guint page_num,
                      gpointer user_data)
{
    CajaSidePane *side_pane;
    SidePanel *panel;

    side_pane = CAJA_SIDE_PANE (user_data);

    panel = panel_for_widget (side_pane,
                              gtk_notebook_get_nth_page (GTK_NOTEBOOK (side_pane->details->notebook),
                                      page_num));

    if (panel && side_pane->details->title_label)
    {
        gtk_label_set_text (GTK_LABEL (side_pane->details->title_label),
                            panel->title);
    }

    g_signal_emit (side_pane, signals[SWITCH_PAGE], 0,
                   panel ? panel->widget : NULL);
}

static void
select_panel (CajaSidePane *side_pane, SidePanel *panel)
{
    int page_num;

    page_num = gtk_notebook_page_num
               (GTK_NOTEBOOK (side_pane->details->notebook), panel->widget);
    gtk_notebook_set_current_page
    (GTK_NOTEBOOK (side_pane->details->notebook), page_num);
}

static void
caja_side_pane_size_allocate (GtkWidget *widget,
                              GtkAllocation *allocation)
{
    int width;
    GtkAllocation child_allocation, frame_allocation;
    CajaSidePane *pane;
    GtkWidget *frame;
    GtkWidget *hbox;
    GtkRequisition child_requisition;

    GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);

    pane = CAJA_SIDE_PANE(widget);
    frame = pane->details->title_frame;
    hbox = pane->details->title_hbox;

    gtk_widget_get_child_requisition (hbox, &child_requisition);
    width = child_requisition.width;

    gtk_widget_get_allocation (frame, &frame_allocation);
    child_allocation = frame_allocation;
    child_allocation.width = MAX (width, frame_allocation.width);

    gtk_widget_size_allocate (frame, &child_allocation);
}

/* initializing the class object by installing the operations we override */
static void
caja_side_pane_class_init (CajaSidePaneClass *klass)
{
    GObjectClass *gobject_class;
    GtkWidgetClass *widget_class;

    gobject_class = G_OBJECT_CLASS (klass);
    widget_class = GTK_WIDGET_CLASS (klass);

    gobject_class->finalize = caja_side_pane_finalize;
    gobject_class->dispose = caja_side_pane_dispose;
    widget_class->size_allocate = caja_side_pane_size_allocate;

    signals[CLOSE_REQUESTED] = g_signal_new
                               ("close_requested",
                                G_TYPE_FROM_CLASS (klass),
                                G_SIGNAL_RUN_LAST,
                                G_STRUCT_OFFSET (CajaSidePaneClass,
                                        close_requested),
                                NULL, NULL,
                                g_cclosure_marshal_VOID__VOID,
                                G_TYPE_NONE, 0);
    signals[SWITCH_PAGE] = g_signal_new
                           ("switch_page",
                            G_TYPE_FROM_CLASS (klass),
                            G_SIGNAL_RUN_LAST,
                            G_STRUCT_OFFSET (CajaSidePaneClass,
                                    switch_page),
                            NULL, NULL,
                            g_cclosure_marshal_VOID__OBJECT,
                            G_TYPE_NONE, 1, GTK_TYPE_WIDGET);

    g_type_class_add_private (gobject_class, sizeof (CajaSidePaneDetails));
}

static void
panel_item_activate_callback (GtkMenuItem *item,
                              gpointer user_data)
{
    CajaSidePane *side_pane;
    SidePanel *panel;

    side_pane = CAJA_SIDE_PANE (user_data);

    panel = g_object_get_data (G_OBJECT (item), "panel-item");

    select_panel (side_pane, panel);
}


static void
menu_position_under (GtkMenu *menu,
                     int *x,
                     int *y,
                     gboolean *push_in,
                     gpointer user_data)
{
    GtkWidget *widget;
    GtkAllocation allocation;

    g_return_if_fail (GTK_IS_BUTTON (user_data));
    g_return_if_fail (!gtk_widget_get_has_window (GTK_WIDGET (user_data)));

    widget = GTK_WIDGET (user_data);

    gdk_window_get_origin (gtk_widget_get_window (widget), x, y);
    gtk_widget_get_allocation (widget, &allocation);

    *x += allocation.x;
    *y += allocation.y + allocation.height;

    *push_in = FALSE;
}

static gboolean
select_button_press_callback (GtkWidget *widget,
                              GdkEventButton *event,
                              gpointer user_data)
{
    CajaSidePane *side_pane;

    side_pane = CAJA_SIDE_PANE (user_data);

    if ((event->type == GDK_BUTTON_PRESS) && event->button == 1)
    {
        GtkRequisition requisition;
        GtkAllocation allocation;
        gint width;

        gtk_widget_get_allocation (widget, &allocation);
        width = allocation.width;
        gtk_widget_set_size_request (side_pane->details->menu, -1, -1);
        gtk_widget_size_request (side_pane->details->menu, &requisition);
        gtk_widget_set_size_request (side_pane->details->menu,
                                     MAX (width, requisition.width), -1);

        gtk_widget_grab_focus (widget);

        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
        gtk_menu_popup (GTK_MENU (side_pane->details->menu),
                        NULL, NULL, menu_position_under, widget,
                        event->button, event->time);

        return TRUE;
    }
    return FALSE;
}

static gboolean
select_button_key_press_callback (GtkWidget *widget,
                                  GdkEventKey *event,
                                  gpointer user_data)
{
    CajaSidePane *side_pane;

    side_pane = CAJA_SIDE_PANE (user_data);

    if (event->keyval == GDK_space ||
            event->keyval == GDK_KP_Space ||
            event->keyval == GDK_Return ||
            event->keyval == GDK_KP_Enter)
    {
        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
        gtk_menu_popup (GTK_MENU (side_pane->details->menu),
                        NULL, NULL, menu_position_under, widget,
                        1, event->time);
        return TRUE;
    }

    return FALSE;
}

static void
close_clicked_callback (GtkWidget *widget,
                        gpointer user_data)
{
    CajaSidePane *side_pane;

    side_pane = CAJA_SIDE_PANE (user_data);

    g_signal_emit (side_pane, signals[CLOSE_REQUESTED], 0);
}

static void
menu_deactivate_callback (GtkWidget *widget,
                          gpointer user_data)
{
    GtkWidget *menu_button;

    menu_button = GTK_WIDGET (user_data);

    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (menu_button), FALSE);
}

static void
menu_detach_callback (GtkWidget *widget,
                      GtkMenu *menu)
{
    CajaSidePane *side_pane;

    side_pane = CAJA_SIDE_PANE (widget);

    side_pane->details->menu = NULL;
}

static void
caja_side_pane_init (GObject *object)
{
    CajaSidePane *side_pane;
    GtkWidget *frame;
    GtkWidget *hbox;
    GtkWidget *close_button;
    GtkWidget *select_button;
    GtkWidget *select_hbox;
    GtkWidget *arrow;
    GtkWidget *image;

    side_pane = CAJA_SIDE_PANE (object);

    side_pane->details = G_TYPE_INSTANCE_GET_PRIVATE (object, CAJA_TYPE_SIDE_PANE, CajaSidePaneDetails);

    /* The frame (really a vbox) has the border */
    frame = gtk_vbox_new (FALSE, 0);
    gtk_container_set_border_width (GTK_CONTAINER (frame), 4);
    side_pane->details->title_frame = frame;
    gtk_widget_show (frame);
    gtk_box_pack_start (GTK_BOX (side_pane), frame, FALSE, FALSE, 0);

    /* And the title_hbox is what gets the same size as the other
       headers */
    hbox = gtk_hbox_new (FALSE, 0);
    side_pane->details->title_hbox = hbox;
    gtk_widget_show (hbox);
    gtk_container_add (GTK_CONTAINER (frame), hbox);

    select_button = gtk_toggle_button_new ();
    gtk_button_set_relief (GTK_BUTTON (select_button), GTK_RELIEF_NONE);
    gtk_widget_show (select_button);

    g_signal_connect (select_button,
                      "button_press_event",
                      G_CALLBACK (select_button_press_callback),
                      side_pane);
    g_signal_connect (select_button,
                      "key_press_event",
                      G_CALLBACK (select_button_key_press_callback),
                      side_pane);

    select_hbox = gtk_hbox_new (FALSE, 0);
    gtk_widget_show (select_hbox);

    side_pane->details->title_label = gtk_label_new ("");
    eel_add_weak_pointer (&side_pane->details->title_label);

    gtk_widget_show (side_pane->details->title_label);
    gtk_box_pack_start (GTK_BOX (select_hbox),
                        side_pane->details->title_label,
                        FALSE, FALSE, 0);

    arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
    gtk_widget_show (arrow);
    gtk_box_pack_end (GTK_BOX (select_hbox), arrow, FALSE, FALSE, 0);

    gtk_container_add (GTK_CONTAINER (select_button), select_hbox);
    gtk_box_pack_start (GTK_BOX (hbox), select_button, TRUE, TRUE, 0);

    close_button = gtk_button_new ();
    gtk_button_set_relief (GTK_BUTTON (close_button), GTK_RELIEF_NONE);
    g_signal_connect (close_button,
                      "clicked",
                      G_CALLBACK (close_clicked_callback),
                      side_pane);

    gtk_widget_show (close_button);

    image = gtk_image_new_from_stock (GTK_STOCK_CLOSE,
                                      GTK_ICON_SIZE_MENU);
    gtk_widget_show (image);

    gtk_container_add (GTK_CONTAINER (close_button), image);

    gtk_box_pack_end (GTK_BOX (hbox), close_button, FALSE, FALSE, 0);

    side_pane->details->shortcut_box = gtk_hbox_new (TRUE, 0);
    gtk_widget_show (side_pane->details->shortcut_box);
    gtk_box_pack_end (GTK_BOX (hbox),
                      side_pane->details->shortcut_box,
                      FALSE, FALSE, 0);

    side_pane->details->notebook = gtk_notebook_new ();
    gtk_notebook_set_show_tabs (GTK_NOTEBOOK (side_pane->details->notebook),
                                FALSE);
    gtk_notebook_set_show_border (GTK_NOTEBOOK (side_pane->details->notebook),
                                  FALSE);
    g_signal_connect_object (side_pane->details->notebook,
                             "switch_page",
                             G_CALLBACK (switch_page_callback),
                             side_pane,
                             0);

    gtk_widget_show (side_pane->details->notebook);

    gtk_box_pack_start (GTK_BOX (side_pane), side_pane->details->notebook,
                        TRUE, TRUE, 0);

    side_pane->details->menu = gtk_menu_new ();
    g_signal_connect (side_pane->details->menu,
                      "deactivate",
                      G_CALLBACK (menu_deactivate_callback),
                      select_button);
    gtk_menu_attach_to_widget (GTK_MENU (side_pane->details->menu),
                               GTK_WIDGET (side_pane),
                               menu_detach_callback);

    gtk_widget_show (side_pane->details->menu);

    gtk_widget_set_tooltip_text (close_button,
                                 _("Close the side pane"));
}

static void
caja_side_pane_dispose (GObject *object)
{
    CajaSidePane *side_pane;

    side_pane = CAJA_SIDE_PANE (object);

    if (side_pane->details->menu)
    {
        gtk_menu_detach (GTK_MENU (side_pane->details->menu));
        side_pane->details->menu = NULL;
    }

    EEL_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
}

static void
caja_side_pane_finalize (GObject *object)
{
    CajaSidePane *side_pane;
    GList *l;

    side_pane = CAJA_SIDE_PANE (object);

    for (l = side_pane->details->panels; l != NULL; l = l->next)
    {
        side_panel_free (l->data);
    }

    g_list_free (side_pane->details->panels);

    EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
}

CajaSidePane *
caja_side_pane_new (void)
{
    return CAJA_SIDE_PANE (gtk_widget_new (caja_side_pane_get_type (), NULL));
}

void
caja_side_pane_add_panel (CajaSidePane *side_pane,
                          GtkWidget *widget,
                          const char *title,
                          const char *tooltip)
{
    SidePanel *panel;

    g_return_if_fail (side_pane != NULL);
    g_return_if_fail (CAJA_IS_SIDE_PANE (side_pane));
    g_return_if_fail (widget != NULL);
    g_return_if_fail (GTK_IS_WIDGET (widget));
    g_return_if_fail (title != NULL);
    g_return_if_fail (tooltip != NULL);

    panel = g_slice_new0 (SidePanel);
    panel->title = g_strdup (title);
    panel->tooltip = g_strdup (tooltip);
    panel->widget = widget;

    gtk_widget_show (widget);

    panel->menu_item = gtk_image_menu_item_new_with_label (title);
    gtk_widget_show (panel->menu_item);
    gtk_menu_shell_append (GTK_MENU_SHELL (side_pane->details->menu),
                           panel->menu_item);
    g_object_set_data (G_OBJECT (panel->menu_item), "panel-item", panel);

    g_signal_connect (panel->menu_item,
                      "activate",
                      G_CALLBACK (panel_item_activate_callback),
                      side_pane);

    side_pane->details->panels = g_list_append (side_pane->details->panels,
                                 panel);

    gtk_notebook_append_page (GTK_NOTEBOOK (side_pane->details->notebook),
                              widget,
                              NULL);
}

void
caja_side_pane_remove_panel (CajaSidePane *side_pane,
                             GtkWidget *widget)
{
    SidePanel *panel;
    int page_num;

    g_return_if_fail (side_pane != NULL);
    g_return_if_fail (CAJA_IS_SIDE_PANE (side_pane));
    g_return_if_fail (widget != NULL);
    g_return_if_fail (GTK_IS_WIDGET (widget));

    panel = panel_for_widget (side_pane, widget);

    g_return_if_fail (panel != NULL);

    if (panel)
    {
        page_num = gtk_notebook_page_num (GTK_NOTEBOOK (side_pane->details->notebook),
                                          widget);
        gtk_notebook_remove_page (GTK_NOTEBOOK (side_pane->details->notebook),
                                  page_num);
        gtk_container_remove (GTK_CONTAINER (side_pane->details->menu),
                              panel->menu_item);

        side_pane->details->panels =
            g_list_remove (side_pane->details->panels,
                           panel);

        side_panel_free (panel);
    }
}

void
caja_side_pane_show_panel (CajaSidePane *side_pane,
                           GtkWidget        *widget)
{
    SidePanel *panel;
    int page_num;

    g_return_if_fail (side_pane != NULL);
    g_return_if_fail (CAJA_IS_SIDE_PANE (side_pane));
    g_return_if_fail (widget != NULL);
    g_return_if_fail (GTK_IS_WIDGET (widget));

    panel = panel_for_widget (side_pane, widget);

    g_return_if_fail (panel != NULL);

    page_num = gtk_notebook_page_num (GTK_NOTEBOOK (side_pane->details->notebook),
                                      widget);
    gtk_notebook_set_current_page (GTK_NOTEBOOK (side_pane->details->notebook),
                                   page_num);
}


static void
shortcut_clicked_callback (GtkWidget *button,
                           gpointer user_data)
{
    CajaSidePane *side_pane;
    GtkWidget *page;

    side_pane = CAJA_SIDE_PANE (user_data);

    page = GTK_WIDGET (g_object_get_data (G_OBJECT (button), "side-page"));

    caja_side_pane_show_panel (side_pane, page);
}

static GtkWidget *
create_shortcut (CajaSidePane *side_pane,
                 SidePanel *panel,
                 GdkPixbuf *pixbuf)
{
    GtkWidget *button;
    GtkWidget *image;

    button = gtk_button_new ();
    gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);

    g_object_set_data (G_OBJECT (button), "side-page", panel->widget);
    g_signal_connect (button, "clicked",
                      G_CALLBACK (shortcut_clicked_callback), side_pane);

    gtk_widget_set_tooltip_text (button, panel->tooltip);

    image = gtk_image_new_from_pixbuf (pixbuf);
    gtk_widget_show (image);
    gtk_container_add (GTK_CONTAINER (button), image);

    return button;
}

void
caja_side_pane_set_panel_image (CajaSidePane *side_pane,
                                GtkWidget *widget,
                                GdkPixbuf *pixbuf)
{
    SidePanel *panel;
    GtkWidget *image;

    g_return_if_fail (side_pane != NULL);
    g_return_if_fail (CAJA_IS_SIDE_PANE (side_pane));
    g_return_if_fail (widget != NULL);
    g_return_if_fail (GTK_IS_WIDGET (widget));
    g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf));

    panel = panel_for_widget (side_pane, widget);

    g_return_if_fail (panel != NULL);

    if (pixbuf)
    {
        image = gtk_image_new_from_pixbuf (pixbuf);
        gtk_widget_show (image);
    }
    else
    {
        image = NULL;
    }

    gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (panel->menu_item),
                                   image);

    if (panel->shortcut)
    {
        gtk_widget_destroy (panel->shortcut);
        panel->shortcut = NULL;
    }

    if (pixbuf)
    {
        panel->shortcut = create_shortcut (side_pane, panel, pixbuf);
        gtk_widget_show (panel->shortcut);
        gtk_box_pack_start (GTK_BOX (side_pane->details->shortcut_box),
                            panel->shortcut,
                            FALSE, FALSE, 0);
    }
}

GtkWidget *
caja_side_pane_get_current_panel (CajaSidePane *side_pane)
{
    int index;

    index = gtk_notebook_get_current_page (GTK_NOTEBOOK (side_pane->details->notebook));
    return gtk_notebook_get_nth_page (GTK_NOTEBOOK (side_pane->details->notebook), index);
}

GtkWidget *
caja_side_pane_get_title (CajaSidePane *side_pane)
{
    return side_pane->details->title_hbox;
}