/*	TProjectView.cpp
 *
 *		This is the view class which displays the contents of a project.
 *	This also dispatches several different commands when different
 *	entries are double-clicked on, and also handles inline editing
 *	of the different parameters
 */

/*  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 "TConstructorApp.h"
#include "TResDocument.h"
#include "TProjectView.h"

#include <XAppMenu.h>
#include <XDraw.h>
#include <XScroll.h>
#include <string.h>

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

/*	TProjectView::TProjectView
 *
 *		Construct my view object
 */

TProjectView::TProjectView(XGOwner *owner, XGArgStream &stream) : XGView(owner,stream)
{
	fSelLine = -1;
	fSelType = -1;
	fOpenFlags = 0;
}

/*	TProjectView::~TProjectView
 *
 *		Destruct me
 */

TProjectView::~TProjectView()
{
}

/************************************************************************/
/*																		*/
/*	Drawing Routines													*/
/*																		*/
/************************************************************************/

/*	TProjectView::DoDrawView
 *
 *		Draw the contents of this view. This draws the list of resources
 *	and the resources that are contained in there
 */

void TProjectView::DoDrawView(Rect)
{
	long x,y;
	long h;
	Rect r,s;
	XGScrollView *v;
	TResDocument *doc;
	short px,py;
	long i,len;
	long lpos;
	long start,end;
	short type;
	Rect t;
	XGDraw draw(this);
	draw.SetFont(XGFont::LoadFont(1));

	/*
	 *	Initialization
	 */
		
	v = dynamic_cast<XGScrollView *>(GetParent());
	doc = dynamic_cast<TResDocument *>(GetDocument());

	/*
	 *	Get the page size
	 */
	
	r = GetContentRect();
	v->GetScrollValue(&x,&y);
	h = v->GetPixelStep();

	start = y;
	end = y + (r.bottom - r.top + h - 1) / h;
	
	/*
	 *	Draw the lines
	 */
	
	px = 5 - h * x;
	py = draw.GetFontAscent() + 1;
	s = r;
	s.top = 0;
	s.bottom = h;
	
	// ViewTypes
	lpos = 0;
	for (type = 0; type < 4; type++) {
		if ((lpos >= start) && (lpos < end)) {
			draw.MoveTo(px,py);				// white border along
			t = s;							// top of gray area
			t.bottom = s.top + 1;
			draw.EraseRect(t);
			draw.SetBackColor(KXGColorBtnFace);
			t = s;
			t.top++;
			draw.EraseRect(t);
			DrawResourceHeader(draw,px,py,type);
			draw.SetBackColor(KXGColorWhite);
			py += h;
			s.top = s.bottom;
			s.bottom = s.bottom + h;
		}
		lpos++;

		if (fOpenFlags & (1 << type)) {
			switch (type) {
				case 0:
					len = doc->GetNumViewType();
					break;
				case 1:
					len = doc->GetNumViews();
					break;
				case 2:
					len = doc->GetNumErrors();
					break;
				case 3:
					len = doc->GetNumFonts();
					break;
			}
			for (i = 0; i < len; i++) {
				if ((lpos >= start) && (lpos < end)) {
				
					if ((fSelType == type) && (fSelLine == i)) {
						draw.SetBackColor(KXGColorHilite);
						draw.SetForeColor(KXGColorHiliteText);
					}
					
					draw.EraseRect(s);
					DrawResourceLine(draw,px,py,type,i);
					
					if ((fSelType == type) && (fSelLine == i)) {
						draw.SetBackColor(KXGColorWhite);
						draw.SetForeColor(KXGColorBlack);
					}
					py += h;
					s.top = s.bottom;
					s.bottom = s.bottom + h;
				}
				lpos++;
			}
		}
	}
	
	/*
	 *	Erase the bottom
	 */
	
	if (lpos < end) {
		r.top = (lpos - start) * h;
		if (r.top < r.bottom) {
			draw.EraseRect(r);
		}
	}
}

/*	TProjectView::DoSizeView
 *
 *		Resize this view; recalculates the scrollbars
 */

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

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

bool TProjectView::DoMouseDown(Point where, short flags)
{
	XGScrollView *v;
	TResDocument *doc;
	long i,len;
	long x,y;
	long h;

	v = dynamic_cast<XGScrollView *>(GetParent());
	doc = dynamic_cast<TResDocument *>(GetDocument());
	
	len = GetDispLines();
	v->GetScrollValue(&x,&y);
	h = v->GetPixelStep();
	
	/*
	 *	Calculate where the selection went
	 */
	
	i = where.v / h + y;
	if (i >= len) i = -1;
	if (!GetSelectItem(i,&fSelType,&fSelLine)) {
		fSelType = -1;
		fSelLine = -1;
	} else {
		if (fSelLine == -1) {
			/*
			 *	Open/Close flag
			 */
			
			fOpenFlags ^= (1 << fSelType);
			fSelType = -1;
			RecalcScroll();
			InvalView();
			return false;
		}
	}
	
	InvalView();
	if (flags & KDoubleClick) {
		OpenSelect();
	}
	return false;
}

/************************************************************************/
/*																		*/
/*	Event Processing													*/
/*																		*/
/************************************************************************/

BEGINDISPATCH(TProjectView,XGView)
	DISPATCHTABLE(KXGEventDocChanged,TProjectView::DoDocChanged)
	DISPATCHTABLE(KXGDoMenuCommand,TProjectView::DoMenuCommand)
	DISPATCHTABLE(KXGGetMenuStatus,TProjectView::DoGetMenuStatus)
	DISPATCHTABLE(KChangeSelect,TProjectView::DoChangeSelect)
ENDDISPATCH

/*	TProjectView::DoChangeSelect
 *
 *		This is sent when the document creates a new item or deletes
 *	an item. This sets my selection state to the indicated value
 */

long TProjectView::DoChangeSelect(long, void *parg)
{
	ChangeSelect *c = (ChangeSelect *)parg;
	
	fSelLine = c->index;
	fSelType = c->type;
	return -1;
}

/*	TProjectView::DoDocChanged
 *
 *		Handle the changing document. This simply invalidates and
 *	recalculates the scrollbar dimensions.
 */

long TProjectView::DoDocChanged(long, void *)
{
	RecalcScroll();
	InvalView();
	return -1;
}

/*	TProjectView::DoGetMenuStatus
 *
 *		Get the status of this thing
 */

long TProjectView::DoGetMenuStatus(long arg, void *parg)
{
	XGSMenuStatus *status = (XGSMenuStatus *)parg;
	
	switch (arg) {
		case KMenuResourceEdit:
		case KMenuResourceGetInfo:
		case KMenuResourceCreate:
		case KXGEditClear:
			status->enable = true;
			return 0;
	}
	return -1;
}

/*	TProjectView::DoMenuCommand
 *
 *		Perform the specified command.
 */

long TProjectView::DoMenuCommand(long arg, void *)
{
	TResDocument *doc;
	
	switch (arg) {
		case KMenuResourceCreate:
			doc = dynamic_cast<TResDocument *>(GetDocument());
			doc->DoCreateResource();
			return 0;
		case KMenuResourceEdit:
			OpenSelect();
			return 0;
		case KMenuResourceGetInfo:
			GetSelectInfo();
			return 0;
		case KXGEditClear:
			DeleteItem();
			return 0;
	}
	return -1;
}

/************************************************************************/
/*																		*/
/*	Selection Processing												*/
/*																		*/
/************************************************************************/

/*	TProjectView::GetDispLines
 *
 *		Get the number of lines that will be displayed given the open state
 *	of this thing
 */

long TProjectView::GetDispLines(void)
{
	TResDocument *doc;
	long len;

	doc = dynamic_cast<TResDocument *>(GetDocument());
	len = 4;
	if (fOpenFlags & 1) len += doc->GetNumViewType();
	if (fOpenFlags & 2) len += doc->GetNumViews();
	if (fOpenFlags & 4) len += doc->GetNumErrors();
	if (fOpenFlags & 8) len += doc->GetNumFonts();
	return len;
}

/*	TProjectView::GetSelectItem
 *
 *		Scan and return the type and index given the fSelect value
 */

bool TProjectView::GetSelectItem(long select, short *type, long *index)
{
	TResDocument *doc;
	long len;

	doc = dynamic_cast<TResDocument *>(GetDocument());
	
	if (fOpenFlags & 1) {
		len = 1 + doc->GetNumViewType();
		if (select < len) {
			if (select >= 0) {
				*index = select - 1;
				*type = 0;
				return true;
			} else return false;
		}
		select -= len;
	} else {
		if (select == 0) {
			*index = -1;
			*type = 0;
			return true;
		}
		select--;
	}
	
	if (fOpenFlags & 2) {
		len = 1 + doc->GetNumViews();
		if (select < len) {
			if (select >= 0) {
				*index = select - 1;
				*type = 1;
				return true;
			} else return false;
		}
		select -= len;
	} else {
		if (select == 0) {
			*index = -1;
			*type = 1;
			return true;
		}
		select--;
	}
	
	if (fOpenFlags & 4) {
		len = 1 + doc->GetNumErrors();
		if (select < len) {
			if (select >= 0) {
				*index = select - 1;
				*type = 2;
				return true;
			} else return false;
		}
		select -= len;
	} else {
		if (select == 0) {
			*index = -1;
			*type = 2;
			return true;
		}
		select--;
	}
	
	if (fOpenFlags & 8) {
		len = 1 + doc->GetNumFonts();
		if (select < len) {
			if (select >= 0) {
				*index = select - 1;
				*type = 3;
				return true;
			} else return false;
		}
	} else {
		if (select == 0) {
			*index = -1;
			*type = 3;
			return true;
		}
		select--;
	}
	
	return false;
}

/*	TProjectView::OpenSelect
 *
 *		Open the specified resource editor by punting this to the document
 *	view
 */

void TProjectView::OpenSelect(void)
{
	TResDocument *doc;
	
	if ((fSelType != -1) && (fSelLine != -1)) {
		doc = dynamic_cast<TResDocument *>(GetDocument());
		doc->DoEditResource(fSelType,fSelLine);
	}
}

/*	TProjectView::GetSelectInfo
 *
 *		Get info about the specified resource by punting upwards
 */

void TProjectView::GetSelectInfo(void)
{
	TResDocument *doc;
	
	if ((fSelType != -1) && (fSelLine != -1)) {
		doc = dynamic_cast<TResDocument *>(GetDocument());
		doc->DoGetResInfo(fSelType,fSelLine);
	}
}

/*	TProjectView::DeleteItem
 *
 *		Delete the selected item
 */

void TProjectView::DeleteItem(void)
{
	TResDocument *doc;
	
	if ((fSelType != -1) && (fSelLine != -1)) {
		doc = dynamic_cast<TResDocument *>(GetDocument());
		doc->DoDeleteResource(fSelType,fSelLine);
	}
}

/************************************************************************/
/*																		*/
/*	Drawing Routines													*/
/*																		*/
/************************************************************************/

/*	TProjectView::DrawResourceLine
 *
 *		Draw the resource header line
 */

void TProjectView::DrawResourceLine(XGDraw &draw, short x, short y, short resType, long index)
{
	TResDocument *resDoc;
	ViewTypeRecord *vtype;
	ViewDeclaration *vitem;
	ErrorDeclaration *error;
	FontDeclaration *font;
	char buffer[256];
	char number[32];
	
	XGFont *oldf;
	XGFont *bold;
	
	resDoc = dynamic_cast<TResDocument *>(GetDocument());
	if (resDoc == NULL) return;
	
	switch (resType) {
		default:
		case 0:
			// Print the type
			vtype = resDoc->GetViewTypeByIndex(index);
			To4Char(number,vtype->viewType);
			draw.MoveTo(x,y);
			draw.DrawString(number);
			
			// Print the child type
			if (vtype->childPtr) {
				To4Char(number,vtype->childPtr->viewType);
				draw.MoveTo(x+50,y);
				draw.DrawString(number);
			}
			
			// No delete? Print the name as bold
			if (vtype->noDelete) {
				oldf = draw.GetFont();
				bold = XGFont::GetFontVariant(oldf,KXGFontBold);
				draw.SetFont(bold);
			}
			
			draw.MoveTo(x+100,y);
			draw.DrawString(vtype->className);
			
			if (vtype->noDelete) {
				draw.SetFont(oldf);
			}
			return;
			
		case 1:
			vitem = resDoc->GetViewDeclaration(index);
			strcpy(buffer,vitem->viewResName);
			sprintf(number,"%d",vitem->viewResID);
			break;
		case 2:
			error = resDoc->GetErrorDeclaration(index);
			strcpy(buffer,error->errorResName);
			sprintf(number,"%d",error->errorResID);
			break;
		case 3:
			font = resDoc->GetFontDeclaration(index);
			strcpy(buffer,font->fontResName);
			sprintf(number,"%d",font->fontResID);
			break;
	}
	
	draw.MoveTo(x,y);
	draw.DrawString(number);
	draw.MoveTo(x+50,y);
	draw.DrawString(buffer);
}

/*	TProjectView::DrawResourceLine
 *
 *		Draw the resource header line
 */

void TProjectView::DrawResourceHeader(XGDraw &draw, short x, short y, short resType)
{
	char *msg;
	XGFont *oldf;
	XGFont *bold;
	Rect r;
	short tmpa,tmpb;
	
	switch (resType) {
		default:
		case 0:		msg = "View Types";				break;
		case 1:		msg = "View Declarations";		break;
		case 2:		msg = "Error Messages";			break;
		case 3:		msg = "Font Declarations";		break;
	}
	
	oldf = draw.GetFont();
	bold = XGFont::GetFontVariant(oldf,KXGFontBold);
	draw.SetFont(bold);

	/*
	 *	Draw arrow
	 */
		
	r.left = x;
	r.bottom = y + draw.GetFontDescent()-1;
	r.top = y + 1 - draw.GetFontAscent();
	r.right = r.left + (r.bottom - r.top);
	
	if (fOpenFlags & (1 << resType)) {
		tmpa = (r.bottom * 3 + r.top) / 4;
		tmpb = (r.bottom + 3 * r.top) / 4;
		draw.MoveTo(r.left,tmpb);
		draw.LineTo(r.right,tmpb);
		draw.LineTo((r.left+r.right)/2,tmpa);
		draw.LineTo(r.left,tmpb);
	} else {
		tmpa = (r.left + 3 * r.right) / 4;
		tmpb = (r.left * 3 + r.right) / 4;
		draw.MoveTo(tmpb,r.top);
		draw.LineTo(tmpb,r.bottom);
		draw.LineTo(tmpa,(r.top + r.bottom)/2);
		draw.LineTo(tmpb,r.top);
	}

	/*
	 *	Draw string
	 */
	
	draw.MoveTo(r.right + 12,y);
	draw.DrawString(msg);
	
	draw.SetFont(oldf);
}

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

/*	TProjectView::RecalcScroll
 *
 *		Recalculate the scrollbar min/max
 */

void TProjectView::RecalcScroll(void)
{
	TResDocument *doc;
	XGScrollView *v;
	Rect r;
	long x,y;
	long len;
	long h;
	
	/*
	 *	Calculate the step size
	 */
	
	XGDraw draw(this);
	draw.SetFont(XGFont::LoadFont(1));
	v = dynamic_cast<XGScrollView *>(GetParent());
	doc = dynamic_cast<TResDocument *>(GetDocument());
	h = draw.GetFontHeight() + 1;
	v->SetPixelStep(h);
	
	/*
	 *	Calculate the page size
	 */
	
	r = GetContentRect();
	x = (r.right - r.left) / h;
	if (x < 1) x = 1;
	y = (r.bottom - r.top) / h;
	if (y < 1) y = 1;
	v->SetPageSize(x,y);
	
	/*
	 *	Calculate the max scroll values
	 */
	
	len = GetDispLines();
	len -= y;
	if (len < 0) len = 0;
	v->SetMaxValue(1000,len);
}
