/*
 * gnc-main-window.c -- GtkWindow which represents the
 *	GnuCash main window.
 *
 * Copyright (C) 2003 Jan Arne Petersen <jpetersen@uni-bonn.de>
 * Copyright (C) 2003 David Hampton <hampton@employees.org>
 *
 * This program 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.
 *
 * This program 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, contact:
 *
 * Free Software Foundation           Voice:  +1-617-542-5942
 * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
 * Boston, MA  02110-1301,  USA       gnu@gnu.org
 */

#include <config.h>

#include <gtk/gtk.h>

#include "gnc-embedded-window.h"

#include "gnc-engine.h"
#include "gnc-filepath-utils.h"
#include "gnc-gnome-utils.h"
#include "gnc-gobject-utils.h"
#include "gnc-gui-query.h"
#include "gnc-plugin.h"
#include "gnc-plugin-manager.h"
#include "gnc-ui.h"
#include "gnc-window.h"
#include "dialog-utils.h"
#include "gnc-gtk-utils.h"

/** Names of signals generated by the embedded window. */
enum
{
    PAGE_CHANGED,
    LAST_SIGNAL
};

/* Static Globals *******************************************************/

/** The debugging module that this .o belongs to.  */
static QofLogModule log_module = GNC_MOD_GUI;


/* Declarations *********************************************************/
static void gnc_embedded_window_constructed (GObject *object);
static void gnc_embedded_window_finalize (GObject *object);
static void gnc_embedded_window_dispose (GObject *object);

static void gnc_window_embedded_window_init (GncWindowInterface *iface);

static void gnc_embedded_window_setup_window (GncEmbeddedWindow *window);

/** The instance private data for an embedded window object. */
typedef struct GncEmbeddedWindowPrivate
{
    /** The dock (vbox) at the top of the window containing the menubar
     *  and toolbar.  These items are generated bu the UI manager and
     *  stored here when the UI manager provides them to the main
     *  window. */
    GtkWidget *menu_dock;
    /** The menubar */
    GtkWidget *menubar;
    /** The menubar_model */
    GMenuModel *menubar_model;
    /** The toolbar. This pointer provides easy access for
     * showing/hiding the toolbar. */
    GtkWidget *toolbar;
    /** A pointer to the status bar at the bottom edge of the window.
     *  This pointer provides easy access for updating/showing/hiding
     *  the status bar. */
    GtkWidget *statusbar;

    /** The group of all actions provided by the embedded window itself.
     *  This does not include any action provided by menu or content
     *  plugins. */
    GSimpleActionGroup *simple_action_group;

    /** The accelerator group of all actions provided by the embedded
     *  window. */
    GtkAccelGroup *accel_group;

    /** The currently selected page. */
    GncPluginPage *page;
    /** The parent of this embedded "window".  This points to a real
     *  GtkWindow widget. */
    GtkWidget *parent_window;
} GncEmbeddedWindowPrivate;

G_DEFINE_TYPE_WITH_CODE(GncEmbeddedWindow, gnc_embedded_window, GTK_TYPE_BOX,
                        G_ADD_PRIVATE(GncEmbeddedWindow)
                        G_IMPLEMENT_INTERFACE(GNC_TYPE_WINDOW,
                                              gnc_window_embedded_window_init))

#define GNC_EMBEDDED_WINDOW_GET_PRIVATE(o)  \
   ((GncEmbeddedWindowPrivate*)gnc_embedded_window_get_instance_private((GncEmbeddedWindow*)o))

/** A holding place for all the signals generated by the embedded window
 *  code. */
static guint embedded_window_signals[LAST_SIGNAL] = { 0 };

/*  Display a data plugin page in a window. */
void
gnc_embedded_window_open_page (GncEmbeddedWindow *window,
                               GncPluginPage *page)
{
    GncEmbeddedWindowPrivate *priv;

    g_return_if_fail (GNC_IS_EMBEDDED_WINDOW (window));
    g_return_if_fail (GNC_IS_PLUGIN_PAGE (page));
    priv = GNC_EMBEDDED_WINDOW_GET_PRIVATE(window);
    g_return_if_fail (priv->page == NULL);

    ENTER("window %p, page %p", window, page);
    priv->page = page;
    page->window = GTK_WIDGET(window);
    page->notebook_page = gnc_plugin_page_create_widget (page);

    gtk_box_pack_end(GTK_BOX(window), page->notebook_page, TRUE, TRUE, 2);
    gnc_plugin_page_inserted (page);
    LEAVE(" ");
}


/*  Remove a data plugin page from a window. */
void
gnc_embedded_window_close_page (GncEmbeddedWindow *window,
                                GncPluginPage *page)
{
    GncEmbeddedWindowPrivate *priv;

    g_return_if_fail (GNC_IS_EMBEDDED_WINDOW (window));
    g_return_if_fail (GNC_IS_PLUGIN_PAGE (page));
    priv = GNC_EMBEDDED_WINDOW_GET_PRIVATE(window);
    g_return_if_fail (priv->page == page);

    ENTER("window %p, page %p", window, page);

    if (!page->notebook_page)
    {
        LEAVE("no displayed widget");
        return;
    }

    gtk_container_remove (GTK_CONTAINER(window), GTK_WIDGET(page->notebook_page));
    priv->page = NULL;
    gnc_plugin_page_removed (page);

    gnc_plugin_page_destroy_widget (page);
    g_object_unref(page);
    LEAVE(" ");
}


/*  Retrieve the plugin that is embedded in the specified window. */
GncPluginPage *
gnc_embedded_window_get_page (GncEmbeddedWindow *window)
{
    GncEmbeddedWindowPrivate *priv;

    priv = GNC_EMBEDDED_WINDOW_GET_PRIVATE(window);
    return priv->page;
}


/** Initialize the class for a new gnucash embedded window.  This will
 *  set up any function pointers that override functions in the parent
 *  class.
 *
 *  @param klass The new class structure created by the object system.
 */
static void
gnc_embedded_window_class_init (GncEmbeddedWindowClass *klass)
{
    GObjectClass *object_class;
    ENTER("klass %p", klass);
    object_class = G_OBJECT_CLASS (klass);

    object_class->constructed = gnc_embedded_window_constructed;
    object_class->finalize = gnc_embedded_window_finalize;
    object_class->dispose = gnc_embedded_window_dispose;

    /**
     * GncEmbeddedWindow::page_changed:
     * @param window: the #GncEmbeddedWindow
     * @param page: the #GncPluginPage
     *
     * The "page_changed" signal is emitted when a new page is
     * selected in the notebook of a GncEmbeddedWindow. This can be
     * used to adjust menu actions based upon which page is
     * currently displayed in a window.
     */
     embedded_window_signals[PAGE_CHANGED] =
        g_signal_new ("page_changed",
                      G_OBJECT_CLASS_TYPE (object_class),
                      G_SIGNAL_RUN_FIRST,
                      G_STRUCT_OFFSET (GncEmbeddedWindowClass, page_changed),
                      NULL, NULL,
                      g_cclosure_marshal_VOID__OBJECT,
                      G_TYPE_NONE, 1,
                      G_TYPE_OBJECT);

    LEAVE(" ");
}


/** Initialize a new instance of a gnucash embedded window.  This
 *  function initializes the object private storage space.
 *
 *  @param view The new object instance created by the object system.
 *
 *  */
static void
gnc_embedded_window_init (GncEmbeddedWindow *window)
{
    ENTER("window %p", window);

    gtk_orientable_set_orientation (GTK_ORIENTABLE(window), GTK_ORIENTATION_VERTICAL);

    // Set the name for this dialog so it can be easily manipulated with css
    gtk_widget_set_name (GTK_WIDGET(window), "gnc-id-embedded-window");

    gnc_embedded_window_setup_window (window);

    LEAVE(" ");
}

/** The object has been fully constructed.
 * This function adds the object to the tracking system.
 *
 *  @param obj The new object instance created by the object
 *  system.
 */
 static void
gnc_embedded_window_constructed (GObject *obj)
{
    gnc_gobject_tracking_remember(obj);

    G_OBJECT_CLASS (gnc_embedded_window_parent_class)->constructed (obj);
}

/** Finish destruction of an embedded window.
 *
 *  @object The window being destroyed. */
static void
gnc_embedded_window_finalize (GObject *object)
{
    g_return_if_fail (object != NULL);
    g_return_if_fail (GNC_IS_EMBEDDED_WINDOW (object));

    ENTER("object %p", object);
    gnc_gobject_tracking_forget(object);
    G_OBJECT_CLASS (gnc_embedded_window_parent_class)->finalize (object);
    LEAVE(" ");
}


/** Begin destruction of an embedded window.  This function should
 *  release all objects referenced by the window (i.e. the page).
 *
 *  @object The window being destroyed. */
static void
gnc_embedded_window_dispose (GObject *object)
{
    GncEmbeddedWindow *window;
    GncEmbeddedWindowPrivate *priv;

    g_return_if_fail (object != NULL);
    g_return_if_fail (GNC_IS_EMBEDDED_WINDOW (object));

    ENTER("object %p", object);
    window = GNC_EMBEDDED_WINDOW (object);
    priv = GNC_EMBEDDED_WINDOW_GET_PRIVATE(window);

    if (priv->page)
    {
        DEBUG("unreffing page %p (count currently %d)", priv->page,
              G_OBJECT(priv->page)->ref_count);
        g_object_unref(priv->page);
        priv->page = NULL;
    }

    G_OBJECT_CLASS (gnc_embedded_window_parent_class)->dispose (object);
    LEAVE(" ");
}


/** Initialize the data structures of a gnucash embedded window.
 *
 *  @param window The object to initialize. */
static void
gnc_embedded_window_setup_window (GncEmbeddedWindow *window)
{
    GncEmbeddedWindowPrivate *priv;

    ENTER("window %p", window);
    priv = GNC_EMBEDDED_WINDOW_GET_PRIVATE(window);

    /* Create widgets and add them to the window */
    gtk_widget_show (GTK_WIDGET(window));

    priv->menu_dock = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
    gtk_box_set_homogeneous (GTK_BOX (priv->menu_dock), FALSE);
    gtk_widget_show (priv->menu_dock);
    gtk_box_pack_start (GTK_BOX (window), priv->menu_dock, FALSE, TRUE, 0);

    priv->statusbar = gtk_statusbar_new ();
    gtk_widget_show (priv->statusbar);
    gtk_box_pack_end (GTK_BOX (window), priv->statusbar, FALSE, TRUE, 0);

    priv->simple_action_group = NULL;
    LEAVE(" ");
}


/** Create a new gnc embedded window plugin. */
GncEmbeddedWindow *
gnc_embedded_window_new (const gchar *action_group_name,
                         GActionEntry *action_entries,
                         gint n_action_entries,
                         const gchar *ui_filename,
                         GtkWidget *enclosing_win,
                         gboolean add_accelerators,
                         gpointer user_data)
{
    GncEmbeddedWindowPrivate *priv;
    GncEmbeddedWindow *window;
    gchar *ui_fullname;
    GError *error = NULL;
    GtkBuilder *builder;

    ENTER("group %s, first %p, num %d, ui file %s, parent %p, add accelerators %d, user data %p",
          action_group_name, action_entries, n_action_entries, ui_filename,
          enclosing_win, add_accelerators, user_data);

    window = g_object_new (GNC_TYPE_EMBEDDED_WINDOW, NULL);
    priv = GNC_EMBEDDED_WINDOW_GET_PRIVATE(window);

    builder = gtk_builder_new ();
    gtk_builder_set_translation_domain (builder, PROJECT_NAME);

    ui_fullname = g_strconcat (GNUCASH_RESOURCE_PREFIX "/", ui_filename, NULL);

    gtk_builder_add_from_resource (builder, ui_fullname, &error);

    if (error)
    {
        g_critical ("Failed to load, Error %s", error->message);
        g_error_free (error);
        return NULL;
    }

    priv->menubar_model = (GMenuModel *)gtk_builder_get_object (builder, "embeddedwin-menu");

    priv->menubar = gtk_menu_bar_new_from_model (priv->menubar_model);
    gtk_container_add (GTK_CONTAINER(priv->menu_dock), priv->menubar);
    gtk_widget_show (GTK_WIDGET(priv->menubar));

    priv->toolbar = (GtkWidget *)gtk_builder_get_object (builder, "embeddedwin-toolbar");
    g_object_set (priv->toolbar, "toolbar-style", GTK_TOOLBAR_BOTH, NULL);
    gtk_container_add (GTK_CONTAINER(priv->menu_dock), GTK_WIDGET(priv->toolbar));
    gtk_widget_show (GTK_WIDGET(priv->toolbar));

    g_object_unref (builder);

    priv->simple_action_group = g_simple_action_group_new ();

    g_action_map_add_action_entries (G_ACTION_MAP(priv->simple_action_group),
                                     action_entries,
                                     n_action_entries,
                                     user_data);

    gtk_widget_insert_action_group (GTK_WIDGET(window), "embeddedwin",
                                    G_ACTION_GROUP(priv->simple_action_group));

    priv->parent_window = enclosing_win;

    // need to add the accelerator keys
    priv->accel_group = gtk_accel_group_new ();
    gtk_window_add_accel_group (GTK_WINDOW(enclosing_win), priv->accel_group);
    gnc_add_accelerator_keys_for_menu (GTK_WIDGET(priv->menubar), priv->menubar_model, priv->accel_group);

    g_free (ui_fullname);
    LEAVE("window %p", window);
    return window;
}


/** Retrieve the gtk window associated with an embedded window object.
 *  This function is called via a vector off a generic window
 *  interface.
 *
 *  @param window_in A pointer to a generic window. */
static GtkWindow *
gnc_embedded_window_get_gtk_window (GncWindow *window_in)
{
    GncEmbeddedWindow *window;
    GncEmbeddedWindowPrivate *priv;

    g_return_val_if_fail (GNC_IS_EMBEDDED_WINDOW (window_in), NULL);

    window = GNC_EMBEDDED_WINDOW(window_in);
    priv = GNC_EMBEDDED_WINDOW_GET_PRIVATE(window);
    return GTK_WINDOW(priv->parent_window);
}


/** Retrieve the status bar associated with an embedded window object.
 *  This function is called via a vector off a generic window
 *  interface.
 *
 *  @param window_in A pointer to a generic window. */
static GtkWidget *
gnc_embedded_window_get_statusbar (GncWindow *window_in)
{
    GncEmbeddedWindowPrivate *priv;
    GncEmbeddedWindow *window;

    g_return_val_if_fail (GNC_IS_EMBEDDED_WINDOW (window_in), NULL);

    window = GNC_EMBEDDED_WINDOW(window_in);
    priv = GNC_EMBEDDED_WINDOW_GET_PRIVATE(window);
    return priv->statusbar;
}


/** Retrieve the menu bar associated with an embedded window object.
 *  This function is called via a vector off a generic window
 *  interface.
 *
 *  @param window_in A pointer to a generic window. */
static GtkWidget *
gnc_embedded_window_get_menubar (GncWindow *window)
{
    GncEmbeddedWindowPrivate *priv;

    g_return_val_if_fail (GNC_IS_EMBEDDED_WINDOW(window), NULL);

    priv = GNC_EMBEDDED_WINDOW_GET_PRIVATE(window);

    return priv->menubar;
}

/** Retrieve the tool bar associated with an embedded window object.
 *  This function is called via a vector off a generic window
 *  interface.
 *
 *  @param window_in A pointer to a generic window. */
static GtkWidget *
gnc_embedded_window_get_toolbar (GncWindow *window)
{
    GncEmbeddedWindowPrivate *priv;

    g_return_val_if_fail (GNC_IS_EMBEDDED_WINDOW(window), NULL);

    priv = GNC_EMBEDDED_WINDOW_GET_PRIVATE(window);

    return priv->toolbar;
}

/** Retrieve the menubar model associated with an embedded window object.
 *  This function is called via a vector off a generic window
 *  interface.
 *
 *  @param window_in A pointer to a generic window. */
static GMenuModel *
gnc_embedded_window_get_menubar_model (GncWindow *window)
{
    GncEmbeddedWindowPrivate *priv;

    g_return_val_if_fail (GNC_IS_EMBEDDED_WINDOW(window), NULL);

    priv = GNC_EMBEDDED_WINDOW_GET_PRIVATE(window);

    return priv->menubar_model;
}

/** Retrieve the accelerator group associated with an embedded window object.
 *  This function is called via a vector off a generic window
 *  interface.
 *
 *  @param window_in A pointer to a generic window. */
static GtkAccelGroup *
gnc_embedded_window_get_accel_group (GncWindow *window)
{
    GncEmbeddedWindowPrivate *priv;

    g_return_val_if_fail (GNC_IS_EMBEDDED_WINDOW(window), NULL);

    priv = GNC_EMBEDDED_WINDOW_GET_PRIVATE(window);

    return priv->accel_group;
}

/** Initialize the generic window interface for an embedded window.
 *
 *  @param iface A pointer to the interface data structure to
 *  populate. */
static void
gnc_window_embedded_window_init (GncWindowInterface *iface)
{
    iface->get_gtk_window      = gnc_embedded_window_get_gtk_window;
    iface->get_statusbar       = gnc_embedded_window_get_statusbar;
    iface->get_menubar         = gnc_embedded_window_get_menubar;
    iface->get_toolbar         = gnc_embedded_window_get_toolbar;
    iface->get_menubar_model   = gnc_embedded_window_get_menubar_model;
    iface->get_accel_group     = gnc_embedded_window_get_accel_group;
}
