/**********************************************************************
 
	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/VSplitView.h"
#include "machine/v_object.h"
extern "C" {
#include "memory_debug.h"
}

typedef struct v_split_view_info {
	VSplitView *obj;
	GtkWidget *al1, *al2;
	int attr;
	bool vert;
	int *value;
} v_split_view_info;


void
VSplitView::destroy_do(VObject * nmp)
{
	nmp->remove_child_do(this);
	v_serialized_exec_sub(gtk_widget_destroy, info);
	v_serialized_exec_sub(g_object_unref, info);
}

VSplitView::~VSplitView()
{
}

VExError
VSplitView::get_status(VObjectStatus *s, int flags) const
{
	V_OP_START_EX
	VExError err = v_get_status_standard(s, &flags, info);
	
	VExError err2 = VObject::get_status(s,flags);
	if ( err2.code )
		err = merge_VExError_vstatus_type(err,err2);
	
	V_OP_END
	return err;
};

VExError
VSplitView::set_status(const VObjectStatus *s, int flags)
{
	V_OP_START_EX
	VExError err = v_set_status_standard(s, flags, info);
	err = VObject::set_status(s,flags);
	if ( err.code ) {
		V_OP_END
		return err;
	}
	
	if ( flags & VSF_VALUE ) {
		v_serialized_exec_sub(gtk_paned_set_position, GTK_PANED(info), s->value);
	}
	
	V_OP_END
	
	if ( flags & (VSF_ALIGN | VSF_PADDING ) ) {
		if ( sts.parent )
			sts.parent->child_status_changed(this, info);
		err.subcode1 &= ~(VSF_ALIGN | VSF_PADDING );
	}
	return err;
}

extern "C" void
v_split_view_add_child_do(GtkPaned* p,
			VInfo *cinfo,
			unsigned alignh,
			unsigned alignv,
			VSize padding,
			int pane)
{
	v_split_view_info *info = (v_split_view_info*)
		g_object_get_data(G_OBJECT(p), "split-view-info");
	GtkWidget *ali = pane==0?info->al1:info->al2;
	gtk_container_add(GTK_CONTAINER(ali), cinfo);
	gtk_alignment_set(GTK_ALIGNMENT(ali),
		alignh==VALIGN_RIGHT?1.0:alignh==VALIGN_EXPAND?0.5:0.0,
		alignv==VALIGN_BOTTOM?1.0:alignv==VALIGN_EXPAND?0.5:0.0,
		alignh==VALIGN_FILL?1.0:0.0,
		alignv==VALIGN_FILL?1.0:0.0);
	//gtk_alignment_set_padding(GTK_ALIGNMENT(ali), padding.h, padding.h, padding.w, padding.w);
}

VExError
VSplitView::add_child_do(VObject* child)
{
	VObjectStatus s;
	VInfo * cinfo = child->get_info_this();
	child->get_status(&s, VSF_ALIGN | VSF_PADDING);

	v_serialized_exec_sub(v_split_view_add_child_do,
		GTK_PANED(info), cinfo, 
		s.alignh, s.alignv, sts.padding, child0 ? 1: 0);

	if ( child0 == 0 )
		child0 = child;
	else if ( child1 == 0 )
		child1 = child;
	else
		er_panic("VSplitView::add_child_do");
	return initial_VExError(V_ER_NO_ERR,0,0);
}


extern "C" void
v_split_view_remove_child_do(GtkPaned* p, VInfo *cinfo, int pane)
{
	v_split_view_info *info = (v_split_view_info*)
		g_object_get_data(G_OBJECT(p), "split-view-info");
	GtkWidget *ali = pane==0?info->al1:info->al2;
	gtk_container_remove(GTK_CONTAINER(ali), cinfo);
}

void
VSplitView::remove_child_do(VObject* child)
{
	VInfo * cinfo = child->get_info_this();
	if ( child0 == child ) {
		child0 = 0;
		v_serialized_exec_sub(v_split_view_remove_child_do,
			GTK_PANED(info), cinfo, 0);
	}
	else if ( child1 == child ) {
		child1 = 0;
		v_serialized_exec_sub(v_split_view_remove_child_do,
			GTK_PANED(info), cinfo, 1);
	}
	else
		er_panic("VSplitView::remove_child_do");
}

void
VSplitView::child_status_changed(VObject *child, VInfo* cinfo)
{
	// do nothing
}

void
VSplitView::redraw(VRect *rect) const
{
	// do nothing
}

void
VSplitView::value_changed()
{
	VObject::value_changed();
}

extern "C" void
v_split_view_show_pane(VInfo *p, VInfo *window, int pane, int expand_window)
{
	v_split_view_info *info = (v_split_view_info*)
		g_object_get_data(G_OBJECT(p), "split-view-info");
	
	if ( GTK_WIDGET_VISIBLE(pane ? info->al2 : info->al1) )
		return;
	
	gint handle_size = 5;
	gtk_widget_style_get(p, "handle-size", &handle_size, NULL);
	
	gtk_widget_show(pane ? info->al2 : info->al1);
	
	if ( expand_window ) {
		int s[2] = { info->vert ? info->al1->allocation.height : info->al1->allocation.width,
					info->vert ? info->al2->allocation.height : info->al2->allocation.width };
		gint win_w, win_h;
		gtk_window_get_size(GTK_WINDOW(window), &win_w, &win_h);
		if ( expand_window < 0 )
			expand_window = s[pane?1:0];
		gtk_window_resize(GTK_WINDOW(window),
			win_w + (info->vert ? 0 : expand_window+handle_size),
			win_h + (info->vert ? expand_window+handle_size : 0));
		while ( gtk_events_pending() )
			gtk_main_iteration();
		gtk_paned_set_position(GTK_PANED(p), s[0]);
	}
}

void
VSplitView::show_pane(int pane, int expand_window)
{
	_V_OP_START_VOID
	child_status[pane?1:0] &= ~child_hidden;
	v_serialized_exec_sub(v_split_view_show_pane, info, sts.window->get_info_this(), pane, expand_window);
	V_OP_END
}

extern "C" void
v_split_view_hide_pane(VInfo *p, VInfo *window, int pane, int shrink_window)
{
	v_split_view_info *info = (v_split_view_info*)
		g_object_get_data(G_OBJECT(p), "split-view-info");
	
	if ( ! GTK_WIDGET_VISIBLE(pane ? info->al2 : info->al1) )
		return;
	
	gtk_widget_hide(pane ? info->al2 : info->al1);
	
	if ( shrink_window ) {
		if ( shrink_window < 0 ) {
			if ( pane == 0 )
				shrink_window = info->vert ? info->al1->allocation.height : info->al1->allocation.width;
			else
				shrink_window = info->vert ? info->al2->allocation.height : info->al2->allocation.width;
		}
		
		gint handle_size = 5;
		gtk_widget_style_get(p, "handle-size", &handle_size, NULL);
		
		gint win_w, win_h;
		gtk_window_get_size(GTK_WINDOW(window), &win_w, &win_h);
		gtk_window_resize(GTK_WINDOW(window),
			win_w - (info->vert ? 0 : shrink_window+handle_size),
			win_h - (info->vert ? shrink_window+handle_size : 0));
	}
}

void
VSplitView::hide_pane(int pane, int shrink_window)
{
	_V_OP_START_VOID
	child_status[pane?1:0] |= child_hidden;
	v_serialized_exec_sub(v_split_view_hide_pane, info, sts.window->get_info_this(), pane, shrink_window);
	V_OP_END
}


extern "C" gboolean
v_split_view_expose_event(GtkPaned *paned, GdkEventExpose *event, v_split_view_info *info)
{
	int pos = gtk_paned_get_position(paned);
	gtk_widget_set_size_request(info->al1, -1, -1);
	gtk_widget_set_size_request(info->al2, -1, -1);
	gtk_paned_set_position(paned, pos);
	g_signal_handlers_disconnect_by_func(G_OBJECT(paned),
		(void*)v_split_view_expose_event, info);
	return FALSE;
}


extern "C" gboolean
v_split_view_position_notify(GtkPaned *paned, GParamSpec *spec, v_split_view_info *info)
{
	gboolean ret = TRUE;
	gint handle_size = 5;
	gtk_widget_style_get(GTK_WIDGET(paned), "handle-size", &handle_size, NULL);
	int pos = gtk_paned_get_position(paned);
	int size = info->vert ? 
		GTK_WIDGET(paned)->allocation.height :
		GTK_WIDGET(paned)->allocation.width;
	int min = info->vert ? info->al1->requisition.height : info->al1->requisition.width;
	int max = size - (info->vert ? info->al2->requisition.height : info->al2->requisition.width) - handle_size;
	
	if ( min > max )
		max = min;
	bool vis1 = GTK_WIDGET_VISIBLE(info->al1);
	bool vis2 = GTK_WIDGET_VISIBLE(info->al2);
	
	if ( (info->attr & VSplitView::shrinkable0) && pos < 5 ) {
		if ( pos != 0 )
			gtk_paned_set_position(paned, 0);
	}
	else if ( (info->attr & VSplitView::shrinkable1) && pos > size-5-handle_size ) {
		if ( pos != size-handle_size )
			gtk_paned_set_position(paned, size-handle_size);
	}
	else if ( vis1 && pos < min )
		gtk_paned_set_position(paned, min);
	else if ( vis2 && pos > max )
		gtk_paned_set_position(paned,max);
	else
		ret = FALSE;
	*info->value = gtk_paned_get_position(paned);
	::value_changed(GTK_WIDGET(paned), (VObject*)info->obj);
	return ret;
}

extern "C" gboolean
v_split_view_destroy_event(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
	d_f_ree(user_data);
	return FALSE;
}

extern "C" VInfo *
v_split_view_create_do(VSplitView *obj, bool vert, short def_size[2], int attr, int *value)
{
	VInfo *ret;
	if ( !vert )
		ret = gtk_hpaned_new();
	else
		ret = gtk_vpaned_new();
	g_object_ref(ret);
	
	v_split_view_info* info = (v_split_view_info*)d_alloc(sizeof(v_split_view_info));
	info->obj = obj;
	info->al1 = gtk_alignment_new(1,1,1,1);
	info->al2 = gtk_alignment_new(1,1,1,1);
	info->attr = attr;
	info->vert = vert;
	info->value = value;
	gtk_paned_pack1(GTK_PANED(ret),info->al1,(attr&VSplitView::expand0_on_resize)!=0,1);
	gtk_paned_pack2(GTK_PANED(ret),info->al2,(attr&VSplitView::expand1_on_resize)!=0,1);
	if ( def_size[0] ) {
		gtk_widget_show(info->al1);
		if ( def_size[0] > 0 ) {
			gtk_widget_set_size_request(info->al1, vert?-1:def_size[0], vert?def_size[0]:-1);
		}
	}
	if ( def_size[1] ) {
		gtk_widget_show(info->al2);
		if ( def_size[1] > 0 ) {
			gtk_widget_set_size_request(info->al2, vert?-1:def_size[1], vert?def_size[1]:-1);
		}
	}
	if (def_size[0] > 0 || def_size[1] > 0 )
		g_signal_connect(GTK_OBJECT(ret), "expose-event",
			GTK_SIGNAL_FUNC(v_split_view_expose_event), info);
	g_signal_connect(GTK_OBJECT(ret), "notify::position",
		GTK_SIGNAL_FUNC(v_split_view_position_notify), info);
	g_signal_connect(GTK_OBJECT(ret), "destroy-event",
		GTK_SIGNAL_FUNC(v_split_view_destroy_event), info);
	g_object_set_data(G_OBJECT(ret), "split-view-info", info);
	return ret;
}

VExError
VHSplitView::create_do(const VObjectStatus* s, int flags,
		VObject * nmp, void * arg)
{
	child0 = child1 = 0;
	child_status[0] = ( (flags & VSF_ATTR) && (s->attr & shrinkable0) ) ? child_shrinkable: 0;
	child_status[1] = ( (flags & VSF_ATTR) && (s->attr & shrinkable1) ) ? child_shrinkable: 0;
	short *ds = (short*)arg;
	def_size[0] = ds[0];
	def_size[1] = ds[1];
	
	info = v_serialized_exec_func(v_split_view_create_do,
		(VSplitView*)this, false, def_size, (flags&VSF_ATTR)?s->attr:0, &sts.value);
	return return_create_do(this,nmp,&sts,s,flags,VSF_ALREADY_FLAGS|VSF_VALUE);
}

VExError
VVSplitView::create_do(const VObjectStatus* s, int flags,
		VObject * nmp, void * arg)
{
	child0 = child1 = 0;
	child_status[0] = ( (flags & VSF_ATTR) && (s->attr & shrinkable0) ) ? child_shrinkable: 0;
	child_status[1] = ( (flags & VSF_ATTR) && (s->attr & shrinkable1) ) ? child_shrinkable: 0;
	short *ds = (short*)arg;
	def_size[0] = ds[0];
	def_size[1] = ds[1];
	
	info = v_serialized_exec_func(v_split_view_create_do,
		(VSplitView*)this, true, def_size, (flags&VSF_ATTR)?s->attr:0, &sts.value);
	return return_create_do(this,nmp,&sts,s,flags,VSF_ALREADY_FLAGS|VSF_VALUE);
}
