//	Zinc Application Framework - D_TEXT.CPP
//	COPYRIGHT (C) 1990-1999.  All Rights Reserved.
//	Zinc Software Incorporated.  Pleasant Grove, Utah  USA

#include <time.h>
#include <z_ctype.hpp>
#include <z_cursor.hpp>
#define USE_RAW_KEYS
#include <z_keymap.hpp>
#include <z_mouse2.hpp>
#include <z_scrll2.hpp>
#include <z_string.hpp>
#include <z_text.hpp>
#include <z_utils.hpp>

#define HSCROLL_SHOWING					5
#define LEADING							2

typedef ZafIChar TEXT_CODE;
const TEXT_CODE TXC_END				= -1;
const TEXT_CODE TXC_MARK			= -2;
const TEXT_CODE TXC_RETURN			= -3;
const TEXT_CODE TXC_SPLIT_END		= -4;
const TEXT_CODE TXC_SPLIT_START		= -5;
const TEXT_CODE TXC_START			= -6;
const TEXT_CODE TXC_WRAP			= -7;

enum MOVE_CODE
{
	LEFT = 1,
	RIGHT,
	NEXT_LINE,
	PREV_LINE
};

// ----- ZafText ---------------------------------------------------------

ZafWindowObject *ZafText::Add(ZafWindowObject *object, ZafWindowObject *position)
{
	// Defer to the base class.
	return (ZafWindow::Add(object, position));
}

int ZafText::CharWidth(int offset)
{
	while (!editBuffer[offset])
	{
		switch (editBuffer[offset + 1])
		{
		case TXC_END:
		case TXC_RETURN:
		case TXC_WRAP:
			return 0;

		case TXC_SPLIT_START:
			offset += splitSize;
			break;

		default:
			offset += 2;
			break;
		}
	}

	ZafIChar charString[2];
	charString[0] = editBuffer[offset];
	charString[1] = 0;

	return (GetTextWidth(charString));
}

int ZafText::CursorOffset(void) const
{
	return TextOffset(offsetCursor);
}

ZafPositionStruct ZafText::CursorPosition(void) const
{
	ZafPositionStruct position;
	position.column = position.line = 0;
#if defined(ZAF_GRAPHICS_MODE)
	position.coordinateType = ZAF_PIXEL;
#elif defined(ZAF_TEXT_MODE)
	position.coordinateType = ZAF_CELL;
#endif
	int offset = offsetCursor;
	while (Move(&offset, LEFT))
		position.column++;
	while (Move(&offset, PREV_LINE))
		position.line++;

	return (position);
}

ZafIChar ZafText::Delete(int *offset)
{
	SetSplit(offset);
	if (editBuffer[offsetSplit + splitSize] == 0)
	{
		splitSize++;
		if (editBuffer[offsetSplit + splitSize] == TXC_RETURN)
			textLength -= 2;
	}
	else
	{
		editBuffer[offsetSplit + splitSize - 1] = 0;
		textLength--;
	}

	ZafIChar delChar = editBuffer[offsetSplit + splitSize];
	editBuffer[offsetSplit + splitSize++] = TXC_SPLIT_END;
	if (delChar == TXC_RETURN || delChar == TXC_WRAP)
		lines--;
	return delChar;
}

#if defined(ZAF_DEFAULT_DRAW)
ZafEventType ZafText::Draw(const ZafEventStruct &event, ZafEventType ccode)
{
	ZafWindow::Draw(event, ccode);
	Draw(offsetTopLine, -1, false, false, &event.region);

	// Return the control code.
	return (ccode);
}

void ZafText::Draw(int startOffset, int endOffset, bool erase, bool markOnly,
	const ZafRegionStruct *clipRegion)
{
	if (!AutomaticUpdate())
		return;

	// Compute the actual draw region.
	ZafPaletteState state = PaletteState();
	ZafRegionStruct drawRegion = BeginDraw();

/* START BLOCK COMMENT
**		if (Bordered())
**			DrawBorder(drawRegion, S_REDISPLAY_REGION);
END BLOCK COMMENT */

	ZafRegionStruct clip = clientRegion;
	if (clipRegion)
		clip.Overlap(*clipRegion, clip);
	display->SetClipRegion(clip);

	ZafPaletteStruct normalPalette = LogicalPalette(ZAF_PM_TEXT, state);
	ZafPaletteStruct backPalette = LogicalPalette(ZAF_PM_BACKGROUND, state);

	normalPalette.colorBackground = backPalette.colorBackground;
	normalPalette.monoBackground = backPalette.monoBackground;

	display->SetPalette(normalPalette);

	ZafPositionStruct drawPosition;
	GetPosition(startOffset, &drawPosition);
	int markStart = ZafMin(offsetMarkAnchor, offsetMarkFloat);
	int markEnd = ZafMax(offsetMarkAnchor, offsetMarkFloat);

	int offset = startOffset;
	int finished = false;
	while (!finished)
	{
		if (editBuffer[offset])
		{
			ZafPaletteStruct palette = ((markOnly && !erase) ||
				(!markOnly && offset > markStart && offset < markEnd)) ?
				*((ZafScreenDisplay *)display)->markPalette : normalPalette;
			display->SetForeground(palette.colorForeground);
			display->SetBackground(palette.colorBackground);
			display->SetFont(normalPalette.font);
			display->Text(clientRegion.left + drawPosition.column,
				clientRegion.top + drawPosition.line, clientRegion.right, clientRegion.bottom,
				&editBuffer[offset], -1, ZAF_HZ_LEFT, ZAF_VT_TOP, -1, true);
			drawPosition.column += GetTextWidth(&editBuffer[offset]);
			while (editBuffer[offset])
				offset++;
		}

		ZafIChar code = editBuffer[offset + 1];
		switch (code)
		{
		case TXC_END:
		case TXC_RETURN:
		case TXC_WRAP:
			if (erase && !markOnly && drawPosition.column < clientRegion.Width())
			{
				display->SetPalette(normalPalette);
				display->Rectangle(clientRegion.left + drawPosition.column,
					clientRegion.top + drawPosition.line, clientRegion.right,
					clientRegion.top + drawPosition.line + lineHeight, 0, true);
			}
			drawPosition.column = -scroll.left;
			drawPosition.line += lineHeight;
			offset += 2;
			if (!endOffset || code == TXC_END)
				finished = true;
			break;

		case TXC_SPLIT_START:
			offset += splitSize;
			break;

		default:
			offset += 2;
			break;
		}

		if (endOffset > 0 && offset >= endOffset || drawPosition.line > clientRegion.Height())
			finished = true;
	}

	if (endOffset == -1 && erase && !markOnly)
	{
		if (drawPosition.line < clientRegion.Height())
		{
			display->SetPalette(normalPalette);
			display->Rectangle(clientRegion.left, clientRegion.top + drawPosition.line,
				clientRegion.right, clientRegion.bottom, 0, true);
		}
	}

	EndDraw();
}
#endif

int ZafText::EditOffset(int textOffset) const
{
	int offset = 0;
	int editOffset = 2;
	while (offset < textOffset)
	{
		if (Move(&editOffset, RIGHT))
			offset++;
		else if (Move(&editOffset, NEXT_LINE))
		{
			if (editBuffer[editOffset - 1] == TXC_RETURN)
				offset += 2;
		}
		else
			break;
	}
	return (editOffset);
}

ZafEventType ZafText::Event(const ZafEventStruct &event)
{
	ZafEventType ccode = LogicalEvent(event);

	if (ccode >= L_MARK_LEFT && ccode <= L_MARK_WORD_LEFT && !offsetMarkAnchor)
		SetMark(offsetCursor);
	else if (ccode >= L_UP && ccode <= L_PGDN && offsetMarkAnchor)
		SetMark(0);

	// Check for zinc events.
	switch (ccode)
	{
	// ----- Create messages -----
	case S_INITIALIZE:
		cursorPos.column = cursorPos.line = 0;
		// Check for an unanswered field.
		if (Unanswered())
			stringData->SetText(ZafLanguageManager::blankString);
		ccode = ZafWindow::Event(event);
#if defined(ZAF_GRAPHICS_MODE)
		display->SetFont(Font());
		lineHeight = display->TextSize(ZAF_ITEXT("Tq")).Height() + LEADING;
#elif defined(ZAF_TEXT_MODE)
		lineHeight = display->TextSize(ZAF_NULLP(ZafIChar)).Height();
#endif
		offsetMarkAnchor = offsetMarkFloat = 0;
		offsetCursor = offsetTopLine = 2;
		break;

	// ----- Action messages -----
	case S_CURRENT:
	case S_NON_CURRENT:
		{
			bool oldFocus = Focus();
			ccode = ZafWindow::Event(event);
/* START BLOCK COMMENT
**				const ZafIChar *text = Text();
**				if (!text || !*text)
**					offsetMarkAnchor = 0;
END BLOCK COMMENT */
			if (event.type == S_CURRENT)
			{
				if (AutoClear() && !oldFocus)
				{
					SetCursor(2);
					SetMark(offsetCursor, false);
					SetCursor(editSize - 2);
					SetMark(offsetCursor, true);
				}

				// Activate the cursor device.
				ZafEventStruct cEvent(D_ACTIVATE);
				cEvent.windowObject = this;
				eventManager->Event(cEvent, E_CURSOR);
				windowManager->cursorObject = this;
		  		UpdateCursor();
			}	
			else
			{
				OSGetText();
				SetMark(0);

				// Deactivate the cursor device.
				ZafEventStruct cEvent(D_DEACTIVATE);
				eventManager->Event(cEvent, E_CURSOR);
				eventManager->SetDeviceState(E_CURSOR, D_OFF);
				windowManager->cursorObject = ZAF_NULLP(ZafWindowObject);
			}
		}
		break;

	case N_NON_CURRENT:
		{
		// Syncronize text buffers.
		ZafIChar *oldText = strdup(stringData->Text());
		OSGetText();

		// Set changed only if the text is really different.
		if (strcmp(oldText, stringData->Text()) != 0)
			SetChanged(true);
		delete []oldText;

		// Validate the field.
		ccode = ZafWindowObject::Event(event);

		// Check for errors.
		if (ccode == ZAF_ERROR_NONE)
			unanswered = false;
		else if (ccode == ZAF_ERROR_LEAVE_INVALID)
		{
			invalid = true;
			unanswered = false;
		}
		}
		break;

	case S_REDISPLAY:
	if (AutomaticUpdate() && Visible() && screenID)
	{
		// S_REDISPLAY is generated by a call to SetFont().
#if defined(ZAF_GRAPHICS_MODE)
		display->SetFont(Font());
		lineHeight = display->TextSize(ZAF_ITEXT("Tq")).Height() + LEADING;
#elif defined(ZAF_TEXT_MODE)
		lineHeight = display->TextSize(ZAF_NULLP(ZafIChar)).Height();
#endif
		bool saveAuto = automaticUpdate;
		automaticUpdate = false;
		int tCursor = CursorOffset();
		FormatText();
		SetCursorOffset(tCursor);
		automaticUpdate = saveAuto;

		ccode = ZafWindow::Event(event);
	}
		break;

	case S_REDISPLAY_CURSOR:
		{
			// Activate the cursor device.
			eventManager->SetDeviceState(E_CURSOR, D_ON);
			ZafEventStruct cEvent(D_ACTIVATE);
			cEvent.windowObject = this;
			eventManager->Event(cEvent, E_CURSOR);
			UpdateCursor();
		}
		break;

	case E_KEY:
		if(!ViewOnly())
		{
			if (offsetMarkAnchor)
				Event(L_DELETE);

			if (IsPrint(event.key.value) && (stringData->MaxLength() == -1 || textLength < stringData->MaxLength()))
			{
				int insertOffset = offsetCursor;
				SetSplit(&insertOffset);
/* START BLOCK COMMENT
**					if (!insert && editBuffer[insertOffset + splitSize])
**						Delete(&insertOffset);
END BLOCK COMMENT */
				Insert(&insertOffset, event.key.value);
				if (WordWrap())
					Wrap(insertOffset, event.key.value == ' ', true, true);
				else
				{
					Draw(insertOffset, 0, true);
					while (Move(&insertOffset, LEFT))
						;
					width = ZafMax(width, LineWidth(insertOffset) / (clientRegion.Width() / HSCROLL_SHOWING) + 1);
				}
				if (WordWrap())
					SetCursorOffset(CursorOffset() + 1);
				else
					SetCursor(offsetCursor + 1);
			}
#if defined(ZAF_CURSES)
			else if (event.rawCode == ENTER && textLength + 2 <= maxLength)
#else
			else if ((event.rawCode == ENTER || event.rawCode == GRAY_ENTER) &&
				(stringData->MaxLength() == -1 || textLength + 2 <= stringData->MaxLength()))
#endif
			{
				int insertOffset = offsetCursor;
				Insert(&insertOffset, 0, TXC_RETURN);
				SetCursor(offsetSplit);

				if (WordWrap())
					Wrap(insertOffset, true, true, true);
				else
					Draw(insertOffset, -1, true);

				// Don't call default button.
				ccode = S_UNKNOWN;
			}
			UpdateScroll();
			lastCursorColumn = scroll.left + cursorPos.column;
		}
		break;

	case L_LEFT:
	case L_MARK_LEFT:
		{
			int offset = offsetCursor;
			if (!Move(&offset, LEFT))
			{
				if (editBuffer[offset - 1] != TXC_START)
					offset -= 2;
				else
					return S_UNKNOWN;
			}
			SetCursor(offset);
			if (ccode == L_MARK_LEFT)
				SetMark(offsetCursor);
			UpdateScroll();
			lastCursorColumn = scroll.left + cursorPos.column;
		}
		break;

	case L_RIGHT:
	case L_MARK_RIGHT:
		{
			int offset = offsetCursor;
			if (!Move(&offset, RIGHT) && editBuffer[offset + 1] != TXC_END)
				offset += 2;
			SetCursor(offset);
			if (ccode == L_MARK_RIGHT)
				SetMark(offsetCursor);
			UpdateScroll();
			lastCursorColumn = scroll.left + cursorPos.column;
		}
		break;

	case L_UP:
	case L_MARK_UP:
		{
			ZafPositionStruct position = cursorPos;
			position.column = lastCursorColumn - scroll.left;
			position.line -= lineHeight;
			SetCursor(position);
			if (ccode == L_MARK_UP)
				SetMark(offsetCursor);
			UpdateScroll();
		}
		break;
	
	case L_DOWN:
	case L_MARK_DOWN:
		{
			int offset = offsetCursor;
			if (Move(&offset, NEXT_LINE))
			{
				ZafPositionStruct position = cursorPos;
				position.column = lastCursorColumn - scroll.left;
				position.line += lineHeight + 1;
				SetCursor(position);
			if (ccode == L_MARK_DOWN)
				SetMark(offsetCursor);
				UpdateScroll();
			}
		}
		break;

	case L_EOL:
	case L_MARK_EOL:
		{
			int offset = offsetCursor;
			while (Move(&offset, RIGHT))
				;
			SetCursor(offset);
			UpdateScroll();
			lastCursorColumn = scroll.left + cursorPos.column;
			if (ccode == L_EOL)
				SetMark(0, true);
			else
				SetMark(offsetCursor);
		}
		break;

	case L_BOL:
	case L_MARK_BOL:
		{
			int offset = offsetCursor;
			while (Move(&offset, LEFT))
				;
			SetCursor(offset);
			UpdateScroll();
			lastCursorColumn = scroll.left + cursorPos.column;
			if (ccode == L_BOL)
				SetMark(0, true);
			else
				SetMark(offsetCursor);
		}
		break;

	case L_PGUP:
	case L_MARK_PGUP:
		{
			ZafPositionStruct position = cursorPos;
			ZafEventStruct scrollEvent(S_VSCROLL);
			scrollEvent.scroll.delta = -ZafMin((int)(vScroll.showing - 1), (int)vScroll.current);
			position.line -= (int)(vScroll.showing + scrollEvent.scroll.delta - 1) * lineHeight;
			position.column = lastCursorColumn;
			Event(scrollEvent);
			SetCursor(position);
			if (ccode == L_MARK_PGUP)
				SetMark(offsetCursor);
			UpdateCursor();
		}
		break;

	case L_PGDN:
	case L_MARK_PGDN:
		{
			ZafPositionStruct position = cursorPos;
			ZafEventStruct scrollEvent(S_VSCROLL);
			scrollEvent.scroll.delta = ZafMin((int)(vScroll.showing - 1),
				(int)(vScroll.maximum - vScroll.current));
			position.line += (int)(vScroll.showing - scrollEvent.scroll.delta - 1) * lineHeight;
			position.column = lastCursorColumn;
			Event(scrollEvent);
			SetCursor(position);
			if (ccode == L_MARK_PGDN)
				SetMark(offsetCursor);
			UpdateCursor();
		}
		break;

	case L_DELETE:
		if(!ViewOnly())
		{
			if (offsetMarkAnchor)
			{
				SetCursor(ZafMin(offsetMarkAnchor, offsetMarkFloat));
				UpdateScroll();
				int oldLines = lines;
				Delete(&offsetCursor);
				while (Delete(&offsetCursor) != TXC_MARK)
					;
				offsetMarkAnchor = offsetMarkFloat = 0;

				int drawToEnd = lines != oldLines;

				if (WordWrap())
					Wrap(offsetCursor, true, true, drawToEnd);
				else if (drawToEnd)
					Draw(offsetSplit, -1, true);
				else
					Draw(offsetSplit, 0, true);
			}
			else
			{
				int offset = offsetCursor;
				if (Move(&offset, RIGHT))
				{
					Delete(&offsetCursor);

					if (WordWrap())
						Wrap(offsetSplit, true);
					else
						Draw(offsetSplit, 0, true);
				}
				else if (Move(&offset, NEXT_LINE))
				{
					Delete(&offsetCursor);

					if (WordWrap())
						Wrap(offsetSplit, false, true, true);
					else
						Draw(offsetSplit, -1, true);
				}
			}
			UpdateScroll(true);
			lastCursorColumn = scroll.left + cursorPos.column;
		}
		break;

	case L_BACKSPACE:
		if(!ViewOnly() && Event(ZafEventStruct(L_LEFT)) != S_UNKNOWN)
			Event(L_DELETE);
		break;

	case N_HSCROLL:
	case S_HSCROLL:
		{
			int scrollDelta = (int)event.scroll.delta;
			if (scrollDelta + hScroll.current < 0)
				scrollDelta = (int)-hScroll.current;
			else if (scrollDelta + hScroll.current > hScroll.maximum)
				scrollDelta = (int)(hScroll.maximum - hScroll.current);

			if (scrollDelta == 0)
				break;

			scroll.left += scrollDelta * (clientRegion.Width() / HSCROLL_SHOWING);
			cursorPos.column -= scrollDelta * (clientRegion.Width() / HSCROLL_SHOWING);

			Draw(offsetTopLine, -1, true);
			if (ccode == S_HSCROLL && HorizontalScrollBar())
				HorizontalScrollBar()->Event(event);

			hScroll.current += scrollDelta;
			UpdateCursor();
		}
		break;

	case N_VSCROLL:
	case S_VSCROLL:
		{
			int scrollDelta = (int)event.scroll.delta;

			if (scrollDelta + vScroll.current < 0)
				scrollDelta = (int)-vScroll.current;
			else if (scrollDelta + vScroll.current > vScroll.maximum)
				scrollDelta = (int)(vScroll.maximum - vScroll.current);

			if (scrollDelta == 0)
				break;

			int i;
			if (scrollDelta > 0)
				for (i = 0; i < scrollDelta; i++)
					Move(&offsetTopLine, NEXT_LINE);
			else
				for (i = 0; i > scrollDelta; i--)
					Move(&offsetTopLine, PREV_LINE);

			scroll.top += scrollDelta * lineHeight;
			cursorPos.line -= scrollDelta * lineHeight;
			Draw(offsetTopLine, -1, true);
			if (ccode == S_VSCROLL && VerticalScrollBar())
				VerticalScrollBar()->Event(event);
			vScroll.current += scrollDelta;
			UpdateCursor();
		}
		break;

	case L_FIRST:
		SetCursor(2);
		UpdateScroll();
		lastCursorColumn = scroll.left + cursorPos.column;
		break;

	case L_LAST:
		SetCursor(editSize - 2);
		UpdateScroll();
		lastCursorColumn = scroll.left + cursorPos.column;
		break;

	case L_WORD_LEFT:
	case L_MARK_WORD_LEFT:
		{
			int offset = offsetCursor;
			if (!Move(&offset, LEFT) && editBuffer[offset - 1] != TXC_START)
				offset -= 2;
			int wordOffset = offset;
			while (Move(&offset, LEFT) && editBuffer[offset] != ' ')
				wordOffset = offset;
			SetCursor(wordOffset);
			if (ccode == L_MARK_WORD_LEFT)
				SetMark(offsetCursor);
			UpdateScroll();
			lastCursorColumn = scroll.left + cursorPos.column;
		}
		break;

	case L_WORD_RIGHT:
	case L_MARK_WORD_RIGHT:
		{
			int offset = offsetCursor;
			while (editBuffer[offset] != ' ' && Move(&offset, RIGHT))
				;
			if (!Move(&offset, RIGHT) && editBuffer[offset + 1] != TXC_END)
				offset += 2;
			SetCursor(offset);
			if (ccode == L_MARK_WORD_RIGHT)
				SetMark(offsetCursor);
			UpdateScroll();
			lastCursorColumn = scroll.left + cursorPos.column;
		}
		break;

	case L_NEXT:
	case L_PREVIOUS:
		ccode = S_UNKNOWN;
		break;

	case L_INSERT_TOGGLE:
		if (!ViewOnly())
		{
/* START BLOCK COMMENT
**				if (insert)
**					insert = false;
**				else
**					insert = true;
END BLOCK COMMENT */
		}

		eventManager->SetDeviceState(E_CURSOR, /*insert ? */DC_INSERT /*: DC_OVERSTRIKE*/);
		break;

	case L_BEGIN_SELECT:
	case L_END_SELECT:
	case L_CONTINUE_SELECT:
//	case L_CONTINUE_COPY_DRAG:
//	case L_END_COPY_DRAG:
	case L_VIEW:
		{
			if (Draggable() && ccode != L_VIEW && !windowManager->dragObject)
				ZafWindowObject::Event(event);
			if (editMode || windowManager->dragObject)
			{
				ZafWindowObject::Event(event);
				break;
			}

			if (ccode == L_BEGIN_SELECT)
				windowManager->CaptureMouse(this);
			else if (ccode == L_END_SELECT)
				windowManager->CaptureMouse(0);

			if (Noncurrent())
				return (ccode);

			// event.position is 0,0 based for the visible client region after 
			// it has been dispatched.
			ZafPositionStruct mousePosition = event.position;
			mousePosition.line += clientRegion.top;
			mousePosition.column += clientRegion.left;
			if (clientRegion.Overlap(mousePosition))
			{
				if (ccode == L_VIEW)
					eventManager->SetDeviceState(E_MOUSE, DM_EDIT);
				else if (ccode != L_END_SELECT)
				{
					SetCursor(event.position);
					if (ccode == L_BEGIN_SELECT)
						SetMark(0);
					SetMark(offsetCursor);
					UpdateCursor();
					lastCursorColumn = scroll.left + cursorPos.column;
				}
			}
			else
				ccode = ZafWindow::Event(event);
		}
		break;

	case L_DOUBLE_CLICK:
		{
		ZafEventStruct uEvent = event;
		uEvent.type = L_WORD_LEFT;

		// Mark the double clicked word.
		SetMark(0);
		Event(uEvent);
		uEvent.type = L_MARK_WORD_RIGHT;
		Event(uEvent);
		}
		break;

	case L_CUT:
		ccode = S_CUT;
		// Continue to S_CUT.

	case S_CUT:
	case S_COPY:
	case L_COPY:
		{
			if (!offsetMarkAnchor)
				break;

			int markStart = ZafMin(offsetMarkAnchor, offsetMarkFloat);
			int markEnd = ZafMax(offsetMarkAnchor, offsetMarkFloat);

			if (pasteBuffer)
				delete []pasteBuffer;

			pasteLength = UnformatText(ZAF_NULLP(ZafIChar), markStart, markEnd);
			pasteBuffer = new ZafIChar[pasteLength + 1];
			UnformatText(pasteBuffer, markStart, markEnd);

			if (ccode == S_CUT)
				Event(L_DELETE);
			else
				SetMark(0);
			UpdateScroll(true);
		}
		break;

	case S_PASTE:
	case L_PASTE:
		if (pasteBuffer && (stringData->MaxLength() == -1 || pasteLength <= stringData->MaxLength() - textLength))
		{
			int oldLines = lines;

			// Insert the text.
			int pasteIndex = 0;
			int bufferIndex = offsetCursor;
			int updateOffset = CursorOffset();
			do
			{
				while (pasteBuffer[pasteIndex] && pasteBuffer[pasteIndex] != '\r' && pasteBuffer[pasteIndex] != '\n')
				{
//					SetCursor(bufferIndex);
					Insert(&bufferIndex, pasteBuffer[pasteIndex++]);
					bufferIndex = offsetSplit;
				}
				if (pasteBuffer[pasteIndex])
				{
//					SetCursor(bufferIndex);
					Insert(&bufferIndex, 0, TXC_RETURN);
					bufferIndex = offsetSplit;
				}
				if (pasteBuffer[pasteIndex] == '\r' && pasteBuffer[pasteIndex + 1] == '\n')
					pasteIndex++;
			} while (pasteBuffer[pasteIndex++]);

			// Update.
			SetCursor(bufferIndex);
			int drawToEnd = lines != oldLines;
			if (WordWrap())
				Wrap(EditOffset(updateOffset), true, true, drawToEnd);
			else if (drawToEnd)
				Draw(EditOffset(updateOffset), -1, true);
			else
				Draw(EditOffset(updateOffset), 0, true);
			UpdateScroll();
		}
		break;

	case S_DEINITIALIZE:
		if (Focus())
		{
			// Deactivate the cursor device.
			SetFocus(false);
			ZafEventStruct cEvent(D_DEACTIVATE);
			eventManager->Event(cEvent, E_CURSOR);
			eventManager->SetDeviceState(E_CURSOR, D_OFF);
		}
		return (ZafWindow::Event(event));
	
	case S_COPY_DATA:
		{
			ZafWindowObject *object = event.windowObject;
			ZafText *text = DynamicPtrCast(object, ZafText);
			if (!text)
				break;

			stringData->SetText(*text->StringData());
		}
		break;

	case S_SET_DATA:
		{
			ZafWindowObject *object = event.windowObject;
			if (object)
			{
				ZafText *text = DynamicPtrCast(object, ZafText);
				if (!text)
					break;

				SetStringData(text->StringData());
			}
			else
				SetStringData(new ZafStringData(*stringData));

		}
		break;

	case S_UPDATE_OBJECT:
		OSSetText();
		break;

	case S_UPDATE_DATA:
		OSGetText();
		break;

	// ----- Default or unknown messages -----
	default:
		ccode = ZafWindow::Event(event);
		break;
	}

	// Return the control code.
	return (ccode);
}

void ZafText::FormatText(void)
{
	// Create edit buffer.
	int tLength = stringData->MaxLength() == -1 ? strlen(stringData->Text()) : stringData->MaxLength();
	if (!editBuffer || editSize < tLength + tLength / 5 + 14)
	{
		if (editBuffer)
			delete []editBuffer;
		editSize = tLength + tLength / 5 + 14;
		editBuffer = new ZafIChar[editSize];
	}

	// Initialize edit buffer.
	splitSize = editSize - 4;
	offsetCursor = offsetSave = offsetTopLine = offsetSplit = 2;
	offsetMarkAnchor = offsetMarkFloat = 0;
	editBuffer[0] = 0;
	editBuffer[1] = TXC_START;
	editBuffer[offsetSplit] = 0;
	editBuffer[offsetSplit + 1] = TXC_SPLIT_START;
	editBuffer[offsetSplit + splitSize - 2] = 0;
	editBuffer[offsetSplit + splitSize - 1] = TXC_SPLIT_END;
	editBuffer[editSize - 2] = 0;
	editBuffer[editSize - 1] = TXC_END;
	textLength = 0;

	// Copy / Format text.
	lines = width = 1;
	int textIndex = 0;
#if 0
	int lastOffsetSplit = 0;
	int lastSplitSize = 0;
	int lastTextLength = 0;
	int lastLines = 0;
	int lastTextIndex = -1;
	int didWrap = false;
	int maxWidth = clientRegion.Width();
	int currentWidth = 0;
	while (text[textIndex])
	{
		if (text[textIndex] == '\r' || text[textIndex] == '\n')
		{
			editBuffer[offsetSplit++] = 0;
			editBuffer[offsetSplit++] = (ZafIChar)TXC_RETURN;
			textLength += 2;
			splitSize -= 2;
			lines++;
			if (text[textIndex] == '\r' && text[textIndex + 1] == '\n')
				textIndex++;
			currentWidth = 0;
			textIndex++;
			lastTextIndex = -1;
		}
		else
		{
			if (!FlagSet(wnFlags, WNF_NO_WRAP))
			{
				ZafIChar charString[2];
				charString[0] = text[textIndex];
				charString[1] = 0;
				currentWidth += display->TextWidth(charString, screenID, font);
				if (currentWidth > maxWidth)
				{
					// backup to the last space
					if (text[textIndex] != ' ' && lastTextIndex >= 0)
					{
						offsetSplit = lastOffsetSplit;
						splitSize = lastSplitSize;
						textLength = lastTextLength;
						lines = lastLines;
						textIndex = lastTextIndex;
					}
					editBuffer[offsetSplit++] = 0;
					editBuffer[offsetSplit++] = (ZafIChar)TXC_WRAP;
					splitSize -= 2;
					lines++;
					currentWidth = 0;
					didWrap = true;
					lastTextIndex = -1;
				}
			}
			if (!(didWrap && text[textIndex] == ' '))
			{
				textLength++;
				editBuffer[offsetSplit++] = (ZafIChar)text[textIndex];
				splitSize--;
			}
			textIndex++;
			didWrap = false;
			if (!FlagSet(wnFlags, WNF_NO_WRAP))
			{
				if (text[textIndex-1] == ' ')
				{
					lastOffsetSplit = offsetSplit;
					lastSplitSize = splitSize;
					lastTextLength = textLength;
					lastLines = lines;
					lastTextIndex = textIndex;
				}
			}
		}
	}
	editBuffer[offsetSplit] = 0;
	editBuffer[offsetSplit + 1] = TXC_SPLIT_START;

#else
	int bufferIndex = 2;
	ZafIChar *text = strdup(stringData->Text());
	do
	{
		int lineIndex = bufferIndex;
		while (text[textIndex] && text[textIndex] != '\r' && text[textIndex] != '\n')
		{
			Insert(&bufferIndex, text[textIndex++]);
			bufferIndex = offsetSplit;
		}
		if (text[textIndex])
		{
			Insert(&bufferIndex, 0, TXC_RETURN);
			bufferIndex = offsetSplit;
		}
		if (WordWrap())
		{
			Wrap(lineIndex, false, false);
			bufferIndex = editSize - 2;
			SetSplit(&bufferIndex);
		}
		else
			width = ZafMax(width, LineWidth(lineIndex) / (clientRegion.Width() / HSCROLL_SHOWING) + 1);
		if (text[textIndex] == '\r' && text[textIndex + 1] == '\n')
			textIndex++;
	} while (text[textIndex++]);
	delete []text;
#endif

	// Initialize Data.
	scroll.left = scroll.top = 0;
	cursorPos.column = cursorPos.line = 0;
	lastCursorColumn = 0;
	vScroll.minimum = vScroll.maximum = vScroll.current =
		vScroll.showing = 0;
	vScroll.delta = 1;
	hScroll.minimum = hScroll.maximum = hScroll.current =
		hScroll.showing = 0;
	hScroll.delta = 1;
}

void ZafText::GetPosition(int offset, ZafPositionStruct *position)
{
	*position = cursorPos;
	int tOffset = offsetCursor;

	int lineOffset = tOffset;
	if (tOffset < offset)
	{
		while (Move(&tOffset, NEXT_LINE) && tOffset <= offset)
		{
			position->line += lineHeight;
			lineOffset = tOffset;
		}
	}
	else
	{
		while (Move(&tOffset, LEFT))
			;
		while (tOffset > offset && Move(&tOffset, PREV_LINE))
		{
			position->line -= lineHeight;
			lineOffset = tOffset;
		}
	}

	tOffset = lineOffset;
	if (tOffset != offsetCursor)
		position->column = -scroll.left;
	Move(&tOffset, 0);

	if (tOffset < offset)
		do
			position->column += CharWidth(tOffset);
		while (Move(&tOffset, RIGHT) && tOffset < offset);
	else
		while (Move(&tOffset, LEFT) && tOffset >= offset)
			position->column -= CharWidth(tOffset);
}

int ZafText::GetTextWidth(ZafIChar *string)
{
#if defined(DOS16PP)
	// Workaround for DPMI16 PowerPak textwidth() bug.
	int bufferLength = 255;
	if (strlen(string) > bufferLength)
	{
		ZafIChar *stringPtr = string;
		ZafIChar saveChar;
		int tempLength = bufferLength;
		int textWidth = 0;
		do	{
			saveChar = stringPtr[tempLength];
			stringPtr[tempLength] = 0;
			textWidth += display->TextWidth(stringPtr, screenID, font);
			stringPtr[tempLength] = saveChar;
			stringPtr += tempLength;
			tempLength = MinValue(strlen(stringPtr), bufferLength);
		} while (*stringPtr);

		return textWidth;
	}
	else
		return (display->TextWidth(string, screenID, font));
#else
	display->SetFont(LogicalPalette(ZAF_PM_TEXT, ZAF_PM_ANY_STATE).font);
	return (display->TextSize(string).Width());
#endif
}

void ZafText::Insert(int *offset, ZafIChar value, ZafIChar code)
{
	SetSplit(offset);
	if (value == 0)
	{
		editBuffer[offsetSplit++] = 0;
		splitSize--;
		value = code;
		if (code == TXC_RETURN)
			textLength += 2;
	}
	else
		textLength++;

	editBuffer[offsetSplit++] = (ZafIChar)value;
	splitSize--;
	editBuffer[offsetSplit] = 0;
	editBuffer[offsetSplit + 1] = TXC_SPLIT_START;
	if (value == TXC_RETURN || value == TXC_WRAP)
		lines++;

	// Also need room for marking.
	if (splitSize - 2 < 4 + 4 && code != TXC_MARK)
	{
		int oldSize = editSize;
		int leftBufferSize = offsetSplit + 2;
		int delta = editSize / 5 + 10;
		ZafIChar *oldBuffer = editBuffer;

		editSize += delta;
		splitSize += delta;
		editBuffer = new ZafIChar[editSize];
		memcpy(editBuffer, oldBuffer, leftBufferSize * sizeof(ZafIChar));
		memcpy(editBuffer + leftBufferSize + delta, oldBuffer + leftBufferSize,
			(oldSize - leftBufferSize) * sizeof(ZafIChar));
		delete oldBuffer;
	}
}

int ZafText::LineWidth(int offset, bool wrap)
{
	int width = 0;
	bool finished = false;
	while (!finished)
	{
		if (editBuffer[offset])
		{
			width += GetTextWidth(&editBuffer[offset]);
			while (editBuffer[offset])
				offset++;
		}

		switch (editBuffer[offset + 1])
		{
		case TXC_END:
		case TXC_RETURN:
		case TXC_WRAP:
			if (wrap)
				while (Move(&offset, LEFT) && editBuffer[offset] == ' ')
					width -= CharWidth(offset);
			finished = true;
			break;

		case TXC_SPLIT_START:
			offset += splitSize;
			break;

		default:
			offset += 2;
			break;
		}
	}
	return width;
}

int ZafText::Move(int *offset, int moveCode) const
{
	if (!editBuffer)
		return (false);

	int newOffset = *offset;
	int direction = (moveCode == LEFT || moveCode == PREV_LINE) ? -1 : 1;
	bool moved = false;

	bool finished = false;
	while (!finished)
	{
		if (moveCode == 0 && editBuffer[newOffset])
		{
			*offset = newOffset;
			return true;
		}

		if (direction > 0 && editBuffer[newOffset])
		{
			if (moveCode == RIGHT)
			{
				if (moved)
					finished = true;
				else
				{
					moved = true;
					newOffset++;
				}
			}
			else
				newOffset++;
		}
		else if (direction < 0 && editBuffer[newOffset - 2])
		{
			newOffset--;
			if (moveCode == LEFT)
				finished = true;
		}
		else
		{
			switch (editBuffer[newOffset + direction])
			{
			case TXC_START:
			case TXC_END:
				if (moveCode == PREV_LINE && moved)
					finished = true;
				else
				{
					*offset = newOffset;
					return moved;
				}
				break;

			case TXC_RETURN:
			case TXC_WRAP:
				if (moveCode == NEXT_LINE)
				{
					newOffset += 2;
					finished = true;
				}
				else if (moveCode == PREV_LINE)
				{
					if (moved)
						finished = true;
					else
					{
						newOffset -= 2;
						moved = true;
					}
				}
				else
				{
					*offset = newOffset;
					return moved;
				}
				break;

			case TXC_SPLIT_START:
				if (direction > 0)
					newOffset += splitSize;
				else
					newOffset -= 2;
				break;

			case TXC_SPLIT_END:
				if (direction < 0)
					newOffset -= splitSize;
				else
					newOffset += 2;
				break;

			default:
				newOffset += direction * 2;
				break;
			}
		}
	}
	*offset = newOffset;
	return true;
}

void ZafText::SetCursor(const ZafPositionStruct &position)
{
	ZafPositionStruct tPosition = cursorPos;
	int offset = offsetCursor;
	while (tPosition.line + lineHeight - 1 < position.line && Move(&offset, NEXT_LINE))
		tPosition.line += lineHeight;
	while (tPosition.line > position.line && Move(&offset, PREV_LINE))
		tPosition.line -= lineHeight;

	if (offset != offsetCursor)
		tPosition.column = -scroll.left;

	if (tPosition.column < position.column)
	{
		int charWidth = CharWidth(offset);
		while (tPosition.column + charWidth / 2 < position.column && Move(&offset, RIGHT))
		{
			tPosition.column += charWidth;
			charWidth = CharWidth(offset);
		}
	}
	else
	{
		int prevOffset = offset;
		Move(&prevOffset, LEFT);
		int charWidth = CharWidth(prevOffset);
		tPosition.column -= charWidth;
		while (tPosition.column + charWidth / 2 >= position.column)
		{
			offset = prevOffset;
			if (!Move(&prevOffset, LEFT))
				break;
			charWidth = CharWidth(prevOffset);
			tPosition.column -= charWidth;
		}
	}

	SetCursor(offset);
}

void ZafText::SetCursor(int offset)
{
	GetPosition(offset, &cursorPos);
	offsetCursor = offset;
}

ZafError ZafText::SetCursorOffset(int offset)
{
	SetCursor(EditOffset(offset));
	UpdateScroll();
	return (ZAF_ERROR_NONE);
}

ZafError ZafText::SetCursorPosition(ZafPositionStruct position)
{
	int line = 0;
	int column = 0;
	int offset = 2;
	while (line < position.line && Move(&offset, NEXT_LINE))
		line++;
	while (column < position.column && Move(&offset, RIGHT))
		column++;
	SetCursor(offset);
	UpdateScroll();
	return (ZAF_ERROR_NONE);
}

void ZafText::SetMark(int offset, bool repaint)
{
	if (offset == 0)
	{
		if (offsetMarkAnchor)
		{
			if (repaint)
				Draw(ZafMin(offsetMarkAnchor, offsetMarkFloat),
					ZafMax(offsetMarkAnchor, offsetMarkFloat), true, true);	
			Delete(&offsetMarkAnchor);
			Delete(&offsetMarkFloat);
			offsetMarkAnchor = offsetMarkFloat = 0;
		}
	}
	else if (!offsetMarkAnchor)
	{
		offsetMarkAnchor = offsetMarkFloat = offset;
		Insert(&offsetMarkAnchor, 0, TXC_MARK);
		Insert(&offsetMarkFloat, 0, TXC_MARK);
	}
	else
	{
		offsetSave = ZafMin(ZafMax(ZafMin(offsetCursor, offsetMarkFloat),
			offsetMarkAnchor), ZafMax(offsetCursor, offsetMarkFloat));

		// Insert mark code.
		Insert(&offsetCursor, 0, TXC_MARK);

//		if (offsetCursor == offsetMarkAnchor)
//			errorSystem->Beep();

		// Erase.
		if (offsetMarkFloat != offsetSave && repaint)
			Draw(ZafMin(offsetMarkFloat, offsetSave),
				ZafMax(offsetMarkFloat, offsetSave), true, true);

		// Draw.
		if (offsetCursor != offsetSave && repaint)
			Draw(ZafMin(offsetCursor, offsetSave),
				ZafMax(offsetCursor, offsetSave), false, true);

		// Delete mark code.
		Delete(&offsetMarkFloat);
		offsetMarkFloat = offsetCursor;
	}
}

void ZafText::SetSplit(int *offset)
{
	int newOffset = *offset;
	if (newOffset != offsetSplit)
	{
		if (newOffset < offsetSplit)
		{
			memmove(&editBuffer[newOffset + splitSize], &editBuffer[newOffset],
				(offsetSplit - newOffset) * sizeof(ZafIChar));
			if (offsetCursor > newOffset && offsetCursor <= offsetSplit)
				offsetCursor += splitSize;
			if (offsetTopLine > newOffset && offsetTopLine <= offsetSplit)
				offsetTopLine += splitSize;
			if (offsetMarkAnchor >= newOffset && offsetMarkAnchor <= offsetSplit)
				offsetMarkAnchor += splitSize;
			if (offsetMarkFloat >= newOffset && offsetMarkFloat <= offsetSplit)
				offsetMarkFloat += splitSize;
			if (offsetSave >= newOffset && offsetSave <= offsetSplit)
				offsetSave += splitSize;
		}
		else if (newOffset > offsetSplit)
		{
			memmove(&editBuffer[offsetSplit], &editBuffer[offsetSplit + splitSize],
				(newOffset - offsetSplit - splitSize) * sizeof(ZafIChar));
			if (offsetCursor <= newOffset && offsetCursor > offsetSplit)
				offsetCursor -= splitSize;
			if (offsetTopLine <= newOffset && offsetTopLine > offsetSplit)
				offsetTopLine -= splitSize;
			if (offsetMarkAnchor < newOffset && offsetMarkAnchor > offsetSplit)
				offsetMarkAnchor -= splitSize;
			if (offsetMarkFloat < newOffset && offsetMarkFloat > offsetSplit)
				offsetMarkFloat -= splitSize;
			if (offsetSave < newOffset && offsetSave > offsetSplit)
				offsetSave -= splitSize;
			newOffset -= splitSize;
		}
		offsetSplit = newOffset;
		editBuffer[offsetSplit] = 0;
		editBuffer[offsetSplit + 1] = TXC_SPLIT_START;
		editBuffer[offsetSplit + splitSize - 2] = 0;
		editBuffer[offsetSplit + splitSize - 1] = TXC_SPLIT_END;
	}
	*offset = newOffset;
}

ZafWindowObject *ZafText::Subtract(ZafWindowObject *object)
{
	// Defer to the base class.
	return (ZafWindow::Subtract(object));
}

int ZafText::TextOffset(int editOffset) const
{
	int textOffset = 0;
	int offset = 2;
	while (offset < editOffset)
	{
		if (Move(&offset, RIGHT))
			textOffset++;
		else if (Move(&offset, NEXT_LINE))
		{
			if (editBuffer[offset - 1] == TXC_RETURN)
				textOffset += 2;
		}
		else
			break;
	}
	return (textOffset);
}

int ZafText::UnformatText(ZafIChar *string, int startOffset, int endOffset)
{
	if (!editBuffer)
		return (0);

	int stringIndex = 0;
	int editIndex = startOffset;
	bool finished = false;
	while (!finished)
	{
		if (editBuffer[editIndex])
		{
			if (string)
				string[stringIndex] = editBuffer[editIndex];
			stringIndex++;
			editIndex++;
		}
		else
		{
			if (editBuffer[editIndex + 1] == TXC_RETURN)
			{
				if (string)
				{
					string[stringIndex] = '\r';
					string[stringIndex + 1] = '\n';
				}
				stringIndex += 2;
				editIndex += 2;
			}
			else if (editBuffer[editIndex + 1] == TXC_SPLIT_START)
				editIndex += splitSize;
			else if (editBuffer[editIndex + 1] == TXC_END)
				finished = true;
			else
				editIndex += 2;
		}
		if (endOffset > 0 && editIndex >= endOffset)
			finished = true;
	}
	if (string)
		string[stringIndex] = 0;
	return (stringIndex);
}

void ZafText::UpdateCursor(void)
{
	if (Focus())
	{
#if defined(ZAF_GRAPHICS_MODE)
		int cSize = 14;
#elif defined(ZAF_TEXT_MODE)
		int cSize = 0;
#endif
		if (cursorPos.column >= 0 && cursorPos.column <= clientRegion.Width() &&
			cursorPos.line >= 0 && cursorPos.line + lineHeight <= clientRegion.Height() &&
			clientRegion.left + cursorPos.column >= clientRegion.left && clientRegion.left + cursorPos.column <= clientRegion.right &&
			clientRegion.top + cursorPos.line >= clientRegion.top && clientRegion.top + cursorPos.line + cSize <= clientRegion.bottom)
		{
			eventManager->SetDeviceState(E_CURSOR, /*insert ? */DC_INSERT /*: DC_OVERSTRIKE*/);
			ZafPositionStruct cursorPosition;
			cursorPosition.Assign(zafRegion.left, zafRegion.top);
			cursorPosition.coordinateType = zafRegion.coordinateType;
			cursorPosition = parent->ConvertToOSPosition(this, &cursorPosition);

#if !defined(ZAF_TEXT_MODE)
			eventManager->SetDevicePosition(E_CURSOR,
				cursorPosition.column + clientRegion.left + cursorPos.column +
				(Bordered() ? ZAF_BORDER_SIZE : 0) - 1,
				cursorPosition.line + clientRegion.top + cursorPos.line +
				(Bordered() ? ZAF_BORDER_SIZE : 0));
#else
			eventManager->SetDevicePosition(E_CURSOR, cursorPosition.column + cursorPos.column,
				cursorPosition.line+cursorPos.line);
#endif
		}
		else
			eventManager->SetDeviceState(E_CURSOR, D_OFF);
	}
}

void ZafText::UpdateScroll(bool redisplay, bool scrollCursor)
{
	ZafEventStruct scrollEvent;

//	if (clientRegion.Width() <= 0 || clientRegion.Height() <= 0)
	if (clientRegion.Width() <= GetTextWidth(ZAF_ITEXT("w")) ||
		clientRegion.Height() <= 0)
	{
		UpdateCursor();
		return;
	}

	int showing = clientRegion.Height() / lineHeight;
	int maximum = ZafMax(0, lines - showing);
	vScroll.maximum = maximum;
	vScroll.showing = showing;
	if (VerticalScrollBar())
	{
		scrollEvent.type = S_VSCROLL_SET;
		scrollEvent.scroll = vScroll;
		VerticalScrollBar()->Event(scrollEvent);
	}

	maximum = ZafMax(0, width - HSCROLL_SHOWING);
	hScroll.maximum = maximum;
	hScroll.showing = HSCROLL_SHOWING;
	if (HorizontalScrollBar())
	{
		scrollEvent.type = S_HSCROLL_SET;
		scrollEvent.scroll = hScroll;
		HorizontalScrollBar()->Event(scrollEvent);
	}

	if (cursorPos.column < 0 && scrollCursor)
	{
		scrollEvent.type = S_HSCROLL;
		scrollEvent.scroll.delta = cursorPos.column / (clientRegion.Width() / HSCROLL_SHOWING) - 1;
		Event(scrollEvent);
	}
	else if (cursorPos.column >= clientRegion.Width() && scrollCursor)
	{
		scrollEvent.type = S_HSCROLL;
		scrollEvent.scroll.delta = (cursorPos.column - clientRegion.Width()) / (clientRegion.Width() / HSCROLL_SHOWING) + 1;
		Event(scrollEvent);
	}

	if (cursorPos.line < 0 && scrollCursor)
	{
		scrollEvent.type = S_VSCROLL;
		scrollEvent.scroll.delta = cursorPos.line / lineHeight;
		Event(scrollEvent);
	}
	else if (cursorPos.line + lineHeight - 1 >= clientRegion.Height() && scrollCursor)
	{
		scrollEvent.type = S_VSCROLL;
		scrollEvent.scroll.delta = (cursorPos.line + lineHeight - 1) / lineHeight - showing + 1;
		Event(scrollEvent);
	}

	if (redisplay && VerticalScrollBar())
		VerticalScrollBar()->Event(S_REDISPLAY);
	if (redisplay && HorizontalScrollBar())
		HorizontalScrollBar()->Event(S_REDISPLAY);
	UpdateCursor();
}

void ZafText::Wrap(int offset, bool testPrevLine, bool draw, bool drawToEnd)
{
	int oldLines = lines;
//	offsetSave = offsetCursor;
	int tOffsetSave = CursorOffset();
	SetCursor(offset);
	int maxWidth = clientRegion.Width();

	if (maxWidth <= 0)
		return;

	while (Move(&offset, LEFT))
		;
	if (testPrevLine && editBuffer[offset - 1] == TXC_WRAP)
		Move(&offset, PREV_LINE);

	bool finished = false;
	while (!finished)
	{
		int lineWidth = LineWidth(offset, true);
		if (lineWidth > maxWidth)
		{
		 	while (Move(&offset, RIGHT))
				;
			while (Move(&offset, LEFT) && editBuffer[offset] == ' ')
				;
			int wrapOffset = 0;
			int forceOffset = 0;
			while (!wrapOffset)
			{
				int prevOffset = offset;
				if (!Move(&prevOffset, LEFT))
				{
					if (!forceOffset)
						return;
					wrapOffset = forceOffset;
				}
				else
				{
					lineWidth -= CharWidth(offset);
					if (lineWidth < maxWidth)
					{
						if (editBuffer[prevOffset] == ' ')
							wrapOffset = offset;
						else if (!forceOffset)
							forceOffset = offset;
					}
					offset = prevOffset;
				}
			}
	
			if (wrapOffset <= offsetCursor)
				SetCursor(wrapOffset);

			Insert(&wrapOffset, 0, TXC_WRAP);
	
			if (draw)
				Draw(offsetCursor, 0, true);

			offset = offsetSplit;
			SetCursor(offsetSplit);
		}
		else
		{
			int lineOffset = offset;
			if (Move(&offset, NEXT_LINE) && editBuffer[offset - 1] == TXC_WRAP)
			{
				int wrapOffset = offset - 2;
				int lastChar = wrapOffset;
				Move(&lastChar, LEFT);

				if (editBuffer[lastChar] == ' ' && editBuffer[offset] != ' ')
				{
					do
					{
						lineWidth += CharWidth(offset);
					} while (lineWidth <= maxWidth && Move(&offset, RIGHT) && editBuffer[offset] != ' ');
					lineWidth += CharWidth(offset);
				}
	
				if (lineWidth <= maxWidth)
				{
					if (wrapOffset < offsetCursor)
						SetCursor(wrapOffset);

					Delete(&wrapOffset);

					offset = lineOffset;
					continue;
				}
				offset = lineOffset;
			}

			if (!Move(&offset, NEXT_LINE) || offsetCursor < offset)
			{
				if (draw)
				{
					if (lines != oldLines)
						drawToEnd = true;
					if (drawToEnd || editBuffer[offset + 1] == TXC_END)
						Draw(offsetCursor, -1, true);
					else
						Draw(offsetCursor, 0, true);
				}
				finished = true;
			}
		}
	}
	SetCursor(EditOffset(tOffsetSave));
//	SetCursorOffset(tOffsetSave);
}

// ----- Attributes ---------------------------------------------------------

bool ZafText::SetAutoClear(bool autoClear)
{
	ZafText::autoClear = autoClear;
	return autoClear;
}

/* START BLOCK COMMENT
**	bool ZafText::SetInsert(bool insert)
**	{
**		ZafText::insert = insert;
**		return insert;
**	}
END BLOCK COMMENT */

bool ZafText::SetVisible(bool tVisible)
{
	return (ZafWindow::SetVisible(tVisible));
}

bool ZafText::SetWordWrap(bool tWordWrap)
{
	if (wordWrap != tWordWrap)
	{
		wordWrap = tWordWrap;
		if (screenID)
		{
			FormatText();
			UpdateScroll(true, false);
			Redisplay();
		}
	}
	return (wordWrap);
}

// ----- OS Specific Functions ----------------------------------------------

ZafEventType ZafText::OSEvent(const ZafEventStruct &)
{
	return (S_ERROR);
}

ZafError ZafText::OSGetText(void)
{
	if (screenID && editBuffer)
	{
		// Update the internal text.
		ZafIChar *text = new ZafIChar[textLength + 1];
		UnformatText(text);
		ZafUpdateType updateType = stringData->Update();
		stringData->SetUpdate(this, ZAF_UPDATE_NONE);
		stringData->SetText(text);
		delete []text;
		stringData->SetUpdate(this, updateType);
	}

	return (ZAF_ERROR_NONE);
}

void ZafText::OSMapPalette(ZafPaletteStruct &palette, ZafPaletteType type, ZafPaletteState state)
{
	ZafWindowObject::OSMapPalette(palette, type, state);
}

void ZafText::OSRegisterObject(void)
{
	ZafWindowObject::OSRegisterObject();
}

OSWindowID ZafText::OSScreenID(ZafScreenIDType) const
{
	return (screenID);
}

ZafError ZafText::OSSetText(void)
{
	if (screenID)
	{
		// Initialize the edit buffer.
		FormatText();
		UpdateScroll(false, false);

		// Update cursor in case the text shifted or the new text is shorter.
/* START BLOCK COMMENT
**			if (Focus())
**				SetCursor(cursor);
END BLOCK COMMENT */
//		Event(ZafEventStruct(S_REDISPLAY_REGION, 0, clientRegion));
		OSInvalidateRegion(0, false, true);
	}
	else
	{
		delete []editBuffer;
		editBuffer = 0;
	}

	return (ZAF_ERROR_NONE);
}

void ZafText::OSSize(void)
{
	// ZafText implements OSSize() instead of using ZafWindow::OSSize()
	// because:
	// 
	// FormatText() requires client to be properly updated, which happens
	// in OSSize.  FormatText must be called before a call to Draw is 
	// issued or else problems, most likely involving crashing, will occur.
	// Since the object is drawn from within OSSize, particularly when first
	// creating the object, FormatText needs to be called from within OSSize.
	// Also, if the object has scroll bars, they must be updated here as well.
	// They must receive the S_?SCROLL_SET message *after* they get the
	// S_COMPUTE_SIZE but before they get the OSSize, which will draw them.
	// Thus, that occurs here, as well (UpdateScroll).

	if (zafRegion == oldRegion && clientRegion == MaxRegion(this, ZAF_VT_TOP, ZAF_HZ_LEFT))
		return;

	if (!screenID)
		return;

	// Update screen and set new region

	// Adjust the support object sizes.
	ZafWindowObject *support;
	for (support = SupportFirst(); support; support = support->Next())
		support->Event(S_COMPUTE_SIZE);

	// Get the new clientRegion region;
	clientRegion = MaxRegion(this, ZAF_VT_TOP, ZAF_HZ_LEFT);

	// Don't do any calculations if a parent is minimized.
	for (ZafWindow *pObject = DynamicPtrCast(parent, ZafWindow);
		pObject; pObject = DynamicPtrCast(pObject->parent, ZafWindow))
		if (pObject->Minimized())
			return;

	// Redraw the window.
	if (oldRegion.left != -10000 && oldRegion.top != -10000 &&
		oldRegion.right != -10000 && oldRegion.bottom != -10000)
	{
//		if (WordWrap())
		{
			int oldTopLine = TextOffset(offsetTopLine);
			int oldCursor = CursorOffset();
			int oldMarkAnchor = offsetMarkAnchor ? TextOffset(offsetMarkAnchor) : -1;

			// Reformat edit buffer.
			SetMark(0, false);
//			int length = stringData->MaxLength() == -1 ? strlen(stringData->Text()) : stringData->MaxLength();
			int length = textLength;
			ZafIChar *text = new ZafIChar[length + 1];
//			text[strlen(Text())] = '\0';
//			strcpy(text, Text());
			UnformatText(text);
			ZafUpdateType updateType = stringData->Update();
			stringData->SetUpdate(ZAF_UPDATE_NONE);
			stringData->SetText(text);
			delete []text;
			stringData->SetUpdate(updateType);
			FormatText();

			// Restore scroll position.
			int offset = EditOffset(oldTopLine);
			while (Move(&offset, LEFT))
				;
			while (offsetTopLine < offset && Move(&offsetTopLine, NEXT_LINE))
				vScroll.current++;
			SetCursor(offsetTopLine);
			cursorPos.column = cursorPos.line = 0;
			UpdateScroll(false, false);

			// Restore cursor and mark positions.
			SetCursorOffset(oldCursor);
			UpdateCursor();
			if (oldMarkAnchor > -1)
			{
				SetMark(EditOffset(oldMarkAnchor), false);
				SetMark(offsetCursor, false);
			}
		}
//		else
//			UpdateScroll(false, false);

		// Redraw old region.
		if (parent && !SupportObject())
		{
			// Only invalidate the parts that don't overlap the new region.
			ZafRegionStruct clip;
			ZafRegionList damageList;
			if (zafRegion.Overlap(oldRegion))
			{
				damageList.Allocate(0, oldRegion);
				clip = zafRegion;
				damageList.Split(0, clip, false);
			}
			else
				damageList.Allocate(0, oldRegion);

			if (damageList.First())
			{
				for (ZafRegionElement *regionElement = damageList.First();
					regionElement; regionElement = regionElement->Next())
						parent->OSInvalidateRegion(&regionElement->region, false, true);
			}
/* START BLOCK COMMENT
**				ZafRegionStruct updateRegion = oldRegion;
**				ZafRegionStruct parentClient = DynamicPtrCast(parent, ZafWindow)->ClientRegion();
**				updateRegion.HzShift(parentClient.left);
**				updateRegion.VtShift(parentClient.top);
END BLOCK COMMENT */
		}

		if (AutomaticUpdate() && Visible())
			OSInvalidateRegion(0, false, false);
	}
	else
	{
		// Initialize the edit buffer.
		int oldCursor = CursorOffset();
		FormatText();
		UpdateScroll(false, false);
		SetCursorOffset(oldCursor);

		if (AutomaticUpdate())
		{
/* START BLOCK COMMENT
**				ZafRegionStruct updateRegion;
**				updateRegion.left = updateRegion.top = 0;
**				updateRegion.right = zafRegion.Width() - 1;
**				updateRegion.bottom = zafRegion.Height() - 1;
**				updateRegion.coordinateType = zafRegion.coordinateType;
**				Draw(ZafEventStruct(S_REDISPLAY_REGION, 0, updateRegion), S_REDISPLAY_REGION);
END BLOCK COMMENT */
			OSInvalidateRegion(0, false, false);
		}
	}

	oldRegion = zafRegion;

	// Finalize the sizing operation for support objects.
	for (support = SupportFirst(); support; support = support->Next())
		support->OSSize();

	Event(N_SIZE);
}

ZafError ZafText::OSUpdatePalettes(ZafPaletteType zafTypes, ZafPaletteType osTypes)
{
	// Default to the base class.
	return (ZafWindow::OSUpdatePalettes(zafTypes, osTypes));
}

