/**********************************************************************
 
	Copyright (C) 2003-2004
	Hirohisa MORI <joshua@nichibun.ac.jp>
	Tomoki SEKIYAMA <sekiyama@yahoo.co.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	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.

**********************************************************************/



#include "v/VWindow.h"
#include "v/VDisplay.h"
#include "machine/v_object.h"

extern "C" {
#include <gdk/gdkkeysyms.h>
#include "memory_debug.h"
void vobject_flush_event();


}


GtkMenuBar * make_menu_items(VMenuItem *items, GtkAccelGroup *accel_group, VWindow *vwindow);
void set_menu_item_status(VMenuItem *item);

// ========================================
// Default & Cancel Button shortcut-key control
// ========================================

struct ButtonList {
	int		type;	// 1 for return, 2 for escape
	VInfo *		btn;
	ButtonList *	next;
};

extern "C" void
add_shortcut_key(GtkWidget *window, GtkWidget *widget, int type)
{
	window = gtk_widget_get_toplevel(window);
	ButtonList *list = (ButtonList*)g_object_get_data(G_OBJECT(window), "def_btns");
	ButtonList *org = list;
	ButtonList *newList = new ButtonList;
	for ( ; list ; list = list->next )
		if ( list->btn == widget && list->type == type )
			return;
	newList->type = type;
	newList->btn = widget;
	newList->next = org;
	g_object_set_data(G_OBJECT(window), "def_btns", newList);
}

extern "C" void
remove_shortcut_key(GtkWidget *window, GtkWidget *widget, int type)
{
	window = gtk_widget_get_toplevel(window);
	ButtonList *list, *prev = 0;
	list = (ButtonList*)g_object_get_data(G_OBJECT(window), "def_btns");
	for ( ; list ; prev = list, list = list->next ) {
		if ( list->btn == widget && list->type == type ) {
			if ( prev )
				prev->next = list->next;
			else
				g_object_set_data(G_OBJECT(window), "def_btns", list->next);
			delete list;
			break;
		}
	}
}

struct SET_BUTTON_STATE_NORMAL_ARG
{
	GtkWidget *widget;
	guint	timeout_id;
};

extern "C" void
set_button_state_normal(SET_BUTTON_STATE_NORMAL_ARG *arg)
{
	gtk_widget_set_state(arg->widget, GTK_STATE_NORMAL);
	gtk_timeout_remove(arg->timeout_id);
	delete arg;
}

extern "C" gboolean
key_press_event(GtkWidget *widget, GdkEventKey *key, gpointer *obj)
{
	ButtonList *list;
	SET_BUTTON_STATE_NORMAL_ARG *arg;
	int type = 0;
	
	switch ( key->keyval ) {
	  case GDK_Return:
		if ( GTK_IS_ENTRY(gtk_window_get_focus(GTK_WINDOW(widget))) )
			type = 1;
		break;
	  case GDK_Escape:
		type = 2;
		break;
	}
	if ( type ) {
		list = (ButtonList*)g_object_get_data(G_OBJECT(widget), "def_btns");
		for ( ; list ; list = list->next ) {
			if ( list->type == type && GTK_WIDGET_MAPPED(list->btn)
						&& GTK_WIDGET_SENSITIVE(list->btn) ) {
				gtk_widget_set_state(list->btn, GTK_STATE_ACTIVE);
				arg = new SET_BUTTON_STATE_NORMAL_ARG;
				arg->widget = list->btn;
				arg->timeout_id = gtk_timeout_add(
					100, (GtkFunction)set_button_state_normal, arg);
				gtk_button_clicked(GTK_BUTTON(list->btn));
				return true;
			}
		}
	}
	return false;
}

// ========================================



extern "C" gboolean
delete_event(GtkWidget *widget, GdkEvent *event, gpointer *obj)
{
	((VWindow *)obj)->attempt_close();
	return true; 	// window is destroyed in attempt close
}

extern "C" VInfo*
v_window_create_do(
	VWindow* arg,
	int category,
	VCustomizedMenuBar **bar,
	short padding_h)
{
	VInfo* info = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	g_object_ref(info);
	g_signal_connect(info, "delete_event",
		GTK_SIGNAL_FUNC(delete_event), arg);
	g_signal_connect(info, "key_press_event",
		GTK_SIGNAL_FUNC(key_press_event), arg);
	
	GtkWidget *box1 = gtk_vbox_new(FALSE, 0);
	gtk_container_add(GTK_CONTAINER(info), box1);
	gtk_widget_show(box1);
	
	*bar = VCustomizedMenuBar::create(category, arg);
	if ( *bar ) {
		GtkAccelGroup *accel_group = gtk_accel_group_new();
		gtk_window_add_accel_group(GTK_WINDOW(info), accel_group);
		
		GtkMenuBar *menubar = VCustomizedMenuBarImp::make_menu_items(
			(*bar)->get_items(), accel_group, arg, info);
		gtk_box_pack_start(GTK_BOX(box1), GTK_WIDGET(menubar), FALSE, FALSE, 0);
		
	}
	else {
		*bar = 0;
		gtk_widget_set_size_request(info, V_WINDOW_MIN_WIDTH, V_WINDOW_MIN_HEIGHT);
	}
	
	GtkWidget *box2 = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(box1), box2, TRUE, TRUE, padding_h);
	gtk_widget_show(box2);
	
	return info;
}

VExError
VWindow::create_do(const VObjectStatus* s, int flags, 
		VObject * nmp,void * arg)
{
	category = *(int*)arg;
	info = v_serialized_exec_func(v_window_create_do,
		this, category, &menu_bar, (flags&VSF_PADDING) ? s->padding.h : (short)0);
	sts.min_size = (VSize){V_WINDOW_MIN_WIDTH, V_WINDOW_MIN_HEIGHT};
	window_close_handler = 0;

	return return_create_do(this,nmp,&sts,s,flags);
}

void
VWindow::destroy_do(VObject * nmp)
{
	v_serialized_exec_sub(gtk_widget_hide, info);
	vobject_layout();
	nmp->remove_child_do(this);
	v_serialized_exec_sub(gtk_widget_destroy,info);
	v_serialized_exec_sub(g_object_unref, info);
	if ( menu_bar )
		delete menu_bar;
}

VWindow::~VWindow()
{
}

VExError
VWindow::get_status(VObjectStatus *s, int flags) const
{
	VExError err = initial_VExError(V_ER_NO_ERR,flags,0);
	V_OP_START_EX
	
	if ( flags & VSF_ATTR ) {
		s->attr = sts.attr;
		if ( v_serialized_exec_func(gtk_window_get_resizable,GTK_WINDOW(info)) )
			s->attr |= resizable;
		else
			s->attr &= ~resizable;
		flags &= ~VSF_ATTR;
	}
	if ( flags & VSF_VISIBLE ) {
		s->visible = GTK_WIDGET_VISIBLE(info);
		flags &= ~VSF_VISIBLE;
	}
	if ( flags & VSF_ENABLED ) {
		s->enabled = GTK_WIDGET_SENSITIVE(info);
		flags &= ~VSF_ENABLED;
	}
	if ( flags & VSF_POSITION ) {
		gint x,y;
		v_serialized_exec_sub(gtk_window_get_position, GTK_WINDOW(info), &x, &y);
		s->position.x = x;
		s->position.y = y;
		flags &= ~VSF_POSITION;
	}
	if ( flags & VSF_SIZE ) {
		gint w, h;
		v_serialized_exec_sub(gtk_window_get_size, GTK_WINDOW(info), &w, &h);
		s->size.w = w;
		s->size.h = h;
		flags &= ~VSF_SIZE;
	}
	err.subcode1 = flags;

	VExError err2 = VObject::get_status(s,flags);
	if ( err2.code )
		err = merge_VExError_vstatus_type(err,err2);
	
	V_OP_END
	return err;
};

VExError
VWindow::set_status(const VObjectStatus *s, int flags)
{
	V_OP_START_EX
	
	VExError err = VObject::set_status(s,flags);
	if ( err.code ) {
		V_OP_END
		return err;
	}
	
	if ( flags & VSF_ATTR ) {
		v_serialized_exec_sub(gtk_window_set_resizable, GTK_WINDOW(info), s->attr & resizable);
		v_serialized_exec_sub(gtk_window_set_modal, GTK_WINDOW(info), s->attr & modal);
		err.subcode1 &= ~VSF_ATTR;
	}
	if ( flags & VSF_DESC ) {
		if ( sts.descriptor ) {
			v_serialized_exec_sub(gtk_window_set_title,
				GTK_WINDOW(info), (const gchar*)ucd_n_string(sts.descriptor));
		}
		else {
			v_serialized_exec_sub(gtk_window_set_title, GTK_WINDOW(info), "");
		}
		err.subcode1 &= ~VSF_DESC;
	}
	if ( flags & VSF_ENABLED ) {
		if ( s->enabled )
			if ( sts.visible )
				v_serialized_exec_sub(gtk_window_present, GTK_WINDOW(info));
		err.subcode1 &= ~VSF_ENABLED;
	}
	if ( flags & VSF_PADDING ) {
		if ( sts.children )
			child_status_changed(sts.children->object, sts.children->object->get_info_this());
		err.subcode1 &= ~VSF_PADDING;
	}
	if ( flags & VSF_POSITION ) {
		v_serialized_exec_sub(gtk_window_move, GTK_WINDOW(info), (int)s->position.x, (int)s->position.y);
		err.subcode1 &= ~VSF_POSITION;
	}
	if ( flags & VSF_MIN_SIZE ) {
		gint w = V_WINDOW_MIN_WIDTH, h = V_WINDOW_MIN_HEIGHT;
		if ( s->min_size.w != V_DEFAULT_SIZE )
			w = s->min_size.w;
		if ( s->min_size.h != V_DEFAULT_SIZE )
			h = s->min_size.h;
		v_serialized_exec_sub(gtk_widget_set_size_request, info, w, h);
		err.subcode1 &= ~VSF_MIN_SIZE;
	}
	if ( flags & VSF_SIZE ) {
		if ( s->size.w == V_DEFAULT_SIZE && s->size.h == V_DEFAULT_SIZE )
			v_serialized_exec_sub(gtk_window_reshow_with_initial_size, GTK_WINDOW(info));
		else
			v_serialized_exec_sub(gtk_window_resize, GTK_WINDOW(info), (int)s->size.w, (int)s->size.h);
		err.subcode1 &= ~VSF_SIZE;
	}
	if ( flags & VSF_VISIBLE ) {
		if ( s->visible )
			v_serialized_exec_sub(gtk_widget_show, info);
		else
			if ( get_object_by_id(sts.id) ) // not in making
				v_serialized_exec_sub(gtk_widget_hide, info);
		err.subcode1 &= ~VSF_VISIBLE;
	}
	
	V_OP_END
	return err;
}

void
VWindow::redraw(VRect* rect) const
{
}

bool
VWindow::attempt_close()
{
	if ( window_close_handler == 0 ) {
		gtk_widget_hide(info);
		vq_insert_callback(this, v_object_destroyer, 0, 0, 0);
		return true;
	}
	vq_insert_callback(this, window_close_handler, user_arg, 0, 0);
	return false;
}

VExError
VWindow::add_child_do(VObject* child)
{
	VInfo * cinfo = child->get_info_this();
	GtkBox *box;
	if ( menu_bar )
		box = GTK_BOX(((GtkBoxChild*)(
			GTK_BOX(GTK_BIN(info)->child)->children->next->data))->widget);
	else
		box = GTK_BOX(((GtkBoxChild*)(
			GTK_BOX(GTK_BIN(info)->child)->children->data))->widget);
	v_serialized_exec_sub(gtk_box_pack_start,
		box, cinfo, 1, 1, (unsigned)sts.padding.w);
	v_serialized_exec_sub(gtk_widget_set_size_request, info, -1, -1);
	return initial_VExError(V_ER_NO_ERR,0,0);
}

void
VWindow::remove_child_do(VObject* child)
{
	GtkBox *box;
	if ( menu_bar )
		box = GTK_BOX(((GtkBoxChild*)(
			GTK_BOX(GTK_BIN(info)->child)->children->next->data))->widget);
	else
		box = GTK_BOX(((GtkBoxChild*)(
			GTK_BOX(GTK_BIN(info)->child)->children->data))->widget);
	VInfo * cinfo = child->get_info_this();
	v_serialized_exec_sub(gtk_container_remove, GTK_CONTAINER(box), cinfo);
	if ( ! (sts.attr & resizable) )
		v_serialized_exec_sub(gtk_widget_set_size_request, info, (int)sts.min_size.w, (int)sts.min_size.h);
}

void
VWindow::child_status_changed(VObject *child, VInfo *cinfo)
{
	VObjectStatus s;
	child->get_status(&s, VSF_PADDING | VSF_ALIGN);
	GtkBox *box1 = GTK_BOX(GTK_BIN(info)->child);
	GtkBox *box2;
	if ( menu_bar )
		box2 = GTK_BOX(((GtkBoxChild*)(box1->children->next->data))->widget);
	else
		box2 = GTK_BOX(((GtkBoxChild*)(box1->children->data))->widget);
	v_serialized_exec_sub(gtk_box_set_child_packing,
		box1, GTK_WIDGET(box2),
		(s.alignv == VALIGN_EXPAND || s.alignv == VALIGN_FILL) ? 1 : 0,
		s.alignv == VALIGN_FILL ? 1 : 0,
		(unsigned)s.padding.h + sts.spacing.h,
		s.alignv == VALIGN_RIGHT ? GTK_PACK_END : GTK_PACK_START);
	if ( box2->children )
		v_serialized_exec_sub(gtk_box_set_child_packing,
			box2, ((GtkBoxChild*)box2->children->data)->widget,
			(s.alignh == VALIGN_EXPAND || s.alignh == VALIGN_FILL) ? 1 : 0,
			s.alignh == VALIGN_FILL ? 1 : 0,
			(unsigned)s.padding.w + sts.spacing.w,
			s.alignh == VALIGN_RIGHT ? GTK_PACK_END : GTK_PACK_START);
}

