/**********************************************************************
 
	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/VScrollView.h"
#include "machine/v_object.h"


VExError
VScrollView::create_do(const VObjectStatus* s, int flags, 
		VObject * nmp,void * arg)
{

	info = v_serialized_exec_func<GtkWidget*, GtkAdjustment*, GtkAdjustment*>
		(gtk_scrolled_window_new, NULL, NULL);
	v_serialized_exec_sub(g_object_ref, (void*)info);
	set_internal_size((VSize){0,0});

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

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

VScrollView::~VScrollView()
{
}

VExError
VScrollView::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;
};

#define ATTR_2_GTK_POLICY(attr)	\
((attr) == h_always ? GTK_POLICY_ALWAYS :	\
(attr) == h_automatic ? GTK_POLICY_AUTOMATIC :	\
GTK_POLICY_NEVER)

VExError
VScrollView::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_ATTR ) {
		v_serialized_exec_sub(gtk_scrolled_window_set_policy,
			GTK_SCROLLED_WINDOW(info),
			ATTR_2_GTK_POLICY(s->attr & 0x3),
			ATTR_2_GTK_POLICY((s->attr>>4) & 0x3));
		err.subcode1 &= ~VSF_ATTR;
	}
	if ( flags & VSF_SPACING ) {
		v_serialized_exec_sub(gtk_container_border_width,
			GTK_CONTAINER(info), (unsigned)(s->spacing.w+s->spacing.h)/2); 
		err.subcode1 &= ~VSF_SPACING;
	}
	if ( flags & VSF_MIN_SIZE ) {
		v_serialized_exec_sub(gtk_widget_set_size_request, info, (int)s->min_size.w, (int)s->min_size.h);
		err.subcode1 &= ~VSF_MIN_SIZE;
	}
	
	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
viewport_size_allocate_event(GtkWidget *widget,
							GtkAllocation *alloc,
							gpointer user_data)
{
	VSize *size = (VSize*)user_data;
	size->w = alloc->width;
	size->h = alloc->height;
}

extern "C" void
v_scroll_view_add_child_do(VScrollView *scv,
						VInfo* info,
						VInfo* cinfo,
						VSize *size)
{
	gtk_scrolled_window_add_with_viewport(
						GTK_SCROLLED_WINDOW(info), cinfo);
	GList *list = gtk_container_children(GTK_CONTAINER(info));
	if ( list ) {
		gtk_signal_connect(GTK_OBJECT(list->data), "size-allocate",
						GTK_SIGNAL_FUNC(viewport_size_allocate_event),
						(gpointer)size);
		g_list_free(list);
	}
	scv->set_internal_size(v_default_size);
}

VExError
VScrollView::add_child_do(VObject* child)
{
	v_serialized_exec_sub(v_scroll_view_add_child_do,
		this, info, child->get_info_this(), &internal_size_cur);
	return initial_VExError(V_ER_NO_ERR,0,0);
}

extern "C" void
v_scroll_view_remove_from_viewport(VInfo* info, VInfo* cinfo)
{
	GList *list = gtk_container_children(GTK_CONTAINER(info));
	if ( list ) {
		gtk_container_remove(GTK_CONTAINER(list->data), cinfo);
		g_list_free(list);
	}
}

void
VScrollView::remove_child_do(VObject* child)
{
	v_serialized_exec_sub(v_scroll_view_remove_from_viewport,
		info, child->get_info_this());
}

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

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

extern "C" void
v_scroll_view_set_internal_size(VInfo * info, VSize size) {
	GList *list = gtk_container_children(GTK_CONTAINER(info));
	if ( list ) {
		GList *list2 = gtk_container_children(GTK_CONTAINER(list->data));
		if ( list2 ) {
			if ( size.w == 0 )
				size.w = -1; // "natural" size
			if ( size.h == 0 )
				size.h = -1; // "natural" size
			gtk_widget_set_size_request(GTK_WIDGET(list2->data), size.w, size.h);
			g_list_free(list2);
		}
		g_list_free(list);
	}
}

void
VScrollView::set_internal_size(const VSize size)
{
	if ( size.w != V_DEFAULT_SIZE )
		internal_size.w = size.w;
	if ( size.h != V_DEFAULT_SIZE )
		internal_size.h = size.h;
	set_internal_size_do(internal_size);
}

void
VScrollView::set_internal_size_do(const VSize size)
{
	v_serialized_exec_sub(v_scroll_view_set_internal_size, info, size);
}

VSize
VScrollView::get_internal_size_cur()
{
	return internal_size_cur;
}
