/******************************************************************************
 *
 * Copyright (c) 1999	TOSHIYUKI ARAI. ALL RIGHTS RESERVED. 
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer.
 *  
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 *	Text.cpp
 *
 *****************************************************************************/

// SOL++2000
// 1999.08.19 Added printf method.
// 1999.09.03 Added getStringList method.

#include <sol\Text.h>
#include <sol\StringTokenizer.h>

// The following resources may be used only in constructor.
// For example, if XmNlowerCase is TRUE, then set ES_LOWERCASE. 
Arg Text::styles[] = {
	{XmNlowerCase,	     ES_LOWERCASE},
	{XmNmultiLine,	     ES_MULTILINE},
	{XmNnoHideSel,       ES_NOHIDESEL},
	{XmNoemConvert,      ES_OEMCONVERT},
	{XmNpassword,	     ES_PASSWORD},
	{XmNreadOnly,	     ES_READONLY},
	{XmNupperCase,       ES_UPPERCASE},
};


Text::Text(View* parent, const char* name, Args& args)
       	:Primitive(parent, name, 
			args.set(styles, XtNumber(styles))
			    .set(XmNstyle, (ulong)(WS_TABSTOP|ES_AUTOHSCROLL))
			    .set(XmNexStyle, (ulong)WS_EX_CLIENTEDGE)
				.set(XmNpredefined, TRUE)
			    .set(XmNclassName,  "Edit") )
{
	setValues(args);
}



Boolean Text::create(View* parent, const char* name, Args& args)
{     
	Boolean rc = Primitive::create(parent, name, 
			args.set(styles, XtNumber(styles))
			    .set(XmNstyle, (ulong)(WS_TABSTOP|ES_AUTOHSCROLL))
			    .set(XmNexStyle, (ulong)WS_EX_CLIENTEDGE)
				.set(XmNpredefined, TRUE)
			    .set(XmNclassName,  "Edit") );

	setValues(args);
	return rc;
}


void Text::add(const char* string)
{
	int len = getTextLength();
	setSel(len, len);
	replaceSel(string);
	len = getTextLength();
	setSel(len, len);
	replaceSel("\r\n");
	send(WM_SETREDRAW, 1, 0L);
}


void Text::append(const char* string)
{
	int len = getTextLength();
	setSel(len, len);
	replaceSel(string);
	send(WM_SETREDRAW, 1, 0L);
}


// 1999.08.19 
void Text::printf(const char* format,...)
{
	char* buffer = new char[1024];

	va_list pos;
	va_start(pos, format);
	vsprintf(buffer, format, pos);
	va_end(pos);

	append(buffer);

	delete [] buffer;
}


int Text::getLine(int n, char* string, int num)
{
	*string     = LOBYTE(num);
	*(string+1) = HIBYTE(num);
    int len = (int) send(EM_GETLINE, n, (LONG)string);
	string[len] = NULL;
	return len;
}


Arg* Text::getCallbackTable(int* num)
{
	static Arg table[] = {
	{XmNchangeCallback,		EN_CHANGE},
	{XmNerrorSpaceCallback,	EN_ERRSPACE},
	{XmNhorizScrollCallback, EN_HSCROLL},
	{XmNkillFocusCallback,	EN_KILLFOCUS},
	{XmNmaxTextCallback,	EN_MAXTEXT},
	{XmNsetFocusCallback,	EN_SETFOCUS},
	{XmNupdateCallback,		EN_UPDATE},
	{XmNvertScrollCallback,	EN_VSCROLL},
	};
	*num = XtNumber(table);
	return table;
}


void Text::getSel(int* start, int* end)
{
	int s, e;
	send(EM_GETSEL, (WPARAM)&s, (LPARAM)&e);
	*start = s;
	*end   = e;
}


void Text::getValues(Args& args)
{
	Primitive::getValues(args);

	getStyle(args, styles, XtNumber(styles));

	ulong* val = (ulong*)args.get(XmNvalue);
	if(val) {
		HWND hwnd = getWindow();
		int len = getTextLength();
		char* buff = new char[len+1];
		getText(buff, len+1);
		*val = (ulong)buff;
	}
}


BOOL Text::load(char* filename)
{
	HANDLE	fd = ::CreateFile(filename,
    		GENERIC_READ,				// Access mode 
    		0,							// Share mode 
    		NULL,						// Security descriptor 
    		OPEN_EXISTING,				// How to create 
    		FILE_ATTRIBUTE_READONLY,	// File attributes 
    		NULL); 					

 	if(fd != INVALID_HANDLE_VALUE) {
		int size   = ::GetFileSize(fd, NULL);
		if(size < 64000) {
			char* buff = new char[size+1];
			if(buff) {
				unsigned long len = 0;
				::ReadFile(fd, buff, size, &len, NULL);
				buff[len] = NULL;
				setText(buff);
				delete [] buff;
			}
		}
		::CloseHandle(fd);

		return TRUE;
	}
	return FALSE;
}


BOOL  Text::save(char* filename)
{
	HANDLE fd = ::CreateFile(filename,	 
    		GENERIC_WRITE,			// Access mode 
    		0,						// Share mode 
			NULL,					// Security descriptor 
    		CREATE_ALWAYS,			// How to create: Create always 
    		FILE_ATTRIBUTE_NORMAL,	// File attributes 
    		NULL); 

	if(fd != INVALID_HANDLE_VALUE) {
		int size = getTextLength();

		char* buff = new char[size+1];
		if(buff) {
			// size + 1
			int rc     = getText(buff, size+1);
			buff[rc]   = NULL;
			unsigned int len = 0;
			::WriteFile(fd, buff, size, (unsigned long*)&len, NULL);
			::CloseHandle(fd);

			delete [] buff;
			return TRUE;
		}
	}
	return FALSE;
}


void Text::setValues(Args& args)
{
	Primitive::setValues(args);
	updateStyle(args, styles, XtNumber(styles));

	ulong val;
	if(args.get(XmNvalue, &val))
		setText((char*)val);
}

// 1999.09.03
int Text::getStringList(StringList& list)
{
	// Flatten the text by replacing "\r\n" with "  ".
	char* text = getText();
	int rc = 0;
	if (text && strlen(text)) {
		char* ptr = text;
	 
		while (ptr = strstr(ptr, "\r\n") ) {
			*ptr = ' ';
			*(ptr+1) = ' ';
			ptr = ptr + 2;
		}

		StringTokenizer tokenizer(text);
		String token = "";

		// Get a token and append it to the list.
		while (tokenizer.getToken(token)) {
			if (token.getLength() >0) {
				list.add((const char*)token);
				rc++;
			}
			token = "";
		}
		delete [] text;
	}
	return rc;
}
