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

	Copyright (C) 2003-2004
	Hirohisa MORI <joshua@nichibun.ac.jp>
	Tomohito Nakajima <nakajima@zeta.co.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 "machine/gb_windows.h"
#include "vwin_control.h"
#include "v/VScrollView.h"
#include "v/VLayout.h"
#include "VApplication.h"
#include "vwin_error.h"

class ScrollViewRegister{
public:
	
	ScrollViewRegister(){
		WNDCLASSEX wc;
		wc.cbSize = sizeof(WNDCLASSEX);
		wc.style = CS_PARENTDC|CS_DBLCLKS|CS_HREDRAW|CS_VREDRAW;
		wc.lpfnWndProc = VContainerInfo::WndProc;
		wc.cbClsExtra = 0;
		wc.cbWndExtra = sizeof(DWORD);
		wc.hInstance = ::GetModuleHandle(NULL);
		wc.hIcon = LoadIcon(NULL,IDI_APPLICATION);
		wc.hCursor = LoadCursor(NULL,IDC_ARROW);
		wc.hbrBackground = NULL;//(HBRUSH)GetStockObject(WHITE_BRUSH);
		wc.lpszMenuName = NULL;
		wc.lpszClassName = "vobj_scroll_view";
		wc.hIconSm = NULL;
		
		if(!::RegisterClassEx(&wc)){
			VWinError();
			er_panic("ScrollViewRegister");
		}
	}
}scroll_view_register;

static HWND CreateScrollWindow(int style, HWND hwnd, int id){
	return ::CreateWindowEx(
		WS_EX_CONTROLPARENT, 
		"vobj_scroll_view",
		"scroll_view",
		style,
		0,
		0,
		0,
		0,
		hwnd,
		(HMENU)id,
		theApp->get_instance(),
		NULL);
}


VExError
VScrollView::create_do(const VObjectStatus* s, int flags,VObject * parent, void * arg)
{
	sts.attr = 0;
	if ( flags & VSF_ATTR )
		sts.attr = s->attr;
	sts.min_size.h = sts.min_size.w = 20;

	DWORD style = WS_CHILD | WS_VSCROLL | WS_HSCROLL;
	if(!s->enabled)
		style |= WS_DISABLED;
	if(s->visible)
		style |= WS_VISIBLE;
	if ( sts.attr & h_never )
		style &= ~WS_HSCROLL;
	if ( sts.attr & v_never )
		style &= ~WS_VSCROLL;
	
	VContainerInfo *container_info = VInfo::get_container_info(parent);
	HWND hwnd = v_serialized_exec_func(CreateScrollWindow, style, container_info->get_hwnd(), 0);
	info = new VContainerInfo(this, hwnd, sts.id, 1, 0);
	if(info->get_hwnd()==NULL){
		VWinError();
		er_panic("VScrollView::create");
	}
	static_cast<VContainerInfo*>(info)->set_scrollable(1);

	VSize m={0,0};
	set_internal_size(m);
	return parent->add_child_do(this);
}

void
VScrollView::destroy_do(VObject * parent)
{
	parent->remove_child_do(this);
	if(info){
		v_serialized_exec_sub(::DestroyWindow, info->get_hwnd());
		delete info;
		info = NULL;
	}
	parent->redraw();
}

VScrollView::~VScrollView()
{
}

VExError
VScrollView::get_status(VObjectStatus *s, int flags) const
{
	V_OP_START_EX
	VExError err = VObject::get_status(s,flags);
	win_control_default_get_status(info->get_hwnd(), s, flags, &err);
	V_OP_END
	return err;
};

VExError
VScrollView::set_status(const VObjectStatus *s, int flags)
{
	V_OP_START_EX
	VSize last_size = sts.min_size;
	VExError err = VObject::set_status(s,flags);
	HWND hwnd = info->get_hwnd();
	win_control_default_set_status(hwnd, s, flags, &err);

	if ( flags & VSF_CALC_MIN ) {
		if (sts.children)
			sts.children->object->set_status(0, VSF_CALC_MIN);
		// do nothing - min_size must be set by the user.
		err.subcode1 &= ~VSF_CALC_MIN;
	}
	if ( flags & VSF_LAYOUT ) {
		VLayout layout;
		layout.layout_in_frame(this);
		layout.do_layout(this);
		err.subcode1 &= ~VSF_LAYOUT;
	}

	if ( flags & VSF_VISIBLE ) {
		if ( s->visible != (::IsWindowVisible(hwnd)!=FALSE) ){
			v_serialized_exec_sub(ShowWindow, hwnd, s->visible ? SW_SHOW : SW_HIDE);
		}
		err.subcode1 &= ~VSF_VISIBLE;
	}

	V_OP_END

	if ( (flags & (VSF_HOMOGEN | VSF_SPACING | VSF_ALIGN | VSF_PADDING | VSF_VISIBLE)) ||
		 (flags & (VSF_MIN_SIZE)) &&
			(sts.min_size.w != last_size.w ||
			 sts.min_size.h != last_size.h) )
		VLayout::mark(this);
	return err;
}


VExError
VScrollView::add_child_do(VObject* child)
{
	return initial_VExError(V_ER_NO_ERR,0,0);
}

void
VScrollView::remove_child_do(VObject* child)
{
}

void
VScrollView::child_status_changed(VObject* child, VInfo* info)
{
	// do nothing - object is marked when status is changed
}

void
VScrollView::redraw(VRect* rect) const
{
	if ( ! info )
		return;
	_V_OP_START_VOID
	win_redraw(info->get_hwnd(), rect);
	V_OP_END
}

void
VScrollView::set_internal_size(const VSize size)
{
	_V_OP_START_VOID
	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);
	V_OP_END
}

void
VScrollView::set_internal_size_do(const VSize size)
{
	static_cast<VContainerInfo*>(info)->set_internal_size(size.w, size.h);
}

VSize
VScrollView::get_internal_size_cur()
{
	_V_OP_START(internal_size)
	VSize ret = internal_size;
	RECT r;
	::GetClientRect(info->get_hwnd(), &r);
	if ( r.right-r.left > internal_size.w )
		ret.w = r.right-r.left;
	if ( r.bottom-r.top > internal_size.h )
		ret.h = r.bottom-r.top;
	V_OP_END
	return ret;
}
