/* ScummVM - Scumm Interpreter
 * Copyright (C) 2002-2004 The ScummVM project
 *
 * 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.
 *
 * $Header: /cvsroot/scummvm/scummvm/gui/EditTextWidget.cpp,v 1.26.2.1 2004/12/30 13:14:03 fingolfin Exp $
 */

#include "stdafx.h"
#include "gui/EditTextWidget.h"
#include "gui/dialog.h"
#include "gui/newgui.h"


namespace GUI {

EditTextWidget::EditTextWidget(GuiObject *boss, int x, int y, int w, int h, const String &text)
	: StaticTextWidget(boss, x, y - 1, w, h + 2, text, kTextAlignLeft), _backupString(text) {
	_flags = WIDGET_ENABLED | WIDGET_CLEARBG | WIDGET_RETAIN_FOCUS | WIDGET_WANT_TICKLE;
	_type = kEditTextWidget;

	_caretVisible = false;
	_caretTime = 0;

	_pos = _label.size();

	_labelOffset = (g_gui.getStringWidth(_label) - (_w - 6));
	if (_labelOffset < 0)
		_labelOffset = 0;
}

void EditTextWidget::handleTickle() {
	uint32 time = g_system->getMillis();
	if (_caretTime < time) {
		_caretTime = time + kCaretBlinkTime;
		drawCaret(_caretVisible);
	}
}

void EditTextWidget::handleMouseDown(int x, int y, int button, int clickCount){
	// First remove caret
	if (_caretVisible)
		drawCaret(true);

	NewGui *gui = &g_gui;

	x += _labelOffset;

	int width = 0;
	uint i;

	for (i = 0; i < _label.size(); ++i) {
		width += gui->getCharWidth(_label[i]);
		if (width >= x)
			break;
	}
	_pos = i;
	if (adjustOffset())
		draw();
}

bool EditTextWidget::tryInsertChar(char c, int pos) {
	if (isprint(c)) {
		_label.insertChar(c, pos);
		return true;
	}
	return false;
}

bool EditTextWidget::handleKeyDown(uint16 ascii, int keycode, int modifiers) {
	bool handled = true;
	bool dirty = false;

	// First remove caret
	if (_caretVisible)
		drawCaret(true);

	switch (keycode) {
	case '\n':	// enter/return
	case '\r':
		releaseFocus();
		dirty = true;
		break;
	case 27:	// escape
		_label = _backupString;
		_pos = _label.size() - 1;
		_labelOffset = (g_gui.getStringWidth(_label) - (_w-6));
		if (_labelOffset < 0)
			_labelOffset = 0;
		releaseFocus();
		dirty = true;
		break;
	case 8:		// backspace
		if (_pos > 0) {
			_pos--;
			_label.deleteChar(_pos);
		}
		dirty = true;
		break;
	case 127:	// delete
		_label.deleteChar(_pos);
		dirty = true;
		break;
	case 256 + 20:	// left arrow
		if (_pos > 0) {
			_pos--;
			dirty = adjustOffset();
		}
		break;
	case 256 + 19:	// right arrow
		if (_pos < (int)_label.size()) {
			_pos++;
			dirty = adjustOffset();
		}
		break;
	case 256 + 22:	// home
		_pos = 0;
		dirty = adjustOffset();
		break;
	case 256 + 23:	// end
		_pos = _label.size();
		dirty = adjustOffset();
		break;
	default:
		if (tryInsertChar((char)ascii, _pos)) {
			_pos++;
			dirty = true;
		} else {
			handled = false;
		}
	}

	if (dirty)
		draw();

	return handled;
}

void EditTextWidget::drawWidget(bool hilite) {
	// Draw a thin frame around us.
	g_gui.hLine(_x, _y, _x + _w - 1, g_gui._color);
	g_gui.hLine(_x, _y + _h - 1, _x +_w - 1, g_gui._shadowcolor);
	g_gui.vLine(_x, _y, _y + _h - 1, g_gui._color);
	g_gui.vLine(_x + _w - 1, _y, _y + _h - 1, g_gui._shadowcolor);

	// Draw the text
	adjustOffset();
	g_gui.drawString(_label, _x + 2, _y + 2, _w - 6, g_gui._textcolor, kTextAlignLeft, -_labelOffset, false);
}

int EditTextWidget::getCaretPos() const {
	int caretpos = 0;
	for (int i = 0; i < _pos; i++)
		caretpos += g_gui.getCharWidth(_label[i]);

	caretpos -= _labelOffset;

	return caretpos;
}

void EditTextWidget::drawCaret(bool erase) {
	// Only draw if item is visible
	if (!isVisible() || !_boss->isVisible())
		return;

	int16 color = erase ? g_gui._bgcolor : g_gui._textcolorhi;
	int x = getAbsX() + 2;
	int y = getAbsY() + 1;

	x += getCaretPos();

	g_gui.vLine(x, y, y + kLineHeight, color);
	g_gui.addDirtyRect(x, y, 2, kLineHeight);

	_caretVisible = !erase;
}

bool EditTextWidget::adjustOffset() {
	// check if the caret is still within the textbox; if it isn't,
	// adjust _labelOffset 

	int caretpos = getCaretPos();

	if (caretpos < 0) {
		// scroll left
		_labelOffset += caretpos;
		return true;
	} else if (caretpos >= _w - 6) {
		// scroll right
		_labelOffset -= (_w - 6 - caretpos);
		return true;
	} else if (_labelOffset > 0) {
		int width = g_gui.getStringWidth(_label);
		if (width - _labelOffset < (_w - 6)) {
			// scroll right
			_labelOffset = (width - (_w - 6));
			if (_labelOffset < 0)
				_labelOffset = 0;
		}
	}

	return false;
}

} // End of namespace GUI
