/*	TViewTypeList.cpp
 *
 *		View type list; this knows how to edit the contents of a 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 <stdio.h>
#include <string.h>

#include <XScroll.h>
#include <XMessages.h>
#include <XDraw.h>
#include <XWindow.h>
#include <XInlineEdit.h>
#include <XAppMenu.h>
#include <XPopup.h>

#include "TConstructorApp.h"
#include "TResDocument.h"
#include "TViewTypeList.h"

/************************************************************************/
/*																		*/
/*	Class Declarations													*/
/*																		*/
/************************************************************************/

/*	TViewTypeList::TViewTypeList
 *
 *		Construct me
 */

TViewTypeList::TViewTypeList(XGOwner *owner, XGArgStream &stream) : XGView(owner,stream)
{
	fViewType = NULL;
	fSelect = -1;
	fColumn = 0;
}

/*	TViewTypeList::~TViewTypeList
 *
 *		Delete me
 */

TViewTypeList::~TViewTypeList()
{
}

/************************************************************************/
/*																		*/
/*	Drawing																*/
/*																		*/
/************************************************************************/

/*	TViewTypeList::DoDrawView
 *
 *		Draw this view
 */

void TViewTypeList::DoDrawView(Rect area)
{
	Rect r,s;
	long x,y,h;
	long px,py;
	long i,len;
	long max;
	XGScrollView *v;
	char buffer[256];
	char type[6];
	char base[6];
	
	// Is there anything to draw?
	XGDraw draw(this);
	if (!fViewType) {
		draw.EraseRect(area);
		return;						// nope
	}
	
	// Initialize drawing
	draw.SetFont(XGFont::LoadFont(1));
	v = dynamic_cast<XGScrollView *>(GetParent());
	v->GetScrollValue(&x,&y);
	h = v->GetPixelStep();
	
	px = 5 - h * x;
	py = draw.GetFontAscent();
	r = GetContentRect();
	
	// Draw the summary line
	s = r;
	s.bottom = s.top + h;
	draw.SetBackColor(KXGColorBtnFace);
	draw.EraseRect(s);
	draw.MoveTo(px,py);
	
	To4Char(type,fViewType->viewType);
	if (fViewType->childPtr == NULL) {
		sprintf(buffer,"Type '%s'",type);
	} else {
		To4Char(base,fViewType->childPtr->viewType);
		sprintf(buffer,"Type '%s', base type '%s'",type,base);
	}
	draw.DrawString(buffer);
	draw.SetBackColor(KXGColorWhite);
	
	// Do the drawing
	len = fViewType->declaration->GetNumStrings();
	max = (r.bottom - r.top - 1) / h;
	for (i = 0; i < max; i++) {
		if (i + y >= len) break;
		
		if (fSelect == i + y) {
			draw.SetBackColor(KXGColorHilite);
			draw.SetForeColor(KXGColorHiliteText);
		}
		
		s.top = (i+1) * h;
		s.bottom = s.top + h;
		s.left = r.left;
		s.right = r.right;
		draw.EraseRect(s);
		
		DrawLine(draw,px,py + s.top,i+y);
		if (fSelect == i + y) {
			draw.SetBackColor(KXGColorWhite);
			draw.SetForeColor(KXGColorBlack);
			
			if (fColumn == 0) {
				s.right = 55 + px;
			} else {
				s.left = 55 + px;
			}
			if (s.right > s.left) {
				draw.DrawFocusRect(s);
			}
		}
	}
	
	if (i < max) {
		s.top = (i+1) * h;
		s.bottom = r.bottom;
		s.left = r.left;
		s.right = r.right;
		draw.EraseRect(s);
	}
}

/*	TViewTypeList::DoSizeView
 *
 *		Resize the view by resizing the scrollbars
 */

void TViewTypeList::DoSizeView()
{
	RecalcScroll();
}

/*	TViewListType::DoMouseDown
 *
 *		Handle mouse down event by changing the selection and by
 *	handling the inline editing requirements.
 */

bool TViewTypeList::DoMouseDown(Point where, short)
{
	long x,y,h;
	XGScrollView *v;
	Rect r;
	
	/*
	 *	Make sure I have focus
	 */
	
	if (!fViewType) return false;
	if (!SetFocus()) return false;
	
	/*
	 *	Find where I clicked
	 */
	
	XGDraw draw(this);
	draw.SetFont(XGFont::LoadFont(1));
	h = draw.GetFontHeight() + 1;
	v = dynamic_cast<XGScrollView *>(GetParent());
	v->GetScrollValue(&x,&y);
	
	y = (where.v / h) + y - 1;
	if (55 - h * x > where.h) x = 0;
	else x = 1;
	if (y >= fViewType->declaration->GetNumStrings()) y = -1;
	
	/*
	 *	Select anything?
	 */
	
	if (y < 0) {
		fSelect = -1;
		InvalView();
		return false;
	}
	
	/*
	 *	Now figure out what to do depending on what column I was
	 *	clicked in
	 */
	
	fSelect = y;
	fColumn = x;
	InvalView();
	
	v->GetScrollValue(&x,&y);
	r = GetContentRect();
	if ((fSelect - y + 2) * h > r.bottom) {
		v->SetScrollValue(x,y+1);
	}
	
	if (fColumn == 0) {
		OpenPopupMenu();
		return true;
	} else {
		OpenInlineEdit();
		return false;
	}
}

/*	TViewListType::DoMouseMove
 *
 *		Handle mouse move event if I'm dragging a popup window.
 */

void TViewTypeList::DoMouseMove(Point where, short flags)
{
	if (flags & KCapturedMouse) {
		RunPopupMenu(where,flags);
	} else {
		// put to make sure arrow key is correct
		XGView::DoMouseMove(where,flags);
	}
}

/*	TViewListType::DoMouseUp
 *
 *		Handle mouse up if I'm dragging a popup window.
 */

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


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

BEGINDISPATCH(TViewTypeList,XGView)
	DISPATCHTABLE(KXGDoMenuCommand,TViewTypeList::DoMenuCommand)
	DISPATCHTABLE(KXGGetMenuStatus,TViewTypeList::DoGetMenuStatus)
	DISPATCHTABLE(KXGInlineClose,TViewTypeList::DoInlineClose)
ENDDISPATCH

/*	TViewTypeList::DoMenuCommand
 *
 *		Do a menu command
 */

long TViewTypeList::DoMenuCommand(long arg, void *)
{
	switch (arg) {
		case KMenuResourceCreate:
			DoNewItem();
			break;
		case KXGEditClear:
			DoDeleteItem();
			break;
		default:
			return -1;
	}
	return 0;
}

/*	TViewTypeList::DoGetMenuStatus
 *
 *		Get the status of this menu item
 */

long TViewTypeList::DoGetMenuStatus(long arg, void *parg)
{
	XGSMenuStatus *status = (XGSMenuStatus *)parg;
	
	switch (arg) {
		case KMenuResourceCreate:
			if (fViewType) status->enable = true;
			return 0;
		case KXGEditClear:
			if (fViewType) status->enable = true;
			return 0;
		default:
			break;
	}
	return -1;
}

/*	TViewTypeList::DoInlineClose
 *
 *		handle closing of the inline editor
 */

long TViewTypeList::DoInlineClose(long, void *)
{
	char buffer[256];
	TResDocument *doc;
				
	fInline->GetTitle(buffer);
	fInline = NULL;
	fViewType->declaration->SetString(fSelect,buffer);

	doc = dynamic_cast<TResDocument *>(GetDocument());
	doc->MarkDirty();
	
	return 0;
}

/************************************************************************/
/*																		*/
/*	Scroll bars															*/
/*																		*/
/************************************************************************/

/*	TViewTypeList::RecalcScroll
 *
 *		Recalculate the scroll limits
 */

void TViewTypeList::RecalcScroll()
{
	XGScrollView *v;
	long h;
	long x,y;
	Rect r;
	
	if (fViewType == NULL) return;
	
	XGDraw draw(this);
	draw.SetFont(XGFont::LoadFont(1));
	h = draw.GetFontHeight() + 1;
	
	v = dynamic_cast<XGScrollView *>(GetParent());
	if (v == NULL) return;
	v->SetPixelStep(h);
	
	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);
	
	y = fViewType->declaration->GetNumStrings() + 1 - y;
	if (y < 0) y = 0;
	v->SetMaxValue(1000,y);
	
	if (fSelect != -1) {
		v->GetScrollValue(&x,&y);
		v->SetScrollValue(x,fSelect);
	}
}

/*	TViewTypeList::DrawLine
 *
 *		Handle the drawing of the contents of this line
 */

void TViewTypeList::DrawLine(XGDraw &draw, short px, short py, long index)
{
	unsigned char type;
	char buffer[256];
	
	fViewType->declaration->GetExtra(index,(void *)&type);
	draw.MoveTo(px,py);
	switch (type) {
		case 0:
		default:
			draw.DrawString("integer");
			break;
		case 1:
			draw.DrawString("boolean");
			break;
		case 2:
			draw.DrawString("string");
			break;
	}
	
	draw.MoveTo(px+60,py);
	fViewType->declaration->GetString(index,buffer);
	draw.DrawString(buffer);
}

/************************************************************************/
/*																		*/
/*	Menu commands														*/
/*																		*/
/************************************************************************/

/*	TViewTypeList::DoNewItem
 *
 *		Create a new item in this type.
 */

void TViewTypeList::DoNewItem(void)
{
	long index;
	static long gCount = 0;
	char buffer[256];
	TResDocument *doc;
	
	/*
	 *	Append a string to the end of this.
	 */
	
	gCount++;
	if (gCount == 1) strcpy(buffer,"Untitled");
	else sprintf(buffer,"Untitled %ld",gCount);
	
	index = fViewType->declaration->GetNumStrings();
	fViewType->declaration->Insert(index,buffer);
	fSelect = index;
	RecalcScroll();
	InvalView();
	
	doc = dynamic_cast<TResDocument *>(GetDocument());
	doc->MarkDirty();
}

/*	TViewTypeList::DoDeleteItem
 *
 *		Delete an item from the ist
 */

void TViewTypeList::DoDeleteItem(void)
{
	long len;
	TResDocument *doc;
	
	/*
	 *	Append a string to the end of this.
	 */
	
	len = fViewType->declaration->GetNumStrings();
	if ((fSelect >= 0) && (fSelect < len)) {
		fViewType->declaration->Delete(fSelect);
		fSelect = -1;
		RecalcScroll();
		InvalView();
	}

	doc = dynamic_cast<TResDocument *>(GetDocument());
	doc->MarkDirty();
}

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

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

void TViewTypeList::OpenPopupMenu()
{
	Rect s;
	long x,y;
	long h;
	XGScrollView *v;
	unsigned char value;
	Point size;
	short nright;
	
	/*
	 *	initialize
	 */
	
	XGDraw draw(this);
	draw.SetFont(XGFont::LoadFont(1));
	h = draw.GetFontHeight() + 1;
	v = dynamic_cast<XGScrollView *>(GetParent());
	v->GetScrollValue(&x,&y);
	
	/*
	 *	Get the location of this popup, but don't scroll it over
	 */
	
	s.left = 0;
	s.right = 60;
	s.top = (fSelect - y + 2) * h;

	/*
	 *	Initialize the list
	 */
	
	fPopList.DeleteAll();
	fPopList.Insert(0,"integer");
	fPopList.Insert(1,"boolean");
	fPopList.Insert(2,"string");
	
	/*
	 *	Build the popup window
	 */
	
	fViewType->declaration->GetExtra(fSelect,(void *)&value);
	fPopView = new XGPopMenuView(&fPopList,1,value);
	size = fPopView->GetPopSize(draw);
	s.bottom = s.top + size.v;
	nright = s.left + size.h;
	if (nright > s.right) s.right = nright;
	
	fPopWindow = new XGPopWindow(this,s,fPopView);
}

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

void TViewTypeList::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);
		}
	}
}

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

void TViewTypeList::EndPopupMenu(Point, short)
{
	unsigned char index;
	unsigned char value;
	TResDocument *doc;
	
	if (fPopWindow) {
		index = fPopView->GetLocation();
		delete fPopWindow;
		fPopWindow = NULL;
		
		/*
		 *	Now see if it's time to update
		 */
		
		if (index != -1) {
			fViewType->declaration->GetExtra(fSelect,(void *)&value);
			if (index != value) {
				fViewType->declaration->SetExtra(fSelect,(void *)&index);
				InvalView();
				
				doc = dynamic_cast<TResDocument *>(GetDocument());
				doc->MarkDirty();
			}
		}
	}
}

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

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

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