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

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

  YAAF is Copyright 1997-1999 by William Woody.

  Redistribution and use in source and binary forms, with or without 
  modification, are permitted provided that the following conditions 
  are met:

  Redistributions of source code must retain the above copyright notice, 
  this list of conditions and the following disclaimer.

  Neither the name of the copyright holder or other contributors may
  be used to promote products derived from this software without
  specific written prior permission.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
  SOFTWARE.
  
  Contact:   woody@alumni.caltech.edu
  Web page:  http://www.pandawave.com/yaaf/

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

#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											*/
/*																		*/
/************************************************************************/

/*$	XGListView::XGListView
 *
 *		Construct this list view. This will be locked to the boundaries
 *	of the scrollview
 */

XGListView::XGListView(XGListControl *control) : XGView(control,ViewInitRecord)
{
	fControl = control;
	
	/* Initialize the scrollbar stuff in my parent */
	CalcResize();
}

/*$	XGListView::~XGListView
 *
 *		Delete me
 */

XGListView::~XGListView()
{
}

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

/*$	XGListView::DoDrawView
 *
 *		Handle drawing the contents of this thing
 */

void XGListView::DoDrawView(XRect)
{
	long xs,ys;
	long len;
	long h;
	long x;
	short ypos;
	XRect 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);
	}
}

/*$	XGListView::DoSizeView
 *
 *		Resize this thing
 */

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

/*$	XGListView::DoMouseDown
 *
 *		Handle mouse down event. This flips the selection state of the
 *	specified item
 */

bool XGListView::DoMouseDown(XPoint where, short flags)
{
	long xs,ys;
	long h;
	long ypos;
	XRect 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 (!fControl->fSwitchSelect) {
		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;						//###
}

/*$	XGListView::DoMouseMove
 *
 *		Handle mouse move event. This flips the selection state of the
 *	specified item
 */

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

/*$	XGListView::DoMouseUp
 *
 *		Handle mouse up event. This flips the selection state of the
 *	specified item
 */

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

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

/*$	XGListView::RedrawLine
 *
 *		Redraw the specified line
 */

void XGListView::RedrawLine(long index)
{
	long xs,ys;
	long len;
	long h;
	short ypos;
	XRect 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));
}

/*$	XGListView::CalcResize
 *
 *		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;
	XRect 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);
}

/*$	XGListView::DrawLine
 *
 *		Draw a single line, with selection hiliting
 */

void XGListView::DrawLine(XGDraw &draw, long index, short pos, bool hilite)
{
	XRect 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 (IsEnabled()) {
		if (hilite && IsActive()) {
			draw.FillRect(r,KXGColorHilite);
			draw.SetForeColor(KXGColorHiliteText);
		} else {
			draw.EraseRect(r);
		}
	} else {
		draw.EraseRect(r);
		draw.SetForeColor(KXGColorBtnShadow);	// dimmed text
	}
	
	draw.MoveTo(xs,ys);
	fControl->GetItem(index,buffer);
	draw.DrawString(buffer);
	draw.SetForeColor(KXGColorBlack);
	
	draw.ClipRect();
}

/*$	XGListView::ScrollInRange
 *
 *		Make sure the value specified is scrolled into range
 */

void XGListView::ScrollInRange(long index)
{
	long x,y,z;
	XRect 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);
	}
}