/*	TItemListView.cpp
 *
 *		Background view
 */

/*  YAAF Constructor - Rapid application development for YAAF
 *  Copyright (C) 1998 William Edward Woody
 *  
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *  
 *  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.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program; 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 "TResDocument.h"
#include "LayoutViews.h"
#include <XDataUtil.h>
#include <XMessages.h>
#include <XScroll.h>
#include <XDraw.h>
#include <XPopup.h>
#include <XInlineEdit.h>

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

/*	TItemListView::TItemListView
 *
 *		Build me
 */

TItemListView::TItemListView(XGOwner *owner, XGArgStream &stream) : XGView(owner,stream)
{
	fInline = NULL;
	fSelect = -1;
	fItem = NULL;
	fDoc = NULL;
	fWindow = NULL;
	fType = NULL;
	fPopWindow = NULL;
	fPopView = NULL;
}

/*	TItemListView::~TItemListView
 *
 *		Delete me
 */

TItemListView::~TItemListView()
{
}

/************************************************************************/
/*																		*/
/*	Support																*/
/*																		*/
/************************************************************************/

/*	TItemListView::UpdateSelect
 *
 *		Update the selection list.
 */

void TItemListView::UpdateSelect(ViewItemDeclaration *item, 
								 TResDocument *doc,
								 XGWindow *w)
{
	long tlen,slen,x;
	short type;

	/*
	 *	Update
	 */
	
	if (!SetFocus()) return;				// can't change???
	if ((item == fItem) && (fDoc == doc)) return;
	if ((item == NULL) && (fDoc == NULL)) {
		/*
		 *	Special: we're being closed. Only if this is the current
		 *	window.
		 */
		
		if (fWindow != w) return;
	}
	
	fWindow = w;
	fDoc = doc;
	fItem = item;
	fSelect = -1;

	/*
	 *	Get the item
	 */
	
	if (item == NULL) {
		fType = NULL;
	} else {
		fType = fDoc->GetViewTypeByType(item->viewType);
		
		/*
		 *	Verify the item is the same length, and adjust if needed
		 */
		
		tlen = fType->LenDeclaration();
		slen = fItem->data->GetNumStrings();
		if (tlen != slen) {
			if (slen < tlen) {
				for (x = slen; x < tlen; x++) {
					type = fType->GetDeclItem(x);
					switch (type) {
						case 0:
							fItem->data->Insert(x,"0");
							break;
						case 1:
							fItem->data->Insert(x,"False");
							break;
						case 2:
							fItem->data->Insert(x,"Untitled");
							break;
					}
				}
			} else {
				for (x = tlen; x < slen; x++) {
					fItem->data->Delete(tlen);
				}
			}
		}
	}
	
	RecalcScroll();
	InvalView();
}

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

/*	TItemListView::DoDrawView
 *
 *		Draw me
 */

void TItemListView::DoDrawView(Rect)
{
	long i,len;
	Rect r;
	XGScrollView *v;
	long x,y,h,tmp;
	
	XGDraw draw(this);
	draw.Draw3DRect(r,KXGEBackground);
	r = GetContentRect();
	
	if (fType == NULL) {
		draw.EraseRect(r);
		return;
	}
	
	/*
	 *	Initialize
	 */
	
	v = dynamic_cast<XGScrollView *>(GetParent());
	v->GetScrollValue(&x,&y);
	h = draw.GetFontHeight();
	
	x = 5 - x * h;
	tmp = (r.bottom - r.top + h - 1) / h + y;
	len = fType->LenDeclaration();
	if (len > tmp) len = tmp;
	
	/*
	 *	Draw
	 */
	
	for (i = y; i < len; i++) {
		DrawLine(draw,x,(i-y)*h,i);
	}
	
	r.top = (len-y)*h;
	if (r.top < r.bottom) {
		draw.EraseRect(r);
		r.right = x + 100;				// fill in the rest of the left gray
		draw.Draw3DRect(r,KXGEBackground);
	}
}

/*	TItemListView::DoSizeView
 *
 *		Resize the view
 */

void TItemListView::DoSizeView()
{
	RecalcScroll();
	InvalView();
}

/*	TItemListView::DoMouseDown
 *
 *		Handle mouse down event
 */

bool TItemListView::DoMouseDown(Point where, short)
{
	long x,y,h;
	XGScrollView *v;
	long len;
	
	/*
	 *	Make sure i have focus
	 */
	
	if (!fType) return false;
	if (!SetFocus()) return false;
	
	/*
	 *	Handle mouse click
	 */
	
	XGDraw draw(this);
	draw.SetFont(XGFont::LoadFont(1));
	h = draw.GetFontHeight();
	v = dynamic_cast<XGScrollView *>(GetParent());
	v->GetScrollValue(&x,&y);
	
	y = (where.v / h) + y;
	len = fType->LenDeclaration();
	if (y >= len) {
		fSelect = -1;
		InvalView();
		return false;
	}
	
	/*
	 *	Now see if I should just update select, or edit
	 */
	
	if (105 - x * h > where.h) {
		fSelect = y;
		InvalView();
		return false;
	}
	
	/*
	 *	I'm clicking on something. Now figure out how to handle it
	 */
	
	fSelect = y;
	InvalView();
	
	if (fType->GetDeclItem(fSelect) == 1) {
		OpenPopupMenu();
		return true;
	} else {
		OpenInlineEdit();
		return false;
	}
}

/*	TItemListView::DoMouseMove
 *
 *		Handle mouse move event
 */

void TItemListView::DoMouseMove(Point where, short flags)
{
	if (flags & KCapturedMouse) {
		RunPopupMenu(where,flags);
	} else {
		XGView::DoMouseMove(where,flags);
	}
}

/*	TItemListView::DoMouseUp
 *
 *		handle mouse up event
 */

void TItemListView::DoMouseUp(Point where, short flags)
{
	if (flags & KCapturedMouse) {
		EndPopupMenu(where,flags);
	} else {
		XGView::DoMouseUp(where,flags);
	}
}


/************************************************************************/
/*																		*/
/*	Events																*/
/*																		*/
/************************************************************************/

BEGINDISPATCH(TItemListView,XGView)
	DISPATCHTABLE(KXGInlineClose,TItemListView::DoInlineClose)
	DISPATCHTABLE(KXGKeyEvent,TItemListView::DoKeyEvent)
ENDDISPATCH

/*	TItemListView::DoInlineClose
 *
 *		Handle inline editor close
 */

long TItemListView::DoInlineClose(long, void *)
{
	char buffer[256];
				
	fInline->GetTitle(buffer);
	fInline = NULL;
	fItem->data->SetString(fSelect,buffer);
	fWindow->DoDispatch(KUpdateViewItem,0,NULL);
	fDoc->MarkDirty();
	
	return 0;
}

/*	TItemListView::DoKeyEvent
 *
 *		Handle keyboard event
 */

long TItemListView::DoKeyEvent(long arg, void *)
{
	short msg,key;
	
	msg = GETHIWORD(arg);
	key = GETLOWORD(arg);
	
	if (fType == NULL) return -1;		// skip if not editing stuff
	if (!SetFocus()) return -1;			// force me to have focus
	if (!(msg & KSpecialKey)) {
		/*
		 *	Enter key: edits
		 */
		
		if (key == 0x0D) {
			if (fSelect != -1) {
				if (fType->GetDeclItem(fSelect) == 1) {
					char buffer[256];
					
					fItem->data->GetString(fSelect,buffer);
					if ((buffer[0] == 't') || (buffer[0] == 'T')) {
						fItem->data->SetString(fSelect,"False");
					} else {
						fItem->data->SetString(fSelect,"True");
					}
					fWindow->DoDispatch(KUpdateViewItem,0,NULL);
					fDoc->MarkDirty();
					return 0;
				} else {
					OpenInlineEdit();
					return 0;
				}
			}
			return 0;
		}
		
		/*
		 *	Tab key: navigates
		 */
		
		if (key == '\t') {
			if (msg & KKeyShift) {
				fSelect--;
				if (fSelect < 0) {
					fSelect = fType->LenDeclaration()-1;
				}
			} else {
				fSelect++;
				if (fSelect >= fType->LenDeclaration()) {
					fSelect = 0;
				}
			}
			
			// on tab open this thing
			if (fType->GetDeclItem(fSelect) != 1) {
				OpenInlineEdit();
			}
			InvalView();
			return 0;
		}
	} else {
		/*
		 *	Arrow key: navigates
		 */
		
		if (key == KKeyUpCursor) {
			fSelect--;
			if (fSelect < 0) {
				fSelect = fType->LenDeclaration()-1;
			}
			InvalView();
			return 0;
		} else if (key == KKeyDownCursor) {
			fSelect++;
			if (fSelect >= fType->LenDeclaration()) {
				fSelect = 0;
			}
			InvalView();
			return 0;
		}
	}
	
	return -1;
}

/************************************************************************/
/*																		*/
/*	Internal Routines													*/
/*																		*/
/************************************************************************/

/*	TItemListView::DrawLine
 *
 *		Draw a single line. This draws the name of the argument (from
 *	the view type) and the parameter itself
 */

void TItemListView::DrawLine(XGDraw &draw, short x, short y, short index)
{
	Rect r;
	Rect s;
	char buffer[256];
	
	/*
	 *	Initialize
	 */
	
	if (fItem == NULL) return;	

	/*
	 *	Erase the background to white or the selection color
	 */
	
	if (index == fSelect) {
		draw.SetBackColor(KXGColorHilite);
		draw.SetForeColor(KXGColorHiliteText);
	}
	r = GetContentRect();
	r.top = y;
	r.bottom = r.top + draw.GetFontHeight();
	draw.EraseRect(r);
	
	/*
	 *	Now draw the line
	 */
	
	s = r;
	s.right = x + 100;
	if (s.right > s.left) {
		draw.ClipRect(&s);
		
		if (index != fSelect) {
			draw.SetBackColor(KXGColorBtnFace);
			draw.EraseRect(s);
		}
		fType->GetDeclText(index,buffer);
		draw.MoveTo(x,y+draw.GetFontAscent());
		draw.DrawString(buffer);
		
		if (index != fSelect) {
			draw.SetBackColor(KXGColorWhite);
		}
		draw.ClipRect();
	}
	
	fItem->data->GetString(index,buffer);
	draw.MoveTo(x+105,y+draw.GetFontAscent());
	draw.DrawString(buffer);

	if (index == fSelect) {
		draw.SetBackColor(KXGColorWhite);
		draw.SetForeColor(KXGColorBlack);
	}
}

/*	TItemListView::RecalcScroll
 *
 *		Recalculate the scrollbar values
 */

void TItemListView::RecalcScroll()
{
	long x,y,h;
	Rect r;
	XGScrollView *v;
	
	XGDraw draw(this);
	draw.SetFont(XGFont::LoadFont(1));
	
	/*
	 *	Pixel delta
	 */
	
	v = dynamic_cast<XGScrollView *>(GetParent());
	h = draw.GetFontHeight();
	v->SetPixelStep(h);
	
	/*
	 *	Page delta
	 */
	
	r = GetContentRect();
	x = (r.right - r.left)/h;
	y = (r.bottom - r.top)/h;
	if (x < 1) x = 1;
	if (y < 1) y = 1;
	v->SetPageSize(x,y);
	
	/*
	 *	Max scroll
	 */
	
	if (fType == NULL) {
		v->SetMaxValue(1000,0);
	} else {
		y = fType->LenDeclaration() - y;
		if (y < 0) y = 0;
		v->SetMaxValue(1000,y);
	}
}

/************************************************************************/
/*																		*/
/*	Inline Edit Support													*/
/*																		*/
/************************************************************************/

/*	TItemListView::OpenInlineEdit
 *
 *		Open an inline editor box with the string that is the title
 *	of this parameter
 */

void TItemListView::OpenInlineEdit()
{
	Rect r,s;
	long x,y;
	long h;
	XGScrollView *v;
	char buffer[256];
	
	/*
	 *	initialize
	 */
	
	XGDraw draw(this);
	draw.SetFont(XGFont::LoadFont(1));
	v = dynamic_cast<XGScrollView *>(GetParent());
	v->GetScrollValue(&x,&y);
	r = GetContentRect();
	h = v->GetPixelStep();
	
	/*
	 *	Get the location of this inline editor box
	 */
	
	s.left = 108 - h * x;
	if (s.left < 0) s.left = 0;
	s.right = r.right;
	s.top = (fSelect - y) * h;
	s.bottom = s.top + h;
		
	/*
	 *	Pop it open
	 */
	
	fItem->data->GetString(fSelect,buffer);
	fInline = XGInlineEdit::Create(this,s,1,buffer);
}

/************************************************************************/
/*																		*/
/*	Popup Management													*/
/*																		*/
/************************************************************************/

/*	TItemListView::OpenPopupMenu
 *
 *		Open the popup menu so I can change the type of this thing
 */

void TItemListView::OpenPopupMenu()
{
	Rect s;
	long x,y;
	long h;
	XGScrollView *v;
	bool value;
	Point size;
	char buffer[256];
	
	/*
	 *	initialize
	 */
	
	XGDraw draw(this);
	draw.SetFont(XGFont::LoadFont(1));
	v = dynamic_cast<XGScrollView *>(GetParent());
	v->GetScrollValue(&x,&y);
	h = v->GetPixelStep();

	/*
	 *	Get the location of this popup, but don't scroll it over
	 */
	
	s.left = 105 - x * h;
	if (s.left < 0) s.left = 0;
	s.top = (fSelect - y + 1) * h;

	/*
	 *	Initialize the list
	 */
	
	fPopList.DeleteAll();
	fPopList.Insert(0,"True");
	fPopList.Insert(1,"False");
	
	/*
	 *	Build the popup window
	 */
	
	fItem->data->GetString(fSelect,buffer);
	value = ((buffer[0] == 't') || (buffer[0] == 'T'));
	
	fPopView = new XGPopMenuView(&fPopList,1,value ? 0 : 1);
	size = fPopView->GetPopSize(draw);
	s.bottom = s.top + size.v;
	s.right = s.left + size.h;
	
	fPopWindow = new XGPopWindow(this,s,fPopView);
}

/*	TItemListView::RunPopupMenu
 *
 *		Run the popup menu by shunting events to it.
 */

void TItemListView::RunPopupMenu(Point where, short flags)
{
	Rect r;
	
	if (fPopWindow) {
		r = fPopWindow->GetLocation();
		if (PtInRect(where,&r)) {
			where.h -= r.left;
			where.v -= r.top;
			fPopView->DoMouseMove(where,flags);
		} else {
			// Point not in popup menu; force the popup to be clear
			where.h = -1;
			where.v = -1;
			fPopView->DoMouseMove(where,flags);
		}
	}
}

/*	TItemListView::EndPopupMenu
 *
 *		End the popup menu and update my internal value
 */

void TItemListView::EndPopupMenu(Point, short)
{
	unsigned char index;
	unsigned char value;
	char buffer[256];
	
	if (fPopWindow) {
		index = fPopView->GetLocation();
		delete fPopWindow;
		fPopWindow = NULL;
		
		/*
		 *	Now see if it's time to update
		 */
		
		if (index != -1) {
			fItem->data->GetString(fSelect,buffer);
			value = ((buffer[0] == 't') || (buffer[0] == 'T')) ? 0 : 1;
			if (index != value) {
				if (index == 1) {
					fItem->data->SetString(fSelect,"False");
				} else {
					fItem->data->SetString(fSelect,"True");
				}
				InvalView();
				
				fDoc->MarkDirty();
				fWindow->DoDispatch(KUpdateViewItem,0,NULL);
			}
		}
	}
}
