/**********************************************************************
 
	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 "VReflector.h"
#include "CClickCheckAttachment.h"
#include "v/VLayout.h"
#include <CSplitView.h>

extern "C" void er_panic(char*);

VInfo* make_SplitView(const VObjectStatus *s, VObject* obj,VInfo * p_info, bool vertical);


void
VSplitView::destroy_do(VObject* nmp)
{
	nmp->remove_child_do(this);
	delete info;
	nmp->redraw();
}

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, &sts, info);
	VExError err2 = VObject::set_status(s,flags);
	if ( err2.code )
		err = merge_VExError_vstatus_type(err,err2);

	CSplitView *csp = (CSplitView*)info;

	if ( flags & VSF_ATTR ) {
		csp->SetShrinkable(s->attr & shrinkable0, s->attr & shrinkable1);
	}
	if ( flags & VSF_VALUE ) {
		sts.value = 0x7fffffff; // force enable the value_changed handler
		csp->SetPosition(s->value, true);
	}
	
	if ( flags & (VSF_CALC_MIN | VSF_LAYOUT) ) {
		unsigned short pos, min, max;
		if ( sts.value == -1 )
			pos = (unsigned short)-1;
		else
			pos = csp->GetPosition();

		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_split_view(this, get_type()==VVSplitView::object_type,
						pos, min, max, child_status, true);
			sts.min_size = layout.parent_min_size();
			err.subcode1 &= ~VSF_CALC_MIN;
		}
		if ( flags & VSF_LAYOUT ) {
			VLayout layout;
			layout.layout_in_split_view(this, get_type()==VVSplitView::object_type,
						pos, min, max, child_status);
			sts.value = -1; // stop value_changed handler to avoid re-layout
			layout.do_layout(this);
			last_size = sts.size;
			csp->SetMinMax(min, max);
			csp->SetPosition(pos, true);
			sts.value = pos;
			err.subcode1 &= ~VSF_LAYOUT;
		}
	}

	V_OP_END

	if ( flags & (VSF_HOMOGEN | VSF_SPACING | VSF_ALIGN | VSF_PADDING | VSF_VISIBLE ) )
		VLayout::mark(this);

	return err;
}

VExError
VSplitView::add_child_do(VObject*child)
{
	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);
}

void
VSplitView::remove_child_do(VObject* child)
{
	if ( child0 == child )
		child0 = 0;
	else if ( child1 == child )
		child1 = 0;
	else
		er_panic("VSplitView::remove_child_do");
}

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

void
VSplitView::redraw(VRect* rect) const
{
	_V_OP_START()
	if ( rect )
		info->RefreshRect((Rect){rect->t, rect->l, rect->b, rect->r});
	else
		info->Refresh();
	V_OP_END
}

static V_CALLBACK_D(mark_window)
{
	VLayout::mark(object);
	VLayout::layout_marked_window();
}

void
VSplitView::value_changed()
{
	CSplitView *csp = dynamic_cast<CSplitView*>(info);
	if ( csp == 0 )
		return;
	int v = csp->GetPosition();
	if ( sts.value >= 0 && sts.value != v
			&& is_pane_visible(0) && is_pane_visible(1) ) {
		if ( child_status[0] & child_shrinkable ) {
			if ( v == 0 && !(child_status[0] & child_shrinked) ) {
				child_status[0] |= child_shrinked;
				if ( child0 )
					child0->get_info_this()->Hide();
			}
			else if ( v > 0 && (child_status[0] & child_shrinked) ) {
				child_status[0] &= ~child_shrinked;
				if ( child0 )
					child0->get_info_this()->Show();
			}
		}
		if ( child_status[1] & child_shrinkable ) {
			SDimension16 size;
			csp->GetFrameSize(size);
			short max = (get_type() == VHSplitView::object_type
							? size.width : size.height) - SPLIT_BAR_WIDTH;
			if ( v == max && !(child_status[1] & child_shrinked) ) {
				child_status[1] |= child_shrinked;
				if ( child1 )
					child1->get_info_this()->Hide();
			}
			else if ( v < max && (child_status[1] & child_shrinked) ) {
				child_status[1] &= ~child_shrinked;
				if ( child1 )
					child1->get_info_this()->Show();
			}
		}

		vq_insert_callback(this, mark_window, 0, 0, 0);
	}
	if ( sts.value >= 0 )
		VObject::value_changed();
}

void
VSplitView::show_pane(int pane, int expand_window)
{
	_V_OP_START_VOID
	child_status[pane?1:0] &= ~child_hidden;

	CSplitView* csp = (CSplitView*)info;

	if ( pane == 0 ) {
		if ( child0 && !(child_status[0]&child_shrinked) )
			child0->get_info_this()->Show();
	}
	else {
		if ( child1 && !(child_status[1]&child_shrinked) )
			child1->get_info_this()->Show();
	}
	csp->ShowChild(pane);
	
	V_OP_END

	if ( expand_window ) {
		if ( expand_window < 0 )
			expand_window = shrink_size[pane?1:0];

		VObjectStatus s;
		sts.window->get_status(&s, VSF_SIZE);
		UInt16 old_size =
			get_type() == VHSplitView::object_type?sts.size.w:sts.size.h;
		if ( get_type() == VHSplitView::object_type )
			s.size.w += expand_window+SPLIT_BAR_WIDTH;
		else
			s.size.h += expand_window+SPLIT_BAR_WIDTH;
		sts.window->set_status(&s, VSF_SIZE);
		last_size = v_default_size;
		sts.value = -2;	// stop value_changed handler
		vobject_layout();
		csp->SetPosition(pane?old_size:expand_window, false);
		sts.value = csp->GetPosition();
	}

	VLayout::mark(this);
}

void
VSplitView::hide_pane(int pane, int shrink_window)
{
	_V_OP_START_VOID
	child_status[pane?1:0] |= child_hidden;
	
	if ( pane == 0 ) {
		if ( child0 )
			child0->get_info_this()->Hide();
	}
	else {
		if ( child1 )
			child1->get_info_this()->Hide();
	}
	((CSplitView*)info)->HideChild(pane);

	V_OP_END

	if ( shrink_window ) {
		if ( shrink_window < 0 ) {
			if ( pane == 0 )
				shrink_window = sts.value;
			else {
				SDimension16 size;
				info->GetFrameSize(size);
				shrink_window = (get_type() == VHSplitView::object_type
					? size.width : size.height) - SPLIT_BAR_WIDTH - sts.value;
			}
		}

		VObjectStatus s;
		sts.window->get_status(&s, VSF_SIZE);
		if ( get_type() == VHSplitView::object_type )
			s.size.w -= shrink_window+SPLIT_BAR_WIDTH;
		else
			s.size.h -= shrink_window+SPLIT_BAR_WIDTH;
		sts.window->set_status(&s, VSF_SIZE);
	}
	shrink_size[pane?1:0] = shrink_window;

	VLayout::mark(this);
}


VInfo*
make_SplitView(const VObjectStatus *s, VObject* obj, VInfo * p_info, bool vertical)
{
	SPaneInfo paneInfo;
	paneInfo.superView = dynamic_cast<LView*>(p_info);
	if ( paneInfo.superView == 0 )
		er_panic("non-LView object cannot be parent");
	paneInfo.paneID = s->id;
	paneInfo.width = 0;
	paneInfo.height = 0;
	paneInfo.visible = true;
	paneInfo.enabled = true;
	paneInfo.left = 0;
	paneInfo.top = 0;
	paneInfo.bindings = (SBooleanRect){false,false,false,false};
	paneInfo.userCon = (long)obj;
	SViewInfo viewInfo = { (SDimension32){0,0}, {0,0}, {1,1}, false };
	CSplitView *v = new CSplitView(paneInfo, viewInfo, !vertical);
	v->AddAttachment(new CClickCheckAttachment);
	v->FinishCreate();
	v->AddListener(new VReflector(obj));
	v->Shrink1onResize_Callback = 0;	// in order not to change pos on shrink1
	return v;
}

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];
	shrink_size[0] = shrink_size[1] = 0;
	last_size = v_default_size;
	sts.value = -1;		// use default size in VLayout

	info = make_SplitView(s,this,nmp->get_info_this(), false);
	VExError err = nmp->add_child_do(this);
	err.subcode1 |= VSF_VALUE;
	return err;
}

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];
	shrink_size[0] = shrink_size[1] = 0; 
	last_size = v_default_size;
	sts.value = -1;		// use default size in VLayout

	info = make_SplitView(s,this,nmp->get_info_this(), true);
	VExError err = nmp->add_child_do(this);
	err.subcode1 |= VSF_VALUE;
	return err;
}
