/*	XGListView.cpp
 *
 *		This is the code for managing a list of strings.
 */

/*  YAAF - Yet another application framework
 *  Copyright (C) 1997, 1998 William Edward Woody
 *  
 *  This library is free software; you can redistribute it
 *  and/or modify it under the terms of the GNU Library
 *  General Public License as published by the Free Software
 *  Foundation; either version 2 of the License, or any
 *  later version.
 *  
 *  This library is distributed in the hope that it will be
 *  useful, but WITHOUT ANY WARRANTY; without even the implied
 *  warranty of MERCHANTABIILITY or FITNESS FOR A PARTICULAR
 *  PURPOSE. See the GNU Library General Public License for
 *  more details.
 *  
 *  You should have received a copy of the GNU Library General
 *  Public License along with this library; if not, write to the
 *  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 *  Boston, MA 02111-1307, USA.
 *  
 *  To contact the author, either e-mail me at woody@pandawave.com, 
 *	or write to
 *  
 *          William Edward Woody
 *          In Phase Consulting
 *          1545 Ard Eevin Avenue
 *          Glendale, CA 91202
 *
 *	Or visit us on the web at http://www.pandawave.com
 */

#include <XConfig.h>			// must be first to optimize compile on MSVC++
#include <XMessages.h>
#include <XDraw.h>
#include <XList.h>

/************************************************************************/
/*																		*/
/*	Construction/Destruction											*/
/*																		*/
/************************************************************************/

static XGSViewInitRecord ViewInitRecord = {
	'****', 0, 0,
	true, true, true, true,
	0, 0, 0, 0,
	true, true, false
};

/************************************************************************/
/*																		*/
/*	Construction/Destruction											*/
/*																		*/
/************************************************************************/

/*	@F"XGListView::XGListView" @I
 *
 *		Construct this list view. This will be locked to the boundaries
 *	of the scrollview
 */

XGListView::XGListView(XGListControl *control) : XGView(control,ViewInitRecord)
{
	fControl = control;
}

/*	@F"XGListView::~XGListView" @I
 *
 *		Delete me
 */

XGListView::~XGListView()
{
}

/************************************************************************/
/*																		*/
/*	Construction/Destruction											*/
/*																		*/
/************************************************************************/

/*	@F"XGListView::DoDrawView" @I
 *
 *		Handle drawing the contents of this thing
 */

void XGListView::DoDrawView(Rect)
{
	long xs,ys;
	long len;
	long h;
	long x;
	short ypos;
	Rect r;
	XGDraw draw(this);
	
	fControl->fFont->Attach();
	draw.SetFont(fControl->fFont);
	
	/*
	 *	Calculate the lines to draw
	 */
	
	fControl->GetScrollValue(&xs,&ys);
	h = fControl->GetPixelStep();
	r = GetContentRect();
	len = (r.bottom - r.top + h - 1) / h;
	len += ys;
	if (len >= fControl->Length()) len = fControl->Length();
	
	ypos = 0;
	for (x = ys; x < len; x++) {
		DrawLine(draw,x,ypos++,fControl->GetSelect(x));
	}

	/*
	 *	Zap the bottom
	 */
	
	ys = (len-ys) * h;
	if (ys < r.bottom) {
		r.top = ys;
		draw.EraseRect(r);
	}
}

/*	@F"XGListView::DoSizeView" @I
 *
 *		Resize this thing
 */

void XGListView::DoSizeView()
{
	CalcResize();
	XGView::DoSizeView();
}

/*	@F"XGListView::DoMouseDown" @I
 *
 *		Handle mouse down event. This flips the selection state of the
 *	specified item
 */

bool XGListView::DoMouseDown(Point where, short flags)
{
	long xs,ys;
	long h;
	long ypos;
	Rect r;
	
	/*
	 *	Calculate where I clicked.
	 */
	
	fControl->GetScrollValue(&xs,&ys);
	h = fControl->GetPixelStep();
	r = GetContentRect();
	
	ypos = ys + where.v/h;
	if (ypos < 0) ypos = -1;
	else if (ypos >= fControl->Length()) ypos = -1;
	
	/*
	 *	Set the state
	 */
	
	if (!(flags & KKeyShift)) fControl->ClearSelect();
	if (ypos >= 0) {
		fControl->SetSelect(ypos,!(fControl->GetSelect(ypos)));
	}
	
	if (flags & KDoubleClick) {
		DoDispatch(KXGListSelect,fControl->GetViewID(),NULL);
		return false;
	} else {
		DoDispatch(KXGListValue,fControl->GetViewID(),NULL);
	}
	
	/*
	 *	Drag selection stuff
	 */
	 
	return false;						//###
}

/*	@F"XGListView::DoMouseMove" @I
 *
 *		Handle mouse move event. This flips the selection state of the
 *	specified item
 */

void XGListView::DoMouseMove(Point where, short flags)
{
	XGView::DoMouseMove(where,flags);
}

/*	@F"XGListView::DoMouseUp" @I
 *
 *		Handle mouse up event. This flips the selection state of the
 *	specified item
 */

void XGListView::DoMouseUp(Point where, short flags)
{
	XGView::DoMouseUp(where,flags);
}

/************************************************************************/
/*																		*/
/*	Internal Stuff														*/
/*																		*/
/************************************************************************/

/*	@F"XGListView::RedrawLine" @I
 *
 *		Redraw the specified line
 */

void XGListView::RedrawLine(long index)
{
	long xs,ys;
	long len;
	long h;
	short ypos;
	Rect r;
	
	/*
	 *	Calculate various scroll parameters
	 */
	
	fControl->GetScrollValue(&xs,&ys);
	h = fControl->GetPixelStep();
	r = GetContentRect();
	len = (r.bottom - r.top + h - 1) / h;
	len += ys;
	if (len >= fControl->Length()) len = fControl->Length();
	if ((index < ys) || (index >= len)) return;		// don't draw.

	/*
	 *	Draw the line
	 */
	
	XGDraw draw(this);
	fControl->fFont->Attach();
	draw.SetFont(fControl->fFont);

	ypos = 0;
	DrawLine(draw,index,index-ys,fControl->GetSelect(index));
}

/*	@F"XGListView::CalcResize" @I
 *
 *		Calculate the scrollbars for this thing. Normally done when
 *	either the view or the list has been resized
 */

void XGListView::CalcResize(void)
{
	long x,w,width,height;
	Rect r;
	long h;
	char buffer[256];

	XGDraw draw(this);
	fControl->fFont->Attach();
	draw.SetFont(fControl->fFont);
	
	/*
	 *	Calculate the width/height
	 */
	
	width = 0;
	height = fControl->Length();
	for (x = 0; x < height; x++) {
		fControl->GetItem(x,buffer);
		w = 10 + draw.StringWidth(buffer);
		if (w > width) width = w;
	}
	h = draw.GetFontHeight();
	height *= h;
	
	/*
	 *	Now figure out the max stuff
	 */
	
	r = GetContentRect();
	width -= r.right - r.left;
	height -= r.bottom - r.top;
	width = (width + h - 1) / h;
	height = (height + h - 1) / h;
	
	if (width < 0) width = 0;
	if (height < 0) height = 0;
	
	/*
	 *	And set the scrollbar parameters
	 */
	
	fControl->SetMinValue(0,0);
	fControl->SetMaxValue(width,height);
	fControl->SetPixelStep(h);
	
	width = (r.right - r.left) / h;
	if (width < 1) width = 1;
	height = (r.bottom - r.top) / h;
	if (height < 1) height = 1;
	
	fControl->SetPageSize(width,height);
}

/*	@F"XGListView::DrawLine" @I
 *
 *		Draw a single line, with selection hiliting
 */

void XGListView::DrawLine(XGDraw &draw, long index, short pos, bool hilite)
{
	Rect r,s;
	long xs,ys;
	long h;
	char buffer[256];
	
	r = GetContentRect();
	fControl->GetScrollValue(&xs,&ys);
	h = fControl->GetPixelStep();
	
	s = r;
	s.top = pos * h;
	s.bottom = s.top + h;
	xs = 5 - xs * h;
	ys = s.bottom - draw.GetFontDescent();
	
	if (!::SectRect(&s,&r,&r)) return;			// not here.
	draw.ClipRect(&r);
	
	if (hilite) {
		draw.FillRect(r,KXGColorHilite);
		draw.SetForeColor(KXGColorHiliteText);
	} else {
		draw.EraseRect(r);
	}
	
	draw.MoveTo(xs,ys);
	fControl->GetItem(index,buffer);
	draw.DrawString(buffer);
	draw.SetForeColor(KXGColorBlack);
	
	draw.ClipRect();
}

/*	@F"XGListView::ScrollInRange" @I
 *
 *		Make sure the value specified is scrolled into range
 */

void XGListView::ScrollInRange(long index)
{
	long x,y,z;
	Rect r;
	long h;

	XGDraw draw(this);
	fControl->fFont->Attach();
	draw.SetFont(fControl->fFont);
	
	/*
	 *	Peg index
	 */
	
	if ((index < 0) || (index >= fControl->Length())) return;
	
	/*
	 *	Find the current value range
	 */
	
	r = GetContentRect();
	h = draw.GetFontHeight();
	fControl->GetScrollValue(&x,&y);
	z = y + (r.bottom - r.top)/h;
	if (z <= y) z = y + 1;
	
	if (index < y) {
		fControl->SetScrollValue(x,index);
	} else if (index >= z) {
		y += 1 + index - z;
		fControl->SetScrollValue(x,y);
	}
}
