/*	TErrorView.cpp
 *
 *		Error view editor
 */

/*  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 <XAppMenu.h>
#include <XDraw.h>
#include <XDataUtil.h>
#include <XInlineEdit.h>
#include <XScroll.h>
#include <XMessages.h>
#include "TConstructorApp.h"
#include "TResDocument.h"
#include "TErrorView.h"

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

/*	TErrorView::TErrorView
 *
 *		Error view
 */

TErrorView::TErrorView(XGOwner *owner, XGArgStream &stream) : XGView(owner,stream)
{
	fError = NULL;
	fSelect = -1;
}

/*	TErrorView::~TErrorView
 *
 *		Delete this thing
 */

TErrorView::~TErrorView()
{
}

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

/*	TErrorView::DoDrawView
 *
 *		Draw this thing
 */

void TErrorView::DoDrawView(Rect r)
{
	XGScrollView *v;
	long x,y;
	long i,len;
	long h;
	Rect s;
	long ascent;
	
	XGDraw draw(this);
	
	if (fError == NULL) {
		draw.EraseRect(r);
		return;
	}
	
	draw.SetFont(XGFont::LoadFont(1));	
	v = dynamic_cast<XGScrollView *>(GetParent());
	v->GetScrollValue(&x,&y);
	h = v->GetPixelStep();
	r = GetContentRect();

	/*
	 *	Initialize line drawing and draw 'em
	 */
		
	len = fError->smsg->GetNumStrings();
	i = (r.bottom - r.top + h - 1) / h + y;
	if (len > i) len = i;
	ascent = draw.GetFontAscent();
	
	for (i = y; i < len; i++) {
		s = r;
		s.top = (i - y) * h;
		s.bottom = s.top + h;
		s.left = -x * h;
		
		DrawLine(draw,i,s);
	}
	
	s = r;
	s.top = (len - y) * h;
	if (s.top < s.bottom) {
		draw.EraseRect(s);
	}
}

/*	TErrorView::DoSizeView
 *
 *		Resize the view
 */

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

/*	TErrorView::DoMouseDown
 *
 *		Handle the mouse down event
 */

bool TErrorView::DoMouseDown(Point where, short)
{
	XGScrollView *v;
	long x,y,h,len;
	Rect r;
	
	if (!SetFocus()) return false;
	if (!fError) return false;
	
	/*
	 *	Which line?
	 */
	
	v = dynamic_cast<XGScrollView *>(GetParent());
	v->GetScrollValue(&x,&y);
	h = v->GetPixelStep();
	y += where.v / h;
	
	len = fError->smsg->GetNumStrings();
	if (y >= len) y = -1;
	
	/*
	 *	Now update the selection
	 */
	
	fSelect = y;
	if (fSelect == -1) {
		InvalView();
		return false;
	}
	if (where.h < 35 - x * h) {
		/*
		 *	Selected on number. Permit reordering of things
		 */
		
		fColSel = 0;
		fDrawSel = fSelect;
		r = GetContentRect();
		r.top = fDrawSel * h;
		r.bottom = r.top + 1;

		XGDraw draw(this);
		draw.InvertRect(r);
		
		return true;		//###
	} else if (where.h < 180 - x * h) {
		/*
		 *	Select short string
		 */
		
		fColSel = 1;
		OpenInlineEdit();
		InvalView();
		return false;
	} else {
		/*
		 *	Select long string
		 */
		
		fColSel = 2;
		OpenInlineEdit();
		InvalView();
		return false;
	}
}

/*	TErrorView::DoMouseMove
 *
 *		handle mouse move for reordering string order
 */

void TErrorView::DoMouseMove(Point where, short flags)
{
	XGScrollView *v;
	long x,y,h,len;
	Rect r;

	if (flags & KCapturedMouse) {
		v = dynamic_cast<XGScrollView *>(GetParent());
		v->GetScrollValue(&x,&y);
		h = v->GetPixelStep();
		y += where.v / h;
		len = fError->smsg->GetNumStrings();
		if (y > len) y = len;
		
		if (y != fDrawSel) {
			r = GetContentRect();
			
			XGDraw draw(this);
			
			r.top = y * h;
			r.bottom = r.top + 1;
			draw.InvertRect(r);
			
			r.top = fDrawSel * h;
			r.bottom = r.top + 1;
			draw.InvertRect(r);
			
			fDrawSel = y;
		}
	} else {
		XGView::DoMouseMove(where,flags);
	}
}

/*	TErrorView::DoMouseUp
 *
 *		handle mouse up
 */

void TErrorView::DoMouseUp(Point where, short flags)
{
	char buffer[256];
	
	if (flags & KCapturedMouse) {
		if ((fDrawSel != fSelect) && (fDrawSel != fSelect+1)) {
			/*
			 *	String was actually moved
			 */
			
			if (fDrawSel > fSelect) fDrawSel--;
			fError->smsg->GetString(fSelect,buffer);
			fError->smsg->Delete(fSelect);
			fError->smsg->Insert(fDrawSel,buffer);
			fError->lmsg->GetString(fSelect,buffer);
			fError->lmsg->Delete(fSelect);
			fError->lmsg->Insert(fDrawSel,buffer);
			fSelect = fDrawSel;
		}
		InvalView();
	} else {
		XGView::DoMouseUp(where,flags);
	}
}

/*	TErrorView::Scroll
 *
 *		handle different scrolling for the left column
 */

void TErrorView::Scroll(long x, long y)
{
	Rect r;
	
	r = GetContentRect();
	r.right = 30;
	XGView::ScrollView(0,y,&r);
	
	r = GetContentRect();
	r.left = 30;
	XGView::ScrollView(x,y,&r);
}

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

BEGINDISPATCH(TErrorView,XGView)
	DISPATCHTABLE(KXGDoMenuCommand,TErrorView::DoMenuCommand)
	DISPATCHTABLE(KXGGetMenuStatus,TErrorView::DoGetMenuStatus)
	DISPATCHTABLE(KXGKeyEvent,TErrorView::DoKeyEvent)
	DISPATCHTABLE(KXGInlineClose,TErrorView::DoInlineClose)
ENDDISPATCH

/*	TErrorView::DoInlineClose
 *
 *		Handle inline close event
 */

long TErrorView::DoInlineClose(long, void *)
{
	char buffer[256];
	TResDocument *doc;
	
	fInline->GetTitle(buffer);
	fInline = NULL;
	if (fColSel == 1) {
		fError->smsg->SetString(fSelect,buffer);
	} else if (fColSel == 2) {
		fError->lmsg->SetString(fSelect,buffer);
	}
	
	doc = dynamic_cast<TResDocument *>(GetDocument());
	doc->MarkDirty();
	return 0;
}

/*	TErrorView::DoKeyEvent
 *
 *		Handle special key events
 */

long TErrorView::DoKeyEvent(long arg, void *)
{
	short msg,key;
	
	msg = GETHIWORD(arg);
	key = GETLOWORD(arg);
	
	if (fError == NULL) return -1;		// skip if not editing stuff
	if (!SetFocus()) return -1;			// force me to have focus
	if (msg & KSpecialKey) {
		/*
		 *	Arrow key: navigates
		 */
		
		if (key == KKeyUpCursor) {
			fSelect--;
			if (fSelect < 0) {
				fSelect = fError->smsg->GetNumStrings()-1;
			}
			InvalView();
			return 0;
		} else if (key == KKeyDownCursor) {
			fSelect++;
			if (fSelect >= fError->smsg->GetNumStrings()) {
				fSelect = 0;
			}
			InvalView();
			return 0;
		}
	} else {
		/*
		 *	enter key
		 */
		
		if (key == 0x0D) {
			OpenInlineEdit();
			return 0;
		}
		
		/*
		 *	Tab key navigation
		 */
		
		if (key == '\t') {
			if (msg & KKeyShift) {
				fColSel--;
				if (fColSel < 1) {
					fSelect--;
					if (fSelect < 0) {
						fSelect = fError->smsg->GetNumStrings()-1;
					}
					fColSel = 2;
				}
			} else {
				fColSel++;
				if (fColSel > 2) {
					fSelect++;
					if (fSelect >= fError->smsg->GetNumStrings()) {
						fSelect = 0;
					}
					fColSel = 1;
				}
			}
			InvalView();
			OpenInlineEdit();
			return 0;
		}
	}
	
	return -1;
}

/*	TErrorView::DoMenuCommand
 *
 *		Menu command
 */

long TErrorView::DoMenuCommand(long arg, void *)
{
	switch (arg) {
		case KMenuResourceCreate:
			AppendError();
			return 0;
		case KXGEditClear:
			DeleteError();
			return 0;
		default:
			break;
	}
	return -1;
}

/*	TErrorView::DoGetMenuStatus
 *
 *		Get the menu status for this thing
 */

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


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

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

void TErrorView::OpenInlineEdit()
{
	Rect r,s;
	long x,y;
	long h;
	XGScrollView *v;
	char buffer[256];
	
	/*
	 *	initialize
	 */
	
	if ((fSelect == -1) || (fColSel == 0)) return;
	
	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
	 */
	
	if (fColSel == 1) {
		s.left = 30 - h * x;
		s.right = 210 + x * h;
		if (s.left < 30) {
			s.left = 30;
			s.right = s.left + 180;
		}
	} else if (fColSel == 2) {
		s.left = 210 + x * h;
		s.right = r.right;
		if (s.left < 30) {
			s.left = 30;
		}
	}
	s.top = (fSelect - y) * h;
	s.bottom = s.top + h;
		
	/*
	 *	Pop it open
	 */
	
	if (fColSel == 1) {
		fError->smsg->GetString(fSelect,buffer);
	} else if (fColSel == 2) {
		fError->lmsg->GetString(fSelect,buffer);
	}
	fInline = XGInlineEdit::Create(this,s,1,buffer);
}

/************************************************************************/
/*																		*/
/*	Processing Support													*/
/*																		*/
/************************************************************************/

/*	TErrorView::AppendError
 *
 *		Append a new error message to the end of this and select it
 */

void TErrorView::AppendError(void)
{
	if (!fError) return;
	if (!SetFocus()) return;
	
	fSelect = fError->smsg->GetNumStrings();
	fError->smsg->Append("Untitled");
	fError->lmsg->Append("Unnamed error message");
	
	RecalcScroll();
	InvalView();
}

/*	TErrorView::DeleteError
 *
 *		Delete error message
 */

void TErrorView::DeleteError(void)
{
	if (!fError) return;
	if (!SetFocus()) return;
	
	if (fSelect != -1) {
		fError->smsg->Delete(fSelect);
		fError->lmsg->Delete(fSelect);
		fSelect = -1;
	}
	
	RecalcScroll();
	InvalView();
}

/************************************************************************/
/*																		*/
/*	Scroll bar calcs													*/
/*																		*/
/************************************************************************/

/*	TErrorView::DrawLine
 *
 *		Draw an error line
 */

void TErrorView::DrawLine(XGDraw &draw, long index, Rect r)
{
	short h;
	Rect s;
	char buffer[256];

	/*
	 *	Draw the index
	 */
	
	h = draw.GetFontAscent();
	
	s = r;
	s.left = 0;
	s.right = 30;
	draw.SetBackColor(KXGColorBtnFace);
	draw.EraseRect(s);
	sprintf(buffer,"%ld",index);
	draw.MoveTo(28 - draw.StringWidth(buffer),s.top + h);
	draw.DrawString(buffer);
	draw.SetBackColor(KXGColorWhite);
	
	/*
	 *	Draw short string
	 */
	
	s = r;
	s.left = 30;
	s.right = 210 + r.left;
	if (s.left < s.right) {
		draw.ClipRect(&s);
		
		if ((fSelect == index) && ((fColSel == 0) || (fColSel == 1))) {
			draw.SetForeColor(KXGColorHiliteText);
			draw.SetBackColor(KXGColorHilite);
		}
		draw.EraseRect(s);
		fError->smsg->GetString(index,buffer);
		draw.MoveTo(r.left + 32,s.top + h);
		draw.DrawString(buffer);
		if ((fSelect == index) && ((fColSel == 0) || (fColSel == 1))) {
			draw.SetForeColor(KXGColorBlack);
			draw.SetBackColor(KXGColorWhite);
		}
	}
	
	/*
	 *	Draw long string
	 */
	
	s = r;
	s.left = 210 + r.left;
	if (s.left < 30) s.left = 30;
	if (s.left < s.right) {
		draw.ClipRect(&s);

		if ((fSelect == index) && ((fColSel == 0) || (fColSel == 2))) {
			draw.SetForeColor(KXGColorHiliteText);
			draw.SetBackColor(KXGColorHilite);
		}
		draw.EraseRect(s);
		fError->lmsg->GetString(index,buffer);
		draw.MoveTo(r.left + 212,s.top + h);
		draw.DrawString(buffer);
		if ((fSelect == index) && ((fColSel == 0) || (fColSel == 2))) {
			draw.SetForeColor(KXGColorBlack);
			draw.SetBackColor(KXGColorWhite);
		}
	}
	
	draw.ClipRect();
}

/*	TErrorView::RecalcScroll
 *
 *		Recalculate the scrollbars
 */

void TErrorView::RecalcScroll()
{
	XGScrollView *v;
	long x,y;
	Rect r;
	long h;
		
	XGDraw draw(this);
	draw.SetFont(XGFont::LoadFont(1));
	v = dynamic_cast<XGScrollView *>(GetParent());

	h = draw.GetFontHeight();
	v->SetPixelStep(h);

	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);
	
	if (fError) {
		y = fError->smsg->GetNumStrings() - y;
		if (y < 0) y = 0;
	} else {
		y = 0;
	}
	v->SetMaxValue(1000,y);
}
