/**********************************************************************
 
	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/VFocusView.h"
#include "VApplication.h"
#include "vobject_main.h"
#include "vwin_control.h"


class VFocusViewInfo : public VContainerInfo
{
public:
	VFocusViewInfo(VObject *o, HWND h, int id)
		: VContainerInfo(o,h,id,1,0)
	{
	}

	virtual ~VFocusViewInfo()
	{
	}

	static LRESULT CALLBACK FocusViewProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
protected:
	virtual LRESULT dispatch_message(MSG *msg);
};


LRESULT CALLBACK
VFocusViewInfo::FocusViewProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	extern DWORD current_time;
	VFocusViewInfo *info = dynamic_cast<VFocusViewInfo *>(VInfo::get_from_hwnd(hWnd));
	if(!info)
		return DefWindowProc( hWnd, message, wParam, lParam );
	MSG msg;
	memset(&msg, 0, sizeof(msg));
	msg.hwnd = hWnd;
	msg.message = message;
	msg.lParam = lParam;
	msg.wParam = wParam;
	msg.time = current_time;

	return info->dispatch_message(&msg);
}

LRESULT
VFocusViewInfo::dispatch_message(MSG *msg)
{
	VFocusViewInfo *info;
	PAINTSTRUCT ps;
	HDC hdc;
	RECT rect;

	switch (msg->message) {
		case WM_KILLFOCUS:
		case WM_SETFOCUS:
			InvalidateRect(msg->hwnd, NULL, TRUE);
			((VFocusView*)get_obj())->focus_event(msg->message==WM_SETFOCUS);
			((VFocusView*)get_obj())->set_menu_dirty(1);
			break;
		case WM_PAINT:
			if ( ((VFocusView*)get_obj()) == VEditable::get_focused_object() ) {
				hdc = BeginPaint(msg->hwnd, &ps);
				GetClientRect(msg->hwnd, &rect);
				DrawFocusRect(hdc, &rect);
				EndPaint(msg->hwnd, &ps);
			}
			break;
		case WM_LBUTTONDOWN:
		case WM_RBUTTONDOWN:
		case WM_MBUTTONDOWN:
			SetFocus(msg->hwnd);
			get_obj()->value_changed();
			break;
	}
	return VContainerInfo::dispatch_message(msg);
}

class FocusViewRegister{
public:
	
	FocusViewRegister(){
		WNDCLASSEX wc;
		wc.cbSize = sizeof(WNDCLASSEX);
		wc.style = CS_PARENTDC;
		wc.lpfnWndProc = VFocusViewInfo::FocusViewProc;
		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;
		wc.lpszMenuName = NULL;
		wc.lpszClassName = "vobj_focus_view";
		wc.hIconSm = NULL;
		
		if(!::RegisterClassEx(&wc)){
			VWinError();
			er_panic("FocusViewRegister");
		}
	}
}align_view_register;

static HWND CreateFocusViewWindow(int style, VContainerInfo *info, int id){
	return ::CreateWindowEx(
		WS_EX_CONTROLPARENT,
		"vobj_focus_view",
		"focus_view",
		style,
		0,
		0,
		0,
		0,
		info->get_hwnd(),
		(HMENU)id,
		theApp->get_instance(),
		NULL);
}

VExError
VFocusView::create_do(const VObjectStatus* s, int flags,VObject * parent, void * arg)
{
	command_status_handler = 0;
	obey_command_handler = 0;
	menu_dirty = 1;

	DWORD style=WS_CHILD | WS_TABSTOP;
	if(!s->enabled)
		style |= WS_DISABLED;
	if(s->visible)
		style |= WS_VISIBLE;
	
	HWND hwnd;
	VContainerInfo *parent_info = VInfo::get_container_info(parent);
	hwnd = v_serialized_exec_func(CreateFocusViewWindow, style, parent_info, 0);
	info = new VFocusViewInfo(this, hwnd, s->id);
	if(info->get_hwnd()==NULL){
		VWinError();
		er_panic("make_FocusView");
	}
	return parent->add_child_do(this);
}

void
VFocusView::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();
}

VFocusView::~VFocusView()
{
}


VExError
VFocusView::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
VFocusView::set_status(const VObjectStatus *s, int flags)
{
	V_OP_START_EX

	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 ) {
		for ( VObjectList *list = sts.children ; list ; list = list->next ) {
			list->object->set_status(0, VSF_CALC_MIN);
		}
		VLayout layout;
		layout.layout_in_align_view(this, true, true);
		sts.min_size = layout.parent_min_size();
		err.subcode1 &= ~VSF_CALC_MIN;
	}
	if ( flags & VSF_LAYOUT ) {
		VLayout layout;
		layout.layout_in_align_view(this, true);
		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 ) )
		VLayout::mark(this);
	
	return err;
}

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

void
VFocusView::remove_child_do(VObject* child)
{
	// do nothing
}

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

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



void
VFocusView::focus()
{
	v_serialized_exec_sub(SetFocus, info->get_hwnd());
}

void
VFocusView::set_menu_dirty(int d)
{
	menu_dirty = d;
	if ( d )
		check_menu_status();
}

V_CALLBACK_D(VFocusView::call_obey_command_handler)
{
	VFocusView *fv = static_cast<VFocusView*>(object);
	fv->obey_command_handler(object, user_arg, sys_arg);
	fv->set_menu_dirty(1);
}
