/*
 * MultiMail offline mail reader
 * Interface, ShadowedWin, ListWindow

 Copyright (c) 1996 Kolossvary Tamas <thomas@tvnet.hu>
 Copyright (c) 1997 John Zero <john@graphisoft.hu>
 Copyright (c) 1999 William McBrine <wmcbrine@clark.net>

 Distributed under the GNU General Public License.
 For details, see the file COPYING in the parent directory. */

#include "interfac.h"

#ifdef XCURSES
//# undef USE_SHADOWS
const char *XCursesProgramName = MM_NAME;
#endif

Win::Win(int height, int width, int topline, chtype backg)
{
	// All windows are centered horizontally.

	win = newwin(height, width, topline, (COLS - width) / 2);
	Clear(backg);

	keypad(win, TRUE);
	cursor_off();
}

Win::~Win()
{
	delwin(win);
}

void Win::Clear(chtype backg)
{
	wbkgdset(win, backg | ' ');
	werase(win);
	wbkgdset(win, ' ');
	wattrset(win, backg);
}

void Win::put(int y, int x, chtype z)
{
	mvwaddch(win, y, x, z);
}

void Win::put(int y, int x, char z)
{
	mvwaddch(win, y, x, (unsigned char) z);
}

void Win::put(int y, int x, const chtype *z, int len)
{
	// The cast is to suppress warnings with certain implementations
	// of curses that omit "const" from the prototype.

	if (!len)
		len = COLS;

	mvwaddchnstr(win, y, x, (chtype *) z, len);
}

int Win::put(int y, int x, const char *z)
{
	// Formerly just mvwaddstr() -- this ensures printing of (most)
	// characters outside the ASCII range, instead of control codes.

	chtype z2;
	int counter = 0;

	wmove(win, y, x);

	for (; *z; z++) {
		z2 = ((unsigned char) *z);
		switch (z2) {
#ifndef __PDCURSES__			// unprintable control codes
		case 14:		// double musical note
			z2 = 19;
			break;
		case 15:		// much like an asterisk
			z2 = '*';
			break;
		case 155:		// ESC + high bit = slash-o,
			z2 = 'o';	// except in CP 437
			break;
		case 8:			// backspace
		case 12:		// form feed
			z2 = '#';
			break;
#endif
		case 27:		// ESC
			z2 = '`';
			break;
		case '\t':		// TAB
			z2 = ' ';
			int i = 7 - (counter % 8);
			counter += i;
			while (i--)
				waddch(win, z2);
		}
		if ((z2 < ' ') || (z2 > 126))
			z2 |= A_ALTCHARSET;
		waddch(win, z2);
		counter++;
	}
	return counter;
}

void Win::attrib(chtype z)
{
	wattrset(win, z);
}

void Win::horizline(int y, int len)
{
	wmove(win, y, 1);
	whline(win, ACS_HLINE, len);
}

void Win::update()
{
	wrefresh(win);
}

void Win::delay_update()
{
	wnoutrefresh(win);
}

void Win::wtouch()
{
	touchwin(win);
	wnoutrefresh(win);
}

void Win::wscroll(int i)
{
	scrollok(win, TRUE);
	wscrl(win, i);
	scrollok(win, FALSE);
}

/* The cursor toggling routines for PDCurses are ugly, I know; especially
   in the mismatched use of the standard curs_set() to disable, and a
   private PDCurses function to enable. Unfortunately, this seems to be
   the only combination which works correctly on all platforms. (OS/2 Full
   Screen sessions are a particular problem.)
*/
void Win::cursor_on()
{
	leaveok(win, FALSE);
#ifdef __PDCURSES__
# ifdef XCURSES
	curs_set(1);
# else
	PDC_set_cursor_mode(curs_start, curs_end);
# endif
#endif
}

void Win::cursor_off()
{
	leaveok(win, TRUE);
#ifdef __PDCURSES__
	curs_set(0);
#endif
}

bool Win::keypressed()
{
	// Return key status immediately (non-blocking)

	nodelay(win, TRUE);
	return (wgetch(win) != ERR);
}

int Win::inkey()
{
	// Wait until key pressed, OR signal received (e.g., SIGWINCH)

	nodelay(win, FALSE);
	return wgetch(win);
}

void Win::boxtitle(chtype backg, const char *title, chtype titleAttrib)
{
	wattrset(win, backg);
	box(win, 0, 0);

	if (title) {
		put(0, 2, ACS_RTEE);
		put(0, 3 + strlen(title), ACS_LTEE);
		wattrset(win, titleAttrib);
		put(0, 3, title);
		wattrset(win, backg);
	}
}

ShadowedWin::ShadowedWin(int height, int width, int topline, chtype backg,
	const char *title, chtype titleAttrib) : Win(height, width,
	topline, backg)
{
	// The text to be in "shadow" is taken from different windows,
	// depending on the curses implementation. Note that the default,
	// "stdscr", just makes the shadowed area solid black; only with
	// ncurses and PDCurses does it draw proper shadows.

#ifdef USE_SHADOWS
	int i, j;
	chtype *right, *lower;
# ifndef NCURSES_VERSION
#  ifdef __PDCURSES__
	WINDOW *&newscr = curscr;
#  else
	WINDOW *&newscr = stdscr;
#  endif
# endif
	int xlimit = 2 + (interface->active() != letter);
	int firstcol = (COLS - width) / 2;

	while ((width + firstcol) > (COLS - xlimit))
		width--;
	while ((height + topline) > (LINES - 1))
		height--;

	right = new chtype[(height - 1) << 1];
	lower = new chtype[width + 1];

	// Gather the old text and attribute info:

	for (i = 0; i < (height - 1); i++)
		for (j = 0; j < 2; j++)
			right[(i << 1) + j] = (mvwinch(newscr,
				(topline + i + 1), (firstcol + width + j)) &
					(A_CHARTEXT | A_ALTCHARSET));

	mvwinchnstr(newscr, (topline + height), (firstcol + 2), lower, width);

	// Redraw it in darkened form:

	shadow = newwin(height, width, topline + 1, firstcol + 2);
	leaveok(shadow, TRUE);

	for (i = 0; i < (height - 1); i++)
		for (j = 0; j < 2; j++)
			mvwaddch(shadow, i, width - 2 + j,
				(right[(i << 1) + j] | C_SHADOW));
	for (i = 0; i < width; i++)
		mvwaddch(shadow, height - 1, i, (lower[i] & (A_CHARTEXT |
			A_ALTCHARSET)) | C_SHADOW);

	wnoutrefresh(shadow);
#endif
	boxtitle(backg, title, titleAttrib);

#ifdef USE_SHADOWS
	delete[] lower;
	delete[] right;
#endif
}

ShadowedWin::~ShadowedWin()
{
#ifdef USE_SHADOWS
	delwin(shadow);
#endif
}

void ShadowedWin::touch()
{
#ifdef USE_SHADOWS
	touchwin(shadow);
	wnoutrefresh(shadow);
#endif
	Win::wtouch();
}

int ShadowedWin::getstring(int y, int x, char *string, int maxlen,
		int bg_color, int fg_color)
{
	int i, j, end, c;
	char *tmp = new char[maxlen];

	cropesp(string);

	wattrset(win, fg_color);
	for (i = 0; i < maxlen; i++) {
		put(y, x + i, ACS_BOARD);
		tmp[i] = '\0';
	}

	maxlen--;

	put(y, x, string);	// Hmm...

	cursor_on();
	update();

	i = end = 0;
	bool first_key = true;

	while (!end) {
		c = inkey();

		// these keys make the string "accepted" and editable:
		if (first_key) {
			first_key = false;

			switch (c) {
			case KEY_LEFT:
			case KEY_RIGHT:
			//case 8:
			//case KEY_BACKSPACE:
			case KEY_HOME:
			case KEY_END:
				strncpy(tmp, string, maxlen);
				i = strlen(tmp);
			}
		}

		switch (c) {
		case KEY_DOWN:
			end++;
		case KEY_UP:
			end++;
		case MM_ENTER:
			end++;
		case '\033':		// Escape
			end++;
			break;
		case KEY_LEFT:
			if (i > 0)
				i--;
			break;
		case KEY_RIGHT:
			if ((i < maxlen) && tmp[i])
				i++;
			break;
		case 127:
		case KEY_DC:		// Delete key 
			strncpy(&tmp[i], &tmp[i + 1], maxlen - i);
			tmp[maxlen] = '\0';
			break;
		case 8:			// Ctrl-H
		case KEY_BACKSPACE:
			if (i > 0) {
				strncpy(&tmp[i - 1], &tmp[i], maxlen - i);
				tmp[maxlen] = '\0';
				i--;
			}
			break;
		case KEY_HOME:
			i = 0;
			break;
		case KEY_END:
			i = strlen(tmp);
			break;
		default:
			for (j = maxlen; j > i; j--)
				tmp[j] = tmp[j - 1];
			tmp[i] = c;
			if (i < maxlen)
				i++;
		}
		for (j = 0; j <= maxlen; j++)
			put(y, x + j, (tmp[j] ? (unsigned char) tmp[j] :
				ACS_BOARD));
		wmove(win, y, x + i);
		update();
	}

	if (tmp[0])
		strcpy(string, tmp);

	wattrset(win, bg_color);

	j = strlen(string);
	for (i = 0; i <= maxlen; i++)
		put(y, x + i, ((i < j) ? string[i] : ' '));

	update();
	cursor_off();

	delete[] tmp;

	return end - 1;
}

InfoWin::InfoWin(int height, int width, int topline, chtype backg,
	const char *title, chtype titleAttrib, int bottx, int topx) :
	ShadowedWin(height, width, topline, backg, title, titleAttrib)
{
	lineBuf = new char[COLS];
	info = new Win(height - bottx, width - 2, topline + topx, backg);
}

InfoWin::~InfoWin()
{
	delete info;
	delete[] lineBuf;
}

void InfoWin::irefresh()
{
	info->delay_update();
}

void InfoWin::touch()
{
	ShadowedWin::touch();
	info->wtouch();
}

void InfoWin::oneline(int i, chtype ch)
{
	//charconv_in(lineBuf);
	info->attrib(ch);
	info->put(i, 0, lineBuf);
}

void InfoWin::iscrl(int i)
{
	info->wscroll(i);
}

ListWindow::ListWindow()
{
	position = active = 0;
	oldPos = oldActive = -100;
}

void ListWindow::checkPos(int limit)
{
	if (active >= limit)
		active = limit - 1;

	if (active < 0)
		active = 0;

	if (active < position)
		position = active;
	else
		if (active - position >= list_max_y)
			position = active - list_max_y + 1;

	if (limit > list_max_y) {
		if (position < 0)
			position = 0;
		else
			if (position > limit - list_max_y)
				position = limit - list_max_y;
	} else
		position = 0;
}

void ListWindow::Draw()
{
	int i, j, limit = NumOfItems();

	checkPos(limit);

	i = position - oldPos;
	switch (i) {
	case -1:
	case 1:
		list->iscrl(i);
	case 0:
		if (active != oldActive) {
			j = oldActive - position;
			if ((j >= 0) && (j < list_max_y))
				oneLine(j);
		}
		oneLine(active - position);
		list->irefresh();
		break;
	default:
		for (j = 0; j < list_max_y; j++) {
			oneLine(j);
			if (position + j == limit - 1)
				j = list_max_y;
		}
		ReDraw();
	}
	oldPos = position;
	oldActive = active;
}

void ListWindow::DrawOne(int i, chtype ch)
{
	// Highlight, if drawing the active bar

	if (position + i == active) {
		chtype backg = PAIR_NUMBER(ch) & 7;

		switch (backg) {
		case COLOR_BLACK:
		case COLOR_RED:
		case COLOR_BLUE:
			ch = REVERSE(COLOR_WHITE, COLOR_BLACK);
			break;
		default:
			ch = REVERSE(COLOR_BLACK, COLOR_WHITE);
		}
	}
	list->oneline(i, ch);
}

void ListWindow::DrawAll()
{
	oldPos = oldActive = -100;
	Draw();
}

void ListWindow::Move(direction dir)
{
	int limit = NumOfItems();

	switch (dir) {
	case UP:
		active--;
		break;
	case DOWN:
		active++;
		break;
	case PGUP:
		position -= list_max_y;
		active -= list_max_y;
		break;
	case PGDN:
		position += list_max_y;
		active += list_max_y;
		break;
	case HOME:
		active = 0;
		break;
	case END:
		active = limit - 1;
		break;
	}
	checkPos(limit);
}

bool ListWindow::search(const char *item)
{
	int limit = NumOfItems();
	bool found = false;

	for (int x = active + 1; (x < limit) && !found; x++) {
		found = oneSearch(x, item);
		if (found) {
			active = x;
			Draw();
		}
	}

	checkPos(limit);
	return found;
}

void ListWindow::ReDraw()
{
	list->touch();
}

void ListWindow::KeyHandle(int key)
{
	bool draw = true;

	switch (key) {
	case KEY_DOWN:
		Move(DOWN);
		break;
	case KEY_UP:
		Move(UP);
		break;
	case KEY_HOME:
		Move(HOME);
		break;
	case KEY_END:
		Move(END);
		break;
	case 'B':
	case KEY_PPAGE:
		Move(PGUP);
		break;
	case ' ':
	case 'F':
	case KEY_NPAGE:
		Move(PGDN);
		break;
	default:
		draw = false;
		extrakeys(key);
	}
	if (draw)
		Draw();
}

Interface::Interface()
{
        isoConsole = mm.resourceObject->getInt(Charset);
	searchItem = 0;
#ifdef SIGWINCH
	resized = false;
#endif
}

void Interface::main()
{
	colorlist.Init();
	taglines.Init();
	addresses.Init();
	alive();
	screen_init();
	newstate(packetlist);
#if defined (SIGWINCH) && !defined (XCURSES)
	signal(SIGWINCH, sigwinchHandler);
#endif
	KeyHandle();
}

Interface::~Interface()
{
	delete screen;
	touchwin(stdscr);
	refresh();
	leaveok(stdscr, FALSE);
	echo();
	endwin();
#ifdef __PDCURSES__
# ifdef XCURSES
	XCursesExit();
# else
	PDC_set_cursor_mode(curs_start, curs_end);
# endif
#endif
}

void Interface::init_colors()
{
	for (int back = COLOR_BLACK; back <= (COLOR_WHITE); back++)
		for (int fore = COLOR_BLACK; fore <= (COLOR_WHITE); fore++)
			init_pair((fore << 3) + back, fore, back);

	// Color pair 0 cannot be used (argh!), so swap:

	init_pair(((COLOR_WHITE) << 3) + (COLOR_WHITE),
		COLOR_BLACK, COLOR_BLACK);

	// Of course, now I can't do white-on-white (grey). :-(
}

void Interface::alive()
{
#if defined (__PDCURSES__) && !defined (XCURSES)
	// Preserve the startup cursor mode:

	int curs_mode = PDC_get_cursor_mode();
	curs_start = (curs_mode & 0xff00) >> 8;
	curs_end = curs_mode & 0xff;
#endif
	initscr();
	refresh();
	start_color();
	init_colors();
	cbreak();
	noecho();
	nonl();
#if defined (__PDCURSES__) && defined (__RSXNT__)
	/* With the RSXNT port, the keyboard check after printing each
           line makes output very slow, unless we disable line break
           optimization.
	*/
	typeahead(-1);
#endif
}

void Interface::screen_init()
{
	// Make a new background window, fill it with ACS_BOARD characters:

	screen = new Win(LINES, COLS, 0, C_SBACK | ACS_BOARD);

	if ((COLS < 60) || (LINES < 20))
		fatalError("A screen at least 60x20 is required");

	// Border and title:

	char tmp[80];
	sprintf(tmp, MM_TOPHEADER, MM_NAME, MM_MAJOR, MM_MINOR);
	screen->boxtitle(C_SBORDER, tmp, emph(C_SBACK));

	// Help window area:

	screen->attrib(C_SSEPBOTT);
	screen->horizline(LINES - 5, COLS - 2);
	screen->delay_update();
}

int Interface::WarningWindow(const char *warning, const char **selectors,
				int items)
{
	static const char *yesno[] = { "Yes", "No" };
	const char **p;

	int x, y, z, itemlen, c, curitem, def_val = 0;
	bool result = false;

	if (!selectors)
		selectors = yesno;		// default options

	// Calculate the window width and maximum item width:

	for (p = selectors, itemlen = 0, curitem = 0; curitem < items;
	    curitem++, p++) {
		z = strlen(*p);
		if (z > itemlen)
			itemlen = z;
	}
	itemlen += 3;
	x = itemlen * (items + 1);
	z = strlen(warning);
	if ((z + 4) > x)
		x = z + 4;

	ShadowedWin warn(7, x, (LINES >> 1) - 4, C_WTEXT);
	warn.put(2, (x - z) >> 1, warning);

	y = x / (items + 1);

	// Display each item:

	for (p = selectors, curitem = 0; curitem < items; curitem++) {
		x = (curitem + 1) * y - (strlen(*p) >> 1);

		warn.attrib(C_WTEXT);
		warn.put(4, x + 1, *p + 1);

		warn.attrib(C_WTEXTHI);
		warn.put(4, x, **p++);
	}

	// Main loop -- highlight selected item and process keystrokes:

	while (!result) {
		for (p = selectors, curitem = 0; curitem < items;
		    curitem++, p++) {
			z = strlen(*p);
			x = (curitem + 1) * y - (z >> 1);
			warn.put(4, x - 1, (def_val == curitem) ? '[' : ' ');
			warn.put(4, x + z, (def_val == curitem) ? ']' : ' ');
		}
		warn.cursor_on();	// For SIGWINCH
		warn.update();
		warn.cursor_off();

		c = warn.inkey();

		for (p = selectors, curitem = 0; (curitem < items) &&
		    !result; curitem++, p++)
			if ((c == tolower(**p)) || (c == toupper(**p))) {
				def_val = curitem;
				result = true;
			}

		if (!result)
			switch (c) {
			case KEY_LEFT:
				if (!def_val)
					def_val = items;
				def_val--;
				break;
			case KEY_RIGHT:
			case 9:
				if (++def_val == items)
					def_val = 0;
				break;
			case MM_ENTER:
				result = true;
			}
	}
	return (items - 1) - def_val;		// the order is inverted
}

int Interface::savePrompt(char *filename)
{
	ShadowedWin question(5, 60, (LINES >> 1) - 3, C_LLSAVEBORD);
	question.attrib(C_LLSAVETEXT);
	question.put(1, 2, "Save to file:");
	question.put(2, 2, "<");
	question.put(2, 57, ">");
	question.put(3, 30, "<ESC> (pause) to cancel");

	return question.getstring(2, 3, filename, 54, C_LLSAVETEXT,
		C_LLSAVEGET);
}

void Interface::nonFatalError(const char *warn)
{
	static const char *ok[] = { "OK" };

	WarningWindow(warn, ok, 1);
	changestate(state);
}

statetype Interface::active()
{
	return state;
}

statetype Interface::prevactive()
{
	return prevstate;
}

void Interface::oldstate(statetype n)
{
	helpwindow.Delete();

	switch (n) {
	case packetlist:
		packets.Delete();
		break;
	case arealist:
		areas.Delete();
		break;
	case letterlist:
		letters.Delete();
		break;
	case littlearealist:
		areas.Select();
	case address:
	case tagwin:
		currList->Delete();
		oldstate(prevstate);
		break;
	case letter_help:
	case letter:
		letterwindow.Delete();
		break;
	case ansi_help:
	case ansiwin:
		ansiwindow.Delete();
	default:;
	}
}

void Interface::helpreset(statetype oldst)
{
	screen->wtouch();
	if ((oldst != state) && (prevstate != state))
		helpwindow.baseReset();
}

void Interface::newstate(statetype n)
{
	statetype oldst = state;
	state = n;

	switch (n) {
	case packetlist:
		helpreset(oldst);
		currList = &packets;
		packets.MakeActive();
		break;
	case arealist:
		helpreset(oldst);
		currList = &areas;
		areas.MakeActive();
		break;
	case letterlist:
		helpreset(oldst);
		currList = &letters;
		letters.MakeActive();
		break;
	case letter:
		letterwindow.MakeActive(oldst == letterlist);
		break;
	case letter_help:
		letterwindow.MakeActive(false);
		break;
	case littlearealist:
		newstate(prevstate);
		state = littlearealist;
		currList = &littleareas;
		littleareas.MakeActive();
		break;
	case address:
		newstate(prevstate);
		state = address;
		currList = &addresses;
		addresses.MakeActive(addrparm);
		break;
	case tagwin:
		newstate(prevstate);
		state = tagwin;
		currList = &taglines;
		taglines.MakeActive();
		break;
	case ansi_help:
	case ansiwin:
		ansiwindow.MakeActive();
	default:;
	}

	helpwindow.MakeActive();
}

void Interface::changestate(statetype n)
{
	oldstate(state);
	newstate(n);
}

void Interface::newpacket()
{
	newFiles = mm.getFileList();
	bulletins = mm.getBulletins();
	areas.FirstUnread();
	changestate(arealist);

	if (bulletins)
		if (WarningWindow("View bulletins?")) {
			file_header **a = bulletins;
			while (a && *a) {
				switch (ansiFile(*a)) {
				case 1:
					a++;
					break;
				case -1:
					if (a != bulletins)
						a--;
					break;
				default:
					a = 0;
				}
			}
		} else
			changestate(arealist);
	if (newFiles)
		if (WarningWindow("View new files list?"))
			ansiFile(newFiles, "New files");
		else
			changestate(arealist);
}

bool Interface::select()
{
	int pktret;

	switch (state) {
	case packetlist:
		unsaved_reply = any_read = false;
		pktret = packets.OpenPacket();
		if (pktret) {
#ifdef __PDCURSES__
			changestate(packetlist);
#endif
			nonFatalError((pktret == UNCOMP_FAIL) ?
				"Could not uncompress packet" :
				"Packet type not recognized");
		} else
			newpacket();
		break;
	case arealist:
		areas.Select();
		if (mm.areaList->getNoOfLetters() > 0) {
			mm.areaList->getLetterList();
			letters.FirstUnread();
			changestate(letterlist);
		}
		break;
	case letterlist:
		letters.Select();
		changestate(letter);
		break;
	case letter:
		letterwindow.Next();
		break;
	case ansi_help:
	case letter_help:
		back();
		break;
	case littlearealist:
	case address:
	case tagwin:
		currList->KeyHandle('\n');
	case ansiwin:
		return back();
	default:;
	}
	return false;
}

bool Interface::back()
{
	switch (state) {
	case packetlist:
		if (WarningWindow("Do you really want to quit?")) {
			oldstate(state);
			return true;
		} else
			changestate(packetlist);
		break;
	case arealist:
		if (any_read) {
			if (WarningWindow("Save lastread pointers?"))
				mm.saveRead();
			any_read = false;
		}
		if (unsaved_reply)
			if (WarningWindow(
			"The REPLY area has changed. Save changes?"))
				create_reply_packet();
		mm.Delete();
		changestate(packetlist);
		break;
	case letterlist:
		delete mm.letterList;
		changestate(arealist);
		break;
	case letter_help:
		changestate(letter);
		break;
	case letter:
		changestate(letterlist);
		break;
	case ansi_help:
		changestate(ansiwin);
		break;
	case littlearealist:
	case address:
	case tagwin:
	case ansiwin:
		changestate(prevstate);
		return true;
	default:;
	}
	return false;
}

#ifdef SIGWINCH
void Interface::sigwinch()
{
	oldstate(state);

	delete screen;
# ifdef XCURSES
	resize_term(0, 0);
# else
	endwin();
	initscr();
	refresh();
# endif
	screen_init();
	newstate(state);
	resized = false;
}

void Interface::setResized()
{
	resized = true;
}
#endif

void Interface::kill_letter()
{
	if (WarningWindow(
		"Are you sure you want to delete this letter?")) {

		mm.areaList->killLetter(mm.letterList->getMsgNum());
		unsaved_reply = true;

		changestate(letterlist);
		if (!mm.areaList->getNoOfLetters())
			back();
	} else
		changestate(state);
}

void Interface::create_reply_packet()
{
	mm.areaList->makeReply();
	unsaved_reply = false;
}

void Interface::addressbook(bool NoEnter)
{
	prevstate = state;
	addrparm = NoEnter;
	changestate(address);
	KeyHandle();
}

void Interface::Tagwin()
{
	prevstate = state;
	changestate(tagwin);
	KeyHandle();
}

int Interface::ansiLoop(const char *source, const char *title)
{
	ansiwindow.set(source, title);
	return ansiCommon();
}

int Interface::ansiFile(file_header *f, const char *title)
{
	ansiwindow.setFile(f, title);
	return ansiCommon();
}

int Interface::ansiCommon()
{
	prevstate = state;
	changestate(ansiwin);
	KeyHandle();
	switch (Key) {
		case MM_MINUS:
			return -1;
		case MM_ENTER:
		case MM_PLUS:
			return 1;
	}
	return 0;
}

int Interface::areaMenu()
{
	prevstate = state;
	changestate(littlearealist);
	KeyHandle();
	return littleareas.getArea();
}

void Interface::TransToggle()
{
	isoConsole = !isoConsole;
	changestate(state);
}

void Interface::setUnsaved()
{
	unsaved_reply = true;
}

void Interface::setAnyRead()
{
	any_read = true;
}

void Interface::searchNext()
{
	bool result = false;

	switch (state) {
	case letter:
		result = letterwindow.search(searchItem);
		break;
	case ansiwin:
		result = ansiwindow.search(searchItem);
		break;
	default:
		result = currList->search(searchItem);
	}

	if (!result)
		nonFatalError("No more matches");
}

const char *Interface::searchSet()
{
	static char item[80];

	ShadowedWin question(5, 60, (LINES >> 1) - 3, C_SRCHBORD);
	question.attrib(C_SRCHTEXT);
	question.put(1, 2, "Search for:");
	question.put(2, 2, "<");
	question.put(2, 57, ">");

	if (!question.getstring(2, 3, item, 54, C_SRCHTEXT, C_SRCHGET))
		*item = '\0';

	return (*item ? item : 0);
}

void Interface::setKey(int newkey)
{
	ungetch(newkey);
}

void Interface::KeyHandle()		// Main loop
{
	bool end = false;

	while (!end) {
#ifdef SIGWINCH
		if (resized)
			sigwinch();
#endif
		doupdate();
		Key = screen->inkey();
#ifdef XCURSES
		resized = is_termresized();
#endif

#ifdef SIGWINCH
		if (((state == letter_help) || (state == ansi_help))
			&& !resized)
#else
		if ((state == letter_help) || (state == ansi_help))
#endif
				back();
		else {
			if ((Key >= 'a') && (Key <= 'z'))
				Key = toupper(Key);

			switch (Key) {
			case 'Q':
			case '\033':		// escape
			case KEY_BACKSPACE:
				end = back();
				break;
			case MM_ENTER:
				end = select();
				break;
			case MM_SLASH:
				searchItem = searchSet();
				changestate(state);
			case '.':
			case '>':
				if (searchItem)
					searchNext();
				break;
			case 'A':
			case 20:		// ^T
				switch (state) {
				case packetlist:
				case arealist:
				case letterlist:
				case letter:
					if (Key == 'A')
						addressbook(true);
					else
						Tagwin();
				default:;
				}
				break;
			case 'C':
				TransToggle();
				break;
			case 'O':
				switch (state) {
				case packetlist:
				case arealist:
				case letterlist:
					helpwindow.baseNext();
					break;
				case letter:
					letterwindow.KeyHandle(Key);
				default:;
				}
				break;
			default:
				switch (state) {
				case letter:
					letterwindow.KeyHandle(Key);
					break;
				case ansiwin:
					switch (Key) {
					case MM_PLUS:
					case MM_MINUS:
						end = back();
						break;
					default:
						ansiwindow.KeyHandle(Key);
					}
					break;
				default:
					currList->KeyHandle(Key);
				}
			}
		}
	}
}
