/*
 * panel-applet-frame.c:
 *
 * Copyright (C) 2001 Sun Microsystems, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 * Authors:
 *	Mark McLoughlin <mark@skynet.ie>
 */

#include <config.h>
#include <string.h>

#include <libbonoboui.h>
#include <gconf/gconf.h>
#include <gdk/gdk.h>
#include <gdk/gdkx.h>
#include <gdk/gdkkeysyms.h>

#include "panel-applet-frame.h"
#include "panel-gconf.h"
#include "session.h"
#include "applet.h"
#include "panel-marshal.h"

#undef PANEL_APPLET_FRAME_DEBUG

struct _PanelAppletFramePrivate {
	GNOME_Vertigo_PanelAppletShell  applet_shell; /* unused */
	Bonobo_PropertyBag              property_bag;

	AppletInfo                     *applet_info;

	gchar                          *iid;
	gboolean			moving_focus_out;

	gboolean			clean_remove;
};

static GObjectClass *parent_class;

enum {
	MOVE_FOCUS_OUT_OF_APPLET,
	LAST_SIGNAL
};

static guint panel_applet_frame_signals [LAST_SIGNAL];

void
panel_applet_frame_save_to_gconf (PanelAppletFrame *frame,
				  const char       *gconf_key)
{
	GConfClient *client;
	const char  *profile;
	const char  *temp_key;

	client  = panel_gconf_get_client ();
	profile = panel_gconf_get_profile ();

	temp_key = panel_gconf_full_key (PANEL_GCONF_APPLETS, profile, gconf_key, "bonobo-iid");
	gconf_client_set_string (client, temp_key, frame->priv->iid, NULL);
}

void
panel_applet_frame_load_from_gconf (PanelWidget *panel_widget,
				    gint         position,
				    const char  *gconf_key)
{
	GConfClient *client;
	const char  *profile;
	const char  *temp_key;
	char        *applet_iid;

	g_return_if_fail (panel_widget != NULL);
	g_return_if_fail (gconf_key != NULL);

	client  = panel_gconf_get_client ();
	profile = panel_gconf_get_profile ();

	temp_key = panel_gconf_full_key (PANEL_GCONF_APPLETS, profile, gconf_key, "bonobo-iid");
	applet_iid = gconf_client_get_string (client, temp_key, NULL);

	panel_applet_frame_load (applet_iid, panel_widget, position, gconf_key);

	g_free (applet_iid);
}

static void
popup_handle_remove (BonoboUIComponent *uic,
		     PanelAppletFrame  *frame,
		     const gchar       *verbname)
{
	AppletInfo *info = frame->priv->applet_info;
	frame->priv->applet_info = NULL;
	panel_applet_clean (info);
}

static void
popup_handle_move (BonoboUIComponent *uic,
		   PanelAppletFrame  *frame,
		   const gchar       *verbname)
{
	PanelWidget *panel;
	GtkWidget   *widget;

	g_return_if_fail (GTK_IS_WIDGET (frame));

	widget = GTK_WIDGET (frame);

	g_return_if_fail (PANEL_IS_WIDGET (widget->parent));

	panel = PANEL_WIDGET (widget->parent);

	panel_widget_applet_drag_start (panel, widget, PW_DRAG_OFF_CENTER);
}

static BonoboUIVerb popup_verbs [] = {
        BONOBO_UI_UNSAFE_VERB ("RemoveAppletFromPanel", popup_handle_remove),
        BONOBO_UI_UNSAFE_VERB ("MoveApplet",            popup_handle_move),

        BONOBO_UI_VERB_END
};

void
panel_applet_frame_load (const gchar *iid,
			 PanelWidget *panel,
			 gint         pos,
			 const char  *gconf_key)
{
	GtkWidget  *frame = NULL;
	AppletInfo *info;
	char       *real_key;

	g_return_if_fail (iid != NULL);
	g_return_if_fail (panel != NULL);

	if (gconf_key)
		real_key = g_strdup (gconf_key);
	else
		real_key = gconf_unique_key ();

	frame = panel_applet_frame_new (panel, iid, real_key);

	if (!frame)
		return;
	
	gtk_widget_show_all (frame);

	/* Pass frame as 2nd argument, since it is used in
	 * panel_remove_applets()  */
	info = panel_applet_register (frame, frame, NULL, panel, pos,
				      FALSE, APPLET_BONOBO, real_key);

	if (!info)
		g_warning (_("Cannot register control widget\n"));

	g_free (real_key);

	panel_applet_frame_set_info (PANEL_APPLET_FRAME (frame), info);
}

void
panel_applet_frame_get_expand_flags (PanelAppletFrame *frame,
				     gboolean         *expand_major,
				     gboolean         *expand_minor)
{
	gint16 retval;

	retval = bonobo_pbclient_get_short (
			frame->priv->property_bag, "panel-applet-flags", NULL);

	/*
	 * Keep in sync with panel-applet.h. Uggh.
	 */	
	*expand_major = retval & (1 << 0);
	*expand_minor = retval & (1 << 1);
}

void
panel_applet_frame_change_orient (PanelAppletFrame *frame,
				  PanelOrient       orient)
{
	bonobo_pbclient_set_short (frame->priv->property_bag, 
				   "panel-applet-orient",
				   orient,
				   NULL);
}

void
panel_applet_frame_change_size (PanelAppletFrame *frame,
				PanelSize         size)
{
	bonobo_pbclient_set_short (frame->priv->property_bag, 
				   "panel-applet-size",
				   size,
				   NULL);
}

static char *
panel_applet_frame_get_background_string (PanelAppletFrame *frame,
					  PanelWidget      *panel,
					  PanelBackType     type)
{
	char *retval = NULL;

	switch (type) {
	case PANEL_BACK_PIXMAP: {
		GdkNativeWindow pixmap_xid;

		pixmap_xid = gdk_x11_drawable_get_xid (
				GDK_DRAWABLE (GTK_WIDGET (panel)->window));

		retval = g_strdup_printf (
				"pixmap:%d,%d,%d", pixmap_xid,
				GTK_WIDGET (frame)->allocation.x,
				GTK_WIDGET (frame)->allocation.y);
		}
		break;
	case PANEL_BACK_COLOR:
		retval = g_strdup_printf (
				"color:#%.4x%.4x%.4x", 
				panel->back_color.red, 
				panel->back_color.green, 
				panel->back_color.blue);
		break;
	case PANEL_BACK_NONE:
		retval = g_strdup ("none:");
		break;
	default:
		g_assert_not_reached ();
		break;
	}

	return retval;
}

void
panel_applet_frame_change_background (PanelAppletFrame *frame,
				      PanelBackType     type)
{
	char *bg_str;

	g_return_if_fail (PANEL_IS_APPLET_FRAME (frame));
	g_return_if_fail (PANEL_IS_WIDGET (GTK_WIDGET (frame)->parent));

	bg_str = panel_applet_frame_get_background_string (
			frame, PANEL_WIDGET (GTK_WIDGET (frame)->parent), type);

	bonobo_pbclient_set_string (frame->priv->property_bag,
				    "panel-applet-background",
				    bg_str, NULL);

	g_free (bg_str);
}

void
panel_applet_frame_set_info (PanelAppletFrame *frame,
			     AppletInfo       *info)
{
	frame->priv->applet_info = info;
}

void
panel_applet_frame_set_clean_remove (PanelAppletFrame *frame,
				     gboolean          clean_remove)
{
	frame->priv->clean_remove = clean_remove;
}

gboolean
panel_applet_frame_get_clean_remove (PanelAppletFrame *frame)
{
	return frame->priv->clean_remove;
}

static void
panel_applet_frame_finalize (GObject *object)
{
	PanelAppletFrame *frame = PANEL_APPLET_FRAME (object);

	g_free (frame->priv->iid);
	frame->priv->iid = NULL;

        g_free (frame->priv);
        frame->priv = NULL;

        parent_class->finalize (object);
}

static void 
panel_applet_frame_move_focus_out_of_applet (PanelAppletFrame *frame,
					     GtkDirectionType  dir)
{
	GtkWidget *toplevel;

	frame->priv->moving_focus_out = TRUE;
	toplevel = gtk_widget_get_toplevel (GTK_WIDGET (frame));
	g_return_if_fail (toplevel != NULL);

	gtk_widget_child_focus (toplevel, dir);
	frame->priv->moving_focus_out = FALSE;
}

static gboolean
panel_applet_frame_focus (GtkWidget         *widget,
			  GtkDirectionType   dir)
{
	PanelAppletFrame *frame;

	g_return_val_if_fail (PANEL_IS_APPLET_FRAME (widget), FALSE);

	frame = PANEL_APPLET_FRAME (widget);

	if (frame->priv->moving_focus_out)
		return FALSE;

	return GTK_WIDGET_CLASS (parent_class)->focus (widget, dir);
}

static void
add_tab_bindings (GtkBindingSet	   *binding_set,
		  GdkModifierType   modifiers,
		  GtkDirectionType  direction)
{
	gtk_binding_entry_add_signal (binding_set, GDK_Tab, modifiers,
				      "move_focus_out_of_applet", 1,
				      GTK_TYPE_DIRECTION_TYPE, direction);
	gtk_binding_entry_add_signal (binding_set, GDK_KP_Tab, modifiers,
				      "move_focus_out_of_applet", 1,
				      GTK_TYPE_DIRECTION_TYPE, direction);
	gtk_binding_entry_add_signal (binding_set, GDK_ISO_Left_Tab, modifiers,
				      "move_focus_out_of_applet", 1,
				      GTK_TYPE_DIRECTION_TYPE, direction);
}

static void
panel_applet_frame_class_init (PanelAppletFrameClass *klass,
			       gpointer               dummy)
{
	GObjectClass   *gobject_class = (GObjectClass *) klass;
	GtkObjectClass *object_class = (GtkObjectClass *) klass;
	GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
	GtkBindingSet  *binding_set;

	parent_class = g_type_class_peek_parent (klass);

	gobject_class->finalize = panel_applet_frame_finalize;

	widget_class->focus = panel_applet_frame_focus;

	klass->move_focus_out_of_applet = panel_applet_frame_move_focus_out_of_applet;

	panel_applet_frame_signals [MOVE_FOCUS_OUT_OF_APPLET] =
		g_signal_new ("move_focus_out_of_applet",
			      G_TYPE_FROM_CLASS (klass),
			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
			      G_STRUCT_OFFSET (PanelAppletFrameClass, move_focus_out_of_applet),
			      NULL,
			      NULL,
			      panel_marshal_VOID__ENUM,
			      G_TYPE_NONE,
			      1,
			      GTK_TYPE_DIRECTION_TYPE);

	binding_set = gtk_binding_set_by_class (object_class);
	add_tab_bindings (binding_set, 0, GTK_DIR_TAB_FORWARD);
	add_tab_bindings (binding_set, GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD);
}

static void
panel_applet_frame_instance_init (PanelAppletFrame      *frame,
				  PanelAppletFrameClass *klass)
{
	frame->priv = g_new0 (PanelAppletFramePrivate, 1);

	frame->priv->applet_shell = CORBA_OBJECT_NIL;
	frame->priv->property_bag = CORBA_OBJECT_NIL;
	frame->priv->applet_info  = NULL;
	frame->priv->moving_focus_out = FALSE;
	frame->priv->clean_remove = FALSE;
}

GType
panel_applet_frame_get_type (void)
{
	static GType type = 0;

	if (!type) {
		static const GTypeInfo info = {
			sizeof (PanelAppletFrameClass),
			NULL,
			NULL,
			(GClassInitFunc) panel_applet_frame_class_init,
			NULL,
			NULL,
			sizeof (PanelAppletFrame),
			0,
			(GInstanceInitFunc) panel_applet_frame_instance_init,
			NULL
		};

		type = g_type_register_static (GTK_TYPE_EVENT_BOX,
					       "PanelAppletFrame",
					       &info, 0);
	}

	return type;
}

#if 0
static void
panel_applet_frame_get_applet_shell (Bonobo_Control control)
{
	CORBA_Environment              env;
	GNOME_Vertigo_PanelAppletShell retval;

	CORBA_exception_init (&env);

	retval = Bonobo_Unknown_queryInterface (control, 
						"IDL:GNOME/Vertigo/PanelAppletShell:1.0",
						&env);
	if (BONOBO_EX (&env)) {
		g_warning (_("Unable to obtain AppletShell interface from control\n"));

		retval = CORBA_OBJECT_NIL;
	}

	CORBA_exception_free (&env);

	return retval;
}
#endif

static G_CONST_RETURN char *
panel_applet_frame_get_orient_string (PanelAppletFrame *frame,
				      PanelWidget      *panel)
{
	PanelOrient  orient;
	const char  *retval = NULL;

	orient = panel_widget_get_applet_orient (panel);

	switch (orient) {
	case PANEL_ORIENT_UP:
		retval = "up";
		break;
	case PANEL_ORIENT_DOWN:
		retval = "down";
		break;
	case PANEL_ORIENT_LEFT:
		retval = "left";
		break;
	case PANEL_ORIENT_RIGHT:
		retval = "right";
		break;
	default:
		g_assert_not_reached ();
		break;
	}

	return retval;
}

static G_CONST_RETURN char *
panel_applet_frame_get_size_string (PanelAppletFrame *frame,
				    PanelWidget      *panel)
{
	const char *retval = NULL;

	switch (panel->sz) {
	case PANEL_SIZE_XX_SMALL:
		retval = "xx-small";
		break;
	case PANEL_SIZE_X_SMALL:
		retval = "x-small";
		break;
	case PANEL_SIZE_SMALL:
		retval = "small";
		break;
	case PANEL_SIZE_MEDIUM:
		retval = "medium";
		break;
	case PANEL_SIZE_LARGE:
		retval = "large";
		break;
	case PANEL_SIZE_X_LARGE:
		retval = "x-large";
		break;
	case PANEL_SIZE_XX_LARGE:
		retval = "xx-large";
		break;
	default:
		g_assert_not_reached ();
		break;
	}

	return retval;
}

static char *
panel_applet_frame_construct_moniker (PanelAppletFrame *frame,
				      PanelWidget      *panel,
				      const char       *iid,
				      const char       *gconf_key)
{
	char *retval;
	char *bg_str;

	bg_str = panel_applet_frame_get_background_string (frame, panel, panel->back_type);

	retval = g_strdup_printf (
			"%s!prefs_key=/apps/panel/profiles/%s/applets/%s/prefs;"
			"background=%s;orient=%s;size=%s",
			iid, panel_gconf_get_profile (), gconf_key, bg_str,
			panel_applet_frame_get_orient_string (frame, panel),
			panel_applet_frame_get_size_string (frame, panel));

	g_free (bg_str);

	return retval;
}

GtkWidget *
panel_applet_frame_construct (PanelAppletFrame *frame,
			      PanelWidget      *panel,
			      const char       *iid,
			      const char       *gconf_key)
{
	BonoboControlFrame *control_frame;
	Bonobo_Control      control;
	BonoboUIComponent  *ui_component;
	CORBA_Environment   ev;
	GtkWidget          *widget;
	char               *moniker;

	moniker = panel_applet_frame_construct_moniker (frame, panel, iid, gconf_key);

	/* FIXME: this should really use bonobo_get_object_async */
	CORBA_exception_init (&ev);

	control = bonobo_get_object (
		moniker, "IDL:Bonobo/Control:1.0", &ev);

	g_free (moniker);

	if (BONOBO_EX (&ev)) {
		GtkWidget *dialog;
		char *err = bonobo_exception_get_text (&ev);
		char *txt = g_strdup_printf (
			_("There was a problem loading applet '%s': %s"),
			  iid, err);
	
		dialog = gtk_message_dialog_new (
			NULL,
			GTK_DIALOG_DESTROY_WITH_PARENT,
			GTK_MESSAGE_ERROR,
			GTK_BUTTONS_OK,
			txt);
		g_signal_connect (dialog, "response",
				  G_CALLBACK (gtk_widget_destroy), NULL);
	
		gtk_widget_show (dialog);

		CORBA_exception_free (&ev);

		g_free (txt);
		g_free (err);

		return NULL;
	}
	
	CORBA_exception_free (&ev);

	widget = bonobo_widget_new_control_from_objref (control, CORBA_OBJECT_NIL);

	bonobo_object_release_unref (control, NULL);

	if (!widget) {
		g_warning (G_STRLOC ": failed to load applet %s", iid);
		return NULL;
	}

	frame->priv->iid = g_strdup (iid);

	control_frame = bonobo_widget_get_control_frame (BONOBO_WIDGET (widget));

	control = bonobo_control_frame_get_control (control_frame);

	frame->priv->property_bag = 
		bonobo_control_frame_get_control_property_bag (control_frame, NULL);

	ui_component = bonobo_control_frame_get_popup_component (control_frame, NULL);

	bonobo_ui_util_set_ui (ui_component, DATADIR, "GNOME_Panel_Popup.xml", "panel", NULL);

	bonobo_ui_component_add_verb_list_with_data (ui_component, popup_verbs, frame);

	gtk_container_add (GTK_CONTAINER (frame), widget);

	return widget;
}

GtkWidget *
panel_applet_frame_new (PanelWidget *panel,
			const char  *iid,
			const char  *gconf_key)
{
	PanelAppletFrame *frame;

	g_return_val_if_fail (iid != NULL && gconf_key != NULL, NULL);

	frame = g_object_new (PANEL_TYPE_APPLET_FRAME, NULL);

	if (!panel_applet_frame_construct (frame, panel, iid, gconf_key)) {
		gtk_object_sink (GTK_OBJECT (frame));
		return NULL;
	}

	return GTK_WIDGET (frame);
}
