/*	XGDialog.cpp
 *
 *		This is the dialog handling routines. This knows how to
 *	handle both modal and modeless dialogs.
 *
 *		A modeless dialog is really not much more than a window.
 *	A modal dialog is the same, except that the rest of the application
 *	is deactivated until the window is dismissed.
 */

/*  YAAF - Yet another application framework
 *  Copyright (C) 1997, 1998 William Edward Woody
 *  
 *  This library is free software; you can redistribute it
 *  and/or modify it under the terms of the GNU Library
 *  General Public License as published by the Free Software
 *  Foundation; either version 2 of the License, or any
 *  later version.
 *  
 *  This library is distributed in the hope that it will be
 *  useful, but WITHOUT ANY WARRANTY; without even the implied
 *  warranty of MERCHANTABIILITY or FITNESS FOR A PARTICULAR
 *  PURPOSE. See the GNU Library General Public License for
 *  more details.
 *  
 *  You should have received a copy of the GNU Library General
 *  Public License along with this library; 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 <XConfig.h>			// must be first to optimize compile on MSVC++
#include <string.h>
#include <stdio.h>

#include <XDialog.h>
#include <XApplication.h>
#include <XMessages.h>
#include <XDataUtil.h>

#include <XTextControls.h>
#include <XButtons.h>

/************************************************************************/
/*																		*/
/*	Constants															*/
/*																		*/
/************************************************************************/

/*	InitRecord
 *
 *		The initialization record for my popup view.
 */

static XGSViewInitRecord GInitRecord = {
	'****', -1, 0,
	true, true, true, false,
	0, 0, 0, 20,
	true, true, false
};

/************************************************************************/
/*																		*/
/*	Globals																*/
/*																		*/
/************************************************************************/

#if OPT_XWIN == 1
extern bool		_GDialogFlag;
#endif

/************************************************************************/
/*																		*/
/*	Modal Dialog Declarations											*/
/*																		*/
/************************************************************************/

/*	@F"XGDialog::XGDialog" @I
 *
 *		Construct me
 */

XGDialog::XGDialog(XGSWindowInitRecord &x, XGDocument *d, XGForm *f) : 
	XGWindow(x,d,f)
{
	fDataPtr = NULL;
	fDialogProc = NULL;
	fTermValue = 0;
	fTerminate = false;
	fModal = false;
	
	fOK = 0;
	fCancel = 0;
	
	/*
	 *	Now initialize the contained view
	 */
	
	fDialogView = new XGDialogView(this,GInitRecord);
	SetWindowView(fDialogView);
}

XGDialog::XGDialog(XGArgStream &x, XGDocument *d, XGForm *f) : 
	XGWindow(x,d,f)
{
	fDataPtr = NULL;
	fDialogProc = NULL;
	fTermValue = 0;
	fTerminate = false;
	fModal = false;
	
	fOK = x.ReadInteger();
	fCancel = x.ReadInteger();		/* Get OK/Cancel controls */
	
	/*
	 *	Now initialize the contained view
	 */
	
	fDialogView = new XGDialogView(this,GInitRecord);
	SetWindowView(fDialogView);
}

/*	@F"XGDialog::~XGDialog" @I
 *
 *		Destroy me
 */

XGDialog::~XGDialog()
{
	if (fDialogProc) {
		SendMessage(KXGDialogMessage,KXGDMEndDialog,NULL);
	}
	
	fDialogProc = NULL;
	fDataPtr = NULL;
	fTerminate = true;
}

/*	@F"XGDialog::CloseWindow" @I
 *
 *		An alternate way to close this; on the Microsoft Windows
 *	platform this is called when the window's close box is clicked
 */

bool XGDialog::CloseWindow()
{
	EndDialog(0);
	return true;
}

/************************************************************************/
/*																		*/
/*	Modeless creation/destruction										*/
/*																		*/
/************************************************************************/

/*	@F"XGDialog::CreateInit" @I
 *
 *		Internal routine for creating this thing
 */

XGDialog *XGDialog::CreateInit(XGDialog *d, XGPDialogProc proc, void *data)
{
	XGCommandButton *b;
	long ret;
	
	/*
	 *	Now tell me what's up
	 */
	
	if (d) {
		d->fDataPtr = data;
		d->fDialogProc = proc;
		
		ret = d->SendMessage(KXGDialogMessage,KXGDMInitDialog,NULL);
		if (ret == -1) ret = 0;
		if (!ret) {
			XGView *v;
			
			v = d->GetView()->FindFirstView();
			if (v) v->SetFocus();

			b = dynamic_cast<XGCommandButton *>(d->FindViewByID(d->fOK));
			if (b) b->SetDefault(true);
		}
		if (d->fTerminate) {
			delete d;
			return NULL;
		}
	}
	return d;
}

/*	@F"XGDialog::Create" @I
 *
 *		Create this thing from the resource ID
 */

XGDialog *XGDialog::Create(short resID, XGPDialogProc proc, void *data)
{
	XGDialog *d;
	
	/*
	 *	Create my window
	 */
	
	d = (XGDialog *)XGWindowFactory::CreateFromResource(resID);
	SetCTorDispatch(NULL);
	return CreateInit(d,proc,data);
}

/*	@F"XGDialog::Create" @I
 *
 *		Create this thing from the data
 */

XGDialog *XGDialog::Create(const unsigned char *dptr, 
						   XGPDialogProc proc, 
						   void *data)
{
	XGDialog *d;
	
	/*
	 *	Create my window
	 */
	
	d = (XGDialog *)XGWindowFactory::CreateFromData(dptr);
	SetCTorDispatch(NULL);
	return CreateInit(d,proc,data);
}

/************************************************************************/
/*																		*/
/*	Modal Processing													*/
/*																		*/
/************************************************************************/

/*	@F"XGDialog::ModalRun" @I
 *
 *		The heart of the modal processing code
 */

long XGDialog::ModalRun(XGDialog *d)
{
	long l;
	
	if (d == NULL) return 0;
	
	/*
	 *	Run the event loop until I'm told to terminate
	 */
	
#if OPT_WINOS == 1
	EnableWindow(_GMainWindow,FALSE);		// Disable the main window
#endif

	d->fModal = true;						// I'm a modal dialog
	for (;;) {
		XGAppCore::GetApplication()->RunOnce(d);
		if (d->fTerminate) break;
	}

#if OPT_WINOS == 1
	EnableWindow(_GMainWindow,TRUE);		// Re-enable it again
#endif
	
	/*
	 *	Now it's time to quit. Quit.
	 */
	
	l = d->fTermValue;
	delete d;
	return l;
}

/*	@F"XGDialog::ModalDialog" @I
 *
 *		This is the modal dialog version. On the Macintosh and on
 *	Windows I do all sorts of wierd magic stuff.
 */

long XGDialog::ModalDialog(short resID, XGPDialogProc proc, void *data)
{
	XGDialog *d;
	
	/*
	 *	Create the thing
	 */

#if OPT_XWIN == 1
	_GDialogFlag = true;				/* (XWindows) force window floating */
#endif
	d = Create(resID,proc,data);
#if OPT_XWIN == 1
	_GDialogFlag = false;
#endif

	return ModalRun(d);
}

long XGDialog::ModalDialog(const unsigned char *dptr,
						   XGPDialogProc proc, 
						   void *data)
{
	XGDialog *d;
	
	/*
	 *	Create the thing
	 */

#if OPT_XWIN == 1
	_GDialogFlag = true;				/* (XWindows) force window floating */
#endif
	d = Create(dptr,proc,data);
#if OPT_XWIN == 1
	_GDialogFlag = false;
#endif

	return ModalRun(d);
}

/*	@F"XGDialog::ModalAlert" @I
 *
 *		Alert routine; this spins until a button is pressed. This
 *	simplifies the implementation of custom alerts
 */

long XGDialog::ModalAlert(short al, 
						  const char *a, 
						  const char *b, 
						  const char *c, 
						  const char *d)
{
	const char *list[4];
	
	list[0] = a;
	list[1] = b;
	list[2] = c;
	list[3] = d;
	return ModalDialog(al,AlertProc,(void *)list);
}

long XGDialog::ModalAlert(const unsigned char *data, 
						  const char *a, 
						  const char *b, 
						  const char *c, 
						  const char *d)
{
	const char *list[4];
	
	list[0] = a;
	list[1] = b;
	list[2] = c;
	list[3] = d;
	return ModalDialog(data,AlertProc,(void *)list);
}

/*	@F"XGDialog::AlertProc" @I
 *
 *		run the alert proc by inserting the text of the four
 *	fields into the locations specified by '^0' through '^3'
 *	in the text fields
 */

long XGDialog::AlertProc(XGDialog *d, void *ptr, long msg, long arg, void *)
{
	XGStaticText *text;
	XGView *view;
	
	if (msg == KXGDialogMessage) {
		if (arg == KXGDMInitDialog) {
			char buffer[256];

			/*
			 *	Run through all of the text items in the alert
			 *	and perform text substitution
			 */
			
			for (view = d->fDialogView->GetChild(); view != NULL; view = view->GetSibling()) {
				text = dynamic_cast<XGStaticText *>(view);
				if (text) {
					text->GetTitle(buffer);
					ModifyText(buffer,(const char **)ptr);
					text->SetTitle(buffer);
				}
			}
			
			/*
			 *	Now do the same for me
			 */
			
			d->GetTitle(buffer);
			ModifyText(buffer,(const char **)ptr);
			d->SetTitle(buffer);
		}
		return -1;
	}
	
	if (msg == KXGControlClick) {
		d->EndDialog(arg);
		return 0;
	}
	
	return -1;
}

/*	@F"XGDialog::ModifyText" @I
 *
 *		Modify the text in a field by inserting into '^n'
 */

void XGDialog::ModifyText(char *text, const char **list)
{
	char buffer[256];
	char *b;
	const char *c;
	short i;
	
	b = text;
	i = 0;
	while (*b && (i < 255)) {
		if (*b == '^') {
			if ((b[1] >= '0') && (b[1] <= '3')) {
				c = list[b[1] - '0'];
				b += 2;
				
				while ((*c) && (i < 255)) {
					buffer[i++] = *c++;
				}
				continue;
			}
		}
		buffer[i++] = *b++;
	}
	buffer[i] = 0;
	
	strcpy(text,buffer);
}

/*	@F"XGDialog::EndDialog" @I
 *
 *		End this dialog. Destroy if we're a modeless
 */

void XGDialog::EndDialog(long v)
{
	fTerminate = true;
	fTermValue = v;
	
	if (!fModal) delete this;
}

/************************************************************************/
/*																		*/
/*	Dispatch Support													*/
/*																		*/
/************************************************************************/

BEGINDISPATCHPROC(XGDialog,XGWindow,XGDialog::Dispatch)
	DISPATCHTABLE(KXGKeyEvent,XGDialog::DoKey)
	
#if OPT_MACOS
	DISPATCHTABLE(KXGMenuKey,XGDialog::DoMenuKey)
#endif

ENDDISPATCH

/*	XGDialog::Dispatch
 *
 *		Process dispatch message
 */

long XGDialog::Dispatch(long msg, long arg, void *parg)
{
	long l;
	
	l = SendMessage(msg,arg,parg);
	if ((l == -1) && (!fTerminate)) {
		l = XGWindow::Dispatch(msg,arg,parg);
	}
	return l;
}

#if OPT_MACOS

/*	XGDialog::DoMenuKey
 *
 *		Handle menu key
 */

long XGDialog::DoMenuKey(long k, void *)
{
	short key,code;
	XGCommandButton *b;
	
	code = GETHIWORD(k);
	key  = GETLOWORD(k);

	if ((key == '.') && (code & KKeyCommand)) {
		/* Use command+'.' as escape */
		b = dynamic_cast<XGCommandButton *>(FindViewByID(fCancel));
		if (b) b->PushButton();
		return 0;
	}
	return -1;
}

#endif

/*	XGDialog::DoKey
 *
 *		Handle key
 */

long XGDialog::DoKey(long k, void *)
{
	short key,code;
	XGCommandButton *b;
	
	code = GETHIWORD(k);
	key  = GETLOWORD(k);

#if OPT_MACOS
	/* Ignore control key combinations */
	if (code & KKeyControl) return -1;
#endif

#if OPT_XWIN
	if (((key == 'c') || (key == 'C')) && (code & KKeyControl)) {
		/* Translate 'control+c' into escape */
		key = KKeyEscape;
	}	
#endif

	if ((key == 0x0D) && !(code & KSpecialKey) && fOK) {
		b = dynamic_cast<XGCommandButton *>(FindViewByID(fOK));
		if (b) b->PushButton();
		return 0;
	}
	
	if ((key == KKeyEscape) && (code & KSpecialKey) && fCancel) {
		b = dynamic_cast<XGCommandButton *>(FindViewByID(fCancel));
		if (b) b->PushButton();
		return 0;
	}
	

	return -1;
}

/************************************************************************/
/*																		*/
/*	Dispatch Support													*/
/*																		*/
/************************************************************************/

/*	@F"XGDialog::SendMessage" @I
 *
 *		Send a message to me
 */

long XGDialog::SendMessage(long msg, long arg, void *parg)
{
	if (fDialogProc) {
		return fDialogProc(this,fDataPtr,msg,arg,parg);
	} else {
		return 0;
	}
}

/************************************************************************/
/*																		*/
/*	Contents Management													*/
/*																		*/
/************************************************************************/

/*	@F"XGDialog::GetText" @I
 *
 *		Get the text
 */

void XGDialog::GetText(short id, short len, char *msg)
{
	char buffer[256];
	XGView *view;
	
	/*
	 *	Get the title from the appropriate view. If this fails, it
	 *	fails.
	 */
	
	view = FindViewByID(id);
	if (view == NULL) return;				/* No view exists by ID		*/
	view->GetTitle(buffer);
	if (len <= 255) buffer[len-1] = 0;
	strcpy(msg,buffer);
}

/*	@F"XGDialog::SetText" @I
 *
 *		Set the text for this thing
 */

void XGDialog::SetText(short id, const char *msg)
{
	XGView *view;
	
	/*
	 *	Set the title from the appropriate view. If this fails, it
	 *	fails.
	 */
	
	view = FindViewByID(id);
	if (view == NULL) return;				/* No view exists by ID		*/
	view->SetTitle(msg);
}

/*	@F"XGDialog::GetValue" @I
 *
 *		Get the integer value of this thing. This converts from text
 *	if this is a text object
 */

long XGDialog::GetValue(short id)
{
	char buffer[256];
	XGControl *btn;
	XGView *view;
	
	/*
	 *	First, get the view by it's ID.
	 */
	
	view = FindViewByID(id);
	if (view == NULL) return 0;				/* No view exists by ID		*/
		
	/*
	 *	Try massaging this into a button
	 */
	
	btn = dynamic_cast<XGControl *>(view);
	if (btn) {
		/*
		 *	This was a button. Do a 'get'
		 */
		
		return btn->GetValue();
	}
	
	/*
	 *	If this isn't a control, go ahead and get the title instead.
	 */
	 
	view->GetTitle(buffer);
	return atol(buffer);
}

/*	@F"XGDialog::SetValue" @I
 *
 *		Get the integer value of this thing. This converts from text
 *	if this is a text object
 */

void XGDialog::SetValue(short id, long value)
{
	char buffer[256];
	XGControl *btn;
	XGView *view;
	
	/*
	 *	First, get the view by it's ID.
	 */
	
	view = FindViewByID(id);
	if (view == NULL) return;				/* No view exists by ID		*/
		
	/*
	 *	Try massaging this into a button
	 */
	
	btn = dynamic_cast<XGControl *>(view);
	if (btn) {
		/*
		 *	This was a button. Do a 'get'
		 */
		
		btn->SetValue(value);
		return;
	}
	
	/*
	 *	Set thte title instead.
	 */

	sprintf(buffer,"%ld",value);
	view->SetTitle(buffer);
}

/*	@F"XGDialog::SetDefaultOK" @I
 *
 *		Set the default OK button control
 */

void XGDialog::SetDefaultOK(short id)
{
	XGCommandButton *b;
	
	if (fOK == id) return;
	
	if (fOK) {
		b = dynamic_cast<XGCommandButton *>(FindViewByID(fOK));
		if (b) b->SetDefault(false);
	}
	if (id) {
		b = dynamic_cast<XGCommandButton *>(FindViewByID(id));
		if (b) b->SetDefault(true);
	}
	fOK = id;
	
	GetView()->InvalView();
}

/*	@F"XGDialog::SetDefaultCancel" @I
 *
 *		Set the default cancel button control
 */

void XGDialog::SetDefaultCancel(short id)
{
	fCancel = id;
	GetView()->InvalView();
}

