/*
editor.cpp

Редактор
*/
/*
Copyright (c) 1996 Eugene Roshal
Copyright (c) 2000 Far Group
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. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.
3. The name of the authors 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.
*/

#include "headers.hpp"

#include "editor.hpp"
#include "edit.hpp"
#include "keyboard.hpp"
#include "lang.hpp"
#include "macroopcode.hpp"
#include "keys.hpp"
#include "ctrlobj.hpp"
#include "chgprior.hpp"
#include "filestr.hpp"
#include "dialog.hpp"
#include "fileedit.hpp"
#include "savescr.hpp"
#include "scrbuf.hpp"
#include "TPreRedrawFunc.hpp"
#include "syslog.hpp"
#include "interf.hpp"
#include "message.hpp"
#include "mix.hpp"
#include "clipboard.hpp"
#include "xlat.hpp"
#include "datetime.hpp"
#include "stddlg.hpp"
#include "strmix.hpp"
#include "farcolors.hpp"
#include "DialogBuilder.hpp"
#include "wakeful.hpp"
#include "codepage.hpp"

static int ReplaceMode, ReplaceAll;

static int EditorID = 0;

// EditorUndoData
enum
{
	UNDO_EDIT = 1,
	UNDO_INSSTR,
	UNDO_DELSTR,
	UNDO_BEGIN,
	UNDO_END
};

Editor::Editor(ScreenObject *pOwner, bool DialogUsed)
	:
	UndoPos(nullptr),
	UndoSavePos(nullptr),
	UndoSkipLevel(0),
	LastChangeStrPos(0),
	NumLastLine(0),
	NumLine(0),
	Pasting(0),
	BlockStart(nullptr),
	BlockStartLine(0),
	VBlockStart(nullptr),
	MaxRightPos(0),
	LastSearchCase(GlobalSearchCase),
	LastSearchWholeWords(GlobalSearchWholeWords),
	LastSearchReverse(GlobalSearchReverse),
	LastSearchSelFound(Opt.EdOpt.SearchSelFound),
	LastSearchRegexp(Opt.EdOpt.SearchRegexp),
	m_codepage(CP_OEMCP),
	StartLine(-1),
	StartChar(-1),
	StackPos(0),
	NewStackPos(FALSE),
	EditorID(::EditorID++),
	HostFileEditor(nullptr),
	TopList(nullptr),
	EndList(nullptr),
	TopScreen(nullptr),
	CurLine(nullptr),
	LastGetLine(nullptr),
	LastGetLineNumber(0),
	SaveTabSettings(false)
{
	_KEYMACRO(SysLog(L"Editor::Editor()"));
	_KEYMACRO(SysLog(1));
	EdOpt = Opt.EdOpt;
	SetOwner(pOwner);

	if (DialogUsed)
		Flags.Set(FEDITOR_DIALOGMEMOEDIT);

	/*
		$ 26.10.2003 KM
		Если установлен глобальный режим поиска 16-ричных кодов, тогда
		сконвертируем GlobalSearchString в строку, ибо она содержит строку в
		16-ричном представлении.
	*/
	if (GlobalSearchHex)
		Transform(strLastSearchStr, strGlobalSearchString, L'S');
	else
		strLastSearchStr = strGlobalSearchString;

	UnmarkMacroBlock();
	/*
		$ 12.01.2002 IS
		По умолчанию конец строки так или иначе равен \r\n, поэтому нечего
		пудрить мозги, пропишем его явно.
	*/
	wcscpy(GlobalEOL, NATIVE_EOLW);
	memset(&SavePos, 0xff, sizeof(SavePos));
	InsertString(nullptr, 0);
}

Editor::~Editor()
{
	//_SVS(SysLog(L"[%p] Editor::~Editor()",this));
	FreeAllocatedData();
	KeepInitParameters();
	_KEYMACRO(SysLog(-1));
	_KEYMACRO(SysLog(L"Editor::~Editor()"));
}

void Editor::FreeAllocatedData(bool FreeUndo)
{
	while (EndList) {
		Edit *Prev = EndList->m_prev;
		delete EndList;
		EndList = Prev;
	}

	UndoData.Clear();
	UndoSavePos = nullptr;
	UndoPos = nullptr;
	UndoSkipLevel = 0;
	ClearStackBookmarks();
	TopList = EndList = CurLine = nullptr;
	NumLastLine = 0;
	NumLine = 0;
}

void Editor::KeepInitParameters()
{
	// Установлен глобальный режим поиска 16-ричных данных?
	if (GlobalSearchHex)
		Transform(strGlobalSearchString, strLastSearchStr, L'X');
	else
		strGlobalSearchString = strLastSearchStr;

	GlobalSearchCase = LastSearchCase;
	GlobalSearchWholeWords = LastSearchWholeWords;
	GlobalSearchReverse = LastSearchReverse;
	Opt.EdOpt.SearchSelFound = LastSearchSelFound;
	Opt.EdOpt.SearchRegexp = LastSearchRegexp;
}

/*
	преобразование из буфера в список
*/
int Editor::SetRawData(const wchar_t *SrcBuf, int SizeSrcBuf, int TextFormat)
{
#if defined(PROJECT_DI_MEMOEDIT)
	// InsertString(const wchar_t *lpwszStr, int nLength, Edit *pAfter)
	TextChanged(1);

#endif
	return TRUE;
}

/*
	Editor::Edit2Str - преобразование из списка в буфер с учетом EOL

		DestBuf     - куда сохраняем (выделяется динамически!)
		SizeDestBuf - размер сохранения
		TextFormat  - тип концовки строк
*/
int Editor::GetRawData(wchar_t **DestBuf, int &SizeDestBuf, int TextFormat)
{
#if defined(PROJECT_DI_MEMOEDIT)
	wchar_t *PDest = nullptr;
	SizeDestBuf = 0;	// общий размер = 0

	const wchar_t *SaveStr, *EndSeq;

	int Length;

	// посчитаем количество строк и общий размер памяти (чтобы не дергать realloc)
	Edit *CurPtr = TopList;

	DWORD AllLength = 0;

	while (CurPtr) {
		CurPtr->GetBinaryString(&SaveStr, &EndSeq, Length);
		AllLength+= Length + StrLength(!TextFormat ? EndSeq : GlobalEOL) + 1;
	}

	wchar_t *MemEditStr = reinterpret_cast<wchar_t *>(malloc((AllLength + 8) * sizeof(wchar_t)));

	if (MemEditStr) {
		*MemEditStr = 0;
		PDest = MemEditStr;

		// прйдемся по списку строк
		CurPtr = TopList;

		AllLength = 0;

		while (CurPtr) {
			CurPtr->GetBinaryString(&SaveStr, &EndSeq, Length);
			wmemcpy(PDest, SaveStr, Length);
			PDest+= Length;

			size_t LenEndSeq;
			if (!TextFormat) {
				LenEndSeq = StrLength(EndSeq);
				wmemcpy(PDest, EndSeq, LenEndSeq);
			} else {
				LenEndSeq = StrLength(GlobalEOL);
				wmemcpy(PDest, GlobalEOL, LenEndSeq);
			}

			PDest+= LenEndSeq;

			AllLength+= LenEndSeq + Length;

			CurPtr = CurPtr->m_next;
		}

		*PDest = 0;

		SizeDestBuf = AllLength;
		DestBuf = &MemEditStr;
		return TRUE;
	} else
		return FALSE;

#else
	return TRUE;
#endif
}

void Editor::DisplayObject()
{
	ShowEditor(FALSE);
}

void Editor::ShowEditor(int CurLineOnly)
{
	if (Locked() || !TopList)
		return;

	Edit *CurPtr;
	int LeftPos, CurPos, Y;

	//	_SVS(SysLog(L"Enter to ShowEditor, CurLineOnly=%i",CurLineOnly));
	/*
		$ 10.08.2000 skv
		To make sure that CurEditor is set to required value.
	*/
	if (!Flags.Check(FEDITOR_DIALOGMEMOEDIT))
		CtrlObject->Plugins.CurEditor = HostFileEditor;		// this;

	if (NumLastLine > (Y2 - Y1) + 1)
		XX2 = X2 - (EdOpt.ShowScrollBar ? 1 : 0);
	else
		XX2 = X2;

	/*
		17.04.2002 skv
		Что б курсор не бегал при Alt-F9 в конце длинного файла.
		Если на экране есть свободное место, и есть текст сверху,
		перепозиционируем.
	*/

	if (!EdOpt.AllowEmptySpaceAfterEof) {
		while (CalcDistance(TopScreen, nullptr, Y2 - Y1) < Y2 - Y1) {
			if (TopScreen->m_prev)
				TopScreen = TopScreen->m_prev;
			else
				break;
		}
	}

	/*
		если курсор удруг оказался "за экраном",
		подвинем экран под курсор, а не
		курсор загоним в экран.
	*/

	while (CalcDistance(TopScreen, CurLine, -1) >= Y2 - Y1 + 1) {
		TopScreen = TopScreen->m_next;
		// DisableOut=TRUE;
		// ProcessKey(KEY_UP);
		// DisableOut=FALSE;
	}

	CurPos = CurLine->GetCellCurPos();

	if (!EdOpt.CursorBeyondEOL) {
		MaxRightPos = CurPos;
		int RealCurPos = CurLine->GetCurPos();
		int Length = CurLine->GetLength();

		if (RealCurPos > Length) {
			CurLine->SetCurPos(Length);
			CurLine->SetLeftPos(0);
			CurPos = CurLine->GetCellCurPos();
		}
	}

	if (!Pasting) {
		/*
			$ 10.08.2000 skv
			Don't send EE_REDRAW while macro is being executed.
			Send EE_REDRAW with param=2 if text was just modified.
		*/

		_SYS_EE_REDRAW(CleverSysLog Clev(L"Editor::ShowEditor()"));

		if (!ScrBuf.GetLockCount()) {
			/*
				$ 13.09.2000 skv
				EE_REDRAW 1 and 2 replaced.
			*/
			if (Flags.Check(FEDITOR_JUSTMODIFIED)) {
				Flags.Clear(FEDITOR_JUSTMODIFIED);

				if (!Flags.Check(FEDITOR_DIALOGMEMOEDIT)) {
					_SYS_EE_REDRAW(SysLog(L"Call ProcessEditorEvent(EE_REDRAW,EEREDRAW_CHANGE)"));
					CtrlObject->Plugins.ProcessEditorEvent(EE_REDRAW, EEREDRAW_CHANGE);
				}
			} else {
				if (!Flags.Check(FEDITOR_DIALOGMEMOEDIT)) {
					_SYS_EE_REDRAW(SysLog(L"Call ProcessEditorEvent(EE_REDRAW,%ls)",
							(CurLineOnly ? "EEREDRAW_LINE" : "EEREDRAW_ALL")));
					CtrlObject->Plugins.ProcessEditorEvent(EE_REDRAW,
							CurLineOnly ? EEREDRAW_LINE : EEREDRAW_ALL);
				}
			}
		}
		_SYS_EE_REDRAW(else SysLog(L"ScrBuf Locked !!!"));
	}

	DrawScrollbar();

	if (!CurLineOnly) {
		LeftPos = CurLine->GetLeftPos();
#if 0

		// крайне эксперементальный кусок!
		if (CurPos+LeftPos < XX2)
			LeftPos=0;
		else if (CurLine->X2 < XX2)
			LeftPos=CurLine->GetLength()-CurPos;

		if (LeftPos < 0)
			LeftPos=0;

#endif

		for (CurPtr = TopScreen, Y = Y1; Y <= Y2; Y++)
			if (CurPtr) {
				CurPtr->SetEditBeyondEnd(TRUE);
				CurPtr->SetPosition(X1, Y, XX2, Y);
				// CurPtr->SetTables(UseDecodeTable ? &TableSet:nullptr);
				//_D(SysLog(L"Setleftpos 3 to %i",LeftPos));
				CurPtr->SetLeftPos(LeftPos);
				if (CurPtr != CurLine) {
					CurPtr->SetCellCurPos(CurPos);
					CurPtr->FastShow();
				}
				CurPtr->SetEditBeyondEnd(EdOpt.CursorBeyondEOL);
				CurPtr = CurPtr->m_next;
			} else {
				SetScreen(X1, Y, XX2, Y, L' ', FarColorToReal(COL_EDITORTEXT));		// Пустые строки после конца текста
			}
	}

	CurLine->SetOvertypeMode(Flags.Check(FEDITOR_OVERTYPE));
	CurLine->Show();

	if (VBlockStart && VBlockSizeX > 0 && VBlockSizeY > 0) {
		int CurScreenLine = NumLine - CalcDistance(TopScreen, CurLine, -1);
		LeftPos = CurLine->GetLeftPos();

		for (CurPtr = TopScreen, Y = Y1; Y <= Y2; Y++) {
			if (CurPtr) {
				if (CurScreenLine >= VBlockY && CurScreenLine < VBlockY + VBlockSizeY) {
					int BlockX1 = VBlockX - LeftPos + X1;
					int BlockX2 = VBlockX + VBlockSizeX - 1 - LeftPos + X1;

					if (BlockX1 < X1)
						BlockX1 = X1;

					if (BlockX2 > XX2)
						BlockX2 = XX2;

					if (BlockX1 <= XX2 && BlockX2 >= X1)
						ChangeBlockColor(BlockX1, Y, BlockX2, Y, FarColorToReal(COL_EDITORSELECTEDTEXT));
				}

				CurPtr = CurPtr->m_next;
				CurScreenLine++;
			}
		}
	}

	if (HostFileEditor)
		HostFileEditor->ShowStatus();

	//_SVS(SysLog(L"Exit from ShowEditor"));
}

/*
	$ 10.08.2000 skv
	Wrapper for Modified.
	Set JustModified every call to 1
	to track any text state change.
	Even if state==0, this can be
	last UNDO.
*/
void Editor::TextChanged(int State)
{
	Flags.Change(FEDITOR_MODIFIED, State);
	Flags.Set(FEDITOR_JUSTMODIFIED);
}

bool Editor::CheckLine(Edit *line)
{
	if (line) {
		Edit *eLine;

		for (eLine = TopList; eLine; eLine = eLine->m_next) {
			if (eLine == line)
				return true;
		}
	}

	return false;
}

int Editor::BlockStart2NumLine(int *Pos)
{
	if (BlockStart || VBlockStart) {
		Edit *eBlock = VBlockStart ? VBlockStart : BlockStart;

		if (Pos) {
			if (VBlockStart)
				*Pos = eBlock->RealPosToCell(eBlock->CellPosToReal(VBlockX));
			else
				*Pos = eBlock->RealPosToCell(eBlock->SelStart);
		}

		return CalcDistance(TopList, eBlock, -1);
	}

	return -1;
}

int Editor::BlockEnd2NumLine(int *Pos)
{
	int iLine = -1, iPos = -1;
	Edit *eBlock = VBlockStart ? VBlockStart : BlockStart;

	if (eBlock) {
		int StartSel, EndSel;
		Edit *eLine = eBlock;
		iLine = BlockStart2NumLine(nullptr);	// получили строку начала блока

		if (VBlockStart) {
			for (int Line = VBlockSizeY; eLine && Line > 0; Line--, eLine = eLine->m_next) {
				iPos = eLine->RealPosToCell(eLine->CellPosToReal(VBlockX + VBlockSizeX));
				iLine++;
			}

			iLine--;
		} else {
			while (eLine)		// поиск строки, содержащую конец блока
			{
				eLine->GetSelection(StartSel, EndSel);

				if (EndSel == -1)	// это значит, что конец блока "за строкой"
					eLine->GetRealSelection(StartSel, EndSel);

				if (StartSel == -1) {
					// Если в текущей строки нет выделения, это еще не значит что мы в конце. Это может быть только начало :)
					if (eLine->m_next) {
						eLine->m_next->GetSelection(StartSel, EndSel);

						if (EndSel == -1)	// это значит, что конец блока "за строкой"
							eLine->m_next->GetRealSelection(StartSel, EndSel);

						if (StartSel == -1) {
							break;
						}
					} else
						break;
				} else {
					iPos = eLine->RealPosToCell(EndSel);
					iLine++;
				}

				eLine = eLine->m_next;
			}

			iLine--;
		}
	}

	if (Pos)
		*Pos = iPos;

	return iLine;
}

int64_t Editor::VMProcess(MacroOpcode OpCode, void *vParam, int64_t iParam)
{
	const int CurPos = CurLine->GetCurPos();

	switch (OpCode) {
		case MCODE_C_EMPTY:
			return (int64_t)(!CurLine->m_next && !CurLine->m_prev);		//??
		case MCODE_C_EOF:
			return (int64_t)(!CurLine->m_next && CurPos >= CurLine->GetLength());
		case MCODE_C_BOF:
			return (int64_t)(!CurLine->m_prev && !CurPos);
		case MCODE_C_SELECTED:
			return (int64_t)(BlockStart || VBlockStart ? TRUE : FALSE);
		case MCODE_V_EDITORCURPOS:
			return (int64_t)(CurLine->GetCellCurPos() + 1);
		case MCODE_V_EDITORREALPOS:
			return (int64_t)(CurLine->GetCurPos() + 1);
		case MCODE_V_EDITORCURLINE:
			return (int64_t)(NumLine + 1);
		case MCODE_V_ITEMCOUNT:
		case MCODE_V_EDITORLINES:
			return (int64_t)NumLastLine;
			// работа со стековыми закладками
		case MCODE_F_BM_ADD:
			return AddStackBookmark();
		case MCODE_F_BM_CLEAR:
			return ClearStackBookmarks();
		case MCODE_F_BM_NEXT:
			return NextStackBookmark();
		case MCODE_F_BM_PREV:
			return PrevStackBookmark();
		case MCODE_F_BM_BACK:
			return BackStackBookmark();
		case MCODE_F_BM_STAT: {
			switch (iParam) {
				case 0:		// BM.Stat(0) возвращает количество
					return GetStackBookmarks(nullptr);
				case 1:		// индекс текущей закладки (0 если закладок нет)
					return CurrentStackBookmarkIdx() + 1;
			}
			return 0;
		}
		case MCODE_F_BM_PUSH:	// N=BM.push() - сохранить текущую позицию в виде закладки в конце стека
			return PushStackBookMark();
		case MCODE_F_BM_POP:	// N=BM.pop() - восстановить текущую позицию из закладки в конце стека и удалить закладку
			return PopStackBookMark();
		case MCODE_F_BM_GOTO:	// N=BM.goto([n]) - переход на закладку с указанным индексом (0 --> текущую)
			return GotoStackBookmark((int)iParam - 1);
		case MCODE_F_BM_GET:	// N=BM.Get(Idx,M) - возвращает координаты строки (M==0) или колонки (M==1) закладки с индексом (Idx=1...)
		{
			int64_t Ret = -1;
			long Val[1];
			EditorBookMarks ebm = {0};
			auto iMode = (LONG_PTR)vParam;

			switch (iMode) {
				case 0:
					ebm.Line = Val;
					break;
				case 1:
					ebm.Cursor = Val;
					break;
				case 2:
					ebm.LeftPos = Val;
					break;
				case 3:
					ebm.ScreenLine = Val;
					break;
				default:
					return Ret;
			}

			if (GetStackBookmark((int)iParam - 1, &ebm))
				Ret = (int64_t)((DWORD)Val[0] + 1);

			return Ret;
		}
		case MCODE_F_BM_DEL:
			// N=BM.Del(Idx) - удаляет закладку с указанным индексом (x=1...), 0 - удаляет текущую закладку
			return DeleteStackBookmark(PointerToStackBookmark((int)iParam - 1));

		case MCODE_F_EDITOR_SEL: {
			int iLine;
			int iPos;
			int Action = (int)((INT_PTR)vParam);

			switch (Action) {
				case 0:		// Get Param
				{
					switch (iParam) {
						case 0:		// return FirstLine
						{
							return BlockStart2NumLine(nullptr) + 1;
						}
						case 1:		// return FirstPos
						{
							if (BlockStart2NumLine(&iPos) != -1)
								return iPos + 1;

							return 0;
						}
						case 2:		// return LastLine
						{
							return BlockEnd2NumLine(nullptr) + 1;
						}
						case 3:		// return LastPos
						{
							if (BlockEnd2NumLine(&iPos) != -1)
								return iPos;

							return 0;
						}
						case 4:		// return block type (0=nothing 1=stream, 2=column)
						{
							return VBlockStart ? 2 : (BlockStart ? 1 : 0);
						}
					}

					break;
				}
				case 1:		// Set Pos
				{
					switch (iParam) {
						case 0:		// begin block (FirstLine & FirstPos)
						case 1:		// end block (LastLine & LastPos)
						{
							if (!iParam)
								iLine = BlockStart2NumLine(&iPos);
							else
								iLine = BlockEnd2NumLine(&iPos);

							if (iLine > -1 && iPos > -1) {
								GoToLine(iLine);
								CurLine->SetCurPos(CurLine->CellPosToReal(iPos));
								return 1;
							}

							return 0;
						}
					}

					break;
				}
				case 2:		// Set Stream Selection Edge
				case 3:		// Set Column Selection Edge
				{
					switch (iParam) {
						case 0:		// selection start
						{
							MBlockStart = CurLine;
							MBlockStartX = CurLine->GetCurPos();
							return 1;
						}
						case 1:		// selection finish
						{
							int Ret = 0;

							if (CheckLine(MBlockStart)) {
								EditorSelect eSel;
								eSel.BlockType = (Action == 2) ? BTYPE_STREAM : BTYPE_COLUMN;
								eSel.BlockStartPos = MBlockStartX;
								eSel.BlockWidth = CurLine->GetCurPos() - MBlockStartX;

								if (eSel.BlockWidth || (Action == 2 && MBlockStart != CurLine)) {
									int bl = CalcDistance(TopList, MBlockStart, -1);
									int el = CalcDistance(TopList, CurLine, -1);

									if (bl > el) {
										eSel.BlockStartLine = el;
										eSel.BlockHeight = CalcDistance(CurLine, MBlockStart, -1) + 1;
									} else {
										eSel.BlockStartLine = bl;
										eSel.BlockHeight = CalcDistance(MBlockStart, CurLine, -1) + 1;
									}

									if (bl > el || (bl == el && eSel.BlockWidth < 0)) {
										eSel.BlockWidth*= -1;
										eSel.BlockStartPos = CurLine->GetCurPos();
									}

									Ret = EditorControl(ECTL_SELECT, &eSel);
								} else if (/*!eSel.BlockWidth &&*/ MBlockStart == CurLine) {
									UnmarkBlock();
								}
							}

							UnmarkMacroBlock();
							Show();
							return (int64_t)Ret;
						}
					}

					break;
				}
				case 4:		// UnMark sel block
				{
					bool NeedRedraw = BlockStart || VBlockStart;
					UnmarkBlock();
					UnmarkMacroBlock();

					if (NeedRedraw)
						Show();

					return 1;
				}
			}

			break;
		}
		case MCODE_V_EDITORSELVALUE:	// Editor.SelValue
		{
			FARString strText;
			wchar_t *Text;

			if (VBlockStart)
				Text = VBlock2Text(nullptr);
			else
				Text = Block2Text(nullptr);

			if (Text) {
				strText = Text;
				free(Text);
			}

			*(FARString *)vParam = strText;
			return 1;
		}
	}

	return 0;
}

void Editor::ProcessPasteEvent()
{
	if (Flags.Check(FEDITOR_LOCKMODE))
		return;

	Pasting++;
	if (!EdOpt.PersistentBlocks && !VBlockStart)
		DeleteBlock();

	Paste();
	// MarkingBlock=!VBlockStart;
	Flags.Change(FEDITOR_MARKINGBLOCK, !VBlockStart);
	Flags.Clear(FEDITOR_MARKINGVBLOCK);

	if (!EdOpt.PersistentBlocks)
		UnmarkBlock();

	Pasting--;
	Show();
}

int Editor::ProcessKey(FarKey Key)
{
	if (Key == KEY_IDLE) {
		if (Opt.ViewerEditorClock && HostFileEditor && HostFileEditor->IsFullScreen()
				&& Opt.EdOpt.ShowTitleBar)
			ShowTime(FALSE);

		return TRUE;
	}

	if (Key == KEY_NONE)
		return TRUE;

	_KEYMACRO(CleverSysLog SL(L"Editor::ProcessKey()"));
	_KEYMACRO(SysLog(L"Key=%ls", _FARKEY_ToName(Key)));
	int CurPos, CurVisPos, I;
	CurPos = CurLine->GetCurPos();
	CurVisPos = GetLineCurPos();
	const bool isk = IsShiftKey(Key);
	_SVS(SysLog(L"[%d] isk=%d", __LINE__, isk));

	// if ((!isk || CtrlObject->Macro.IsExecuting()) && !isk && !Pasting)
	if (!isk && !Pasting
			&& !(((unsigned int)Key >= KEY_MACRO_BASE && (unsigned int)Key <= KEY_MACRO_ENDBASE)
					|| ((unsigned int)Key >= KEY_OP_BASE && (unsigned int)Key <= KEY_OP_ENDBASE))) {
		_SVS(SysLog(L"[%d] BlockStart=(%d,%d)", __LINE__, BlockStart, VBlockStart));

		if (BlockStart || VBlockStart) {
			Flags.Clear(FEDITOR_MARKINGVBLOCK | FEDITOR_MARKINGBLOCK);
		}

		if ((BlockStart || VBlockStart) && !EdOpt.PersistentBlocks)
		//		if (BlockStart || VBlockStart && !EdOpt.PersistentBlocks)
		{
			Flags.Clear(FEDITOR_MARKINGVBLOCK | FEDITOR_MARKINGBLOCK);

			if (!EdOpt.PersistentBlocks) {
				static FarKey UnmarkKeys[] = {
						KEY_LEFT,
						KEY_NUMPAD4,
						KEY_RIGHT,
						KEY_NUMPAD6,
						KEY_HOME,
						KEY_NUMPAD7,
						KEY_END,
						KEY_NUMPAD1,
						KEY_UP,
						KEY_NUMPAD8,
						KEY_DOWN,
						KEY_NUMPAD2,
						KEY_PGUP,
						KEY_NUMPAD9,
						KEY_PGDN,
						KEY_NUMPAD3,
						KEY_CTRLHOME,
						KEY_CTRLNUMPAD7,
						KEY_CTRLPGUP,
						KEY_CTRLNUMPAD9,
						KEY_CTRLEND,
						KEY_CTRLNUMPAD1,
						KEY_CTRLPGDN,
						KEY_CTRLNUMPAD3,
						KEY_CTRLLEFT,
						KEY_CTRLNUMPAD4,
						KEY_CTRLRIGHT,
						KEY_CTRLNUMPAD7,
						KEY_CTRLUP,
						KEY_CTRLNUMPAD8,
						KEY_CTRLDOWN,
						KEY_CTRLNUMPAD2,
						KEY_CTRLN,
						KEY_CTRLE,
						KEY_CTRLS,
				};

				for (size_t I = 0; I < ARRAYSIZE(UnmarkKeys); I++)
					if (Key == UnmarkKeys[I]) {
						UnmarkBlock();
						break;
					}
			} else {
				int StartSel, EndSel;
				//				Edit *BStart=!BlockStart?VBlockStart:BlockStart;
				//				BStart->GetRealSelection(StartSel,EndSel);
				BlockStart->GetRealSelection(StartSel, EndSel);
				_SVS(SysLog(L"[%d] PersistentBlocks! StartSel=%d, EndSel=%d", __LINE__, StartSel, EndSel));

				if (StartSel == -1 || StartSel == EndSel)
					UnmarkBlock();
			}
		}
	}

	if (Key == KEY_ALTD)
		Key = KEY_CTRLK;

	// работа с закладками
	if (Key >= KEY_CTRL0 && Key <= KEY_CTRL9)
		return GotoBookmark(Key - KEY_CTRL0);

	if (Key >= KEY_CTRLSHIFT0 && Key <= KEY_CTRLSHIFT9)
		Key = Key - KEY_CTRLSHIFT0 + KEY_RCTRL0;

	if (Key >= KEY_RCTRL0 && Key <= KEY_RCTRL9)
		return SetBookmark(Key - KEY_RCTRL0);

	int SelStart = 0, SelEnd = 0;
	int SelFirst = FALSE;
	int SelAtBeginning = FALSE;
	EditorBlockGuard _bg(*this, &Editor::UnmarkEmptyBlock);

	switch (Key) {
		case KEY_SHIFTLEFT:
		case KEY_SHIFTRIGHT:
		case KEY_SHIFTUP:
		case KEY_SHIFTDOWN:
		case KEY_SHIFTHOME:
		case KEY_SHIFTEND:
		case KEY_SHIFTNUMPAD4:
		case KEY_SHIFTNUMPAD6:
		case KEY_SHIFTNUMPAD8:
		case KEY_SHIFTNUMPAD2:
		case KEY_SHIFTNUMPAD7:
		case KEY_SHIFTNUMPAD1:
		case KEY_CTRLSHIFTLEFT:
		case KEY_CTRLSHIFTNUMPAD4:	/* 12.11.2002 DJ */
		{
			_KEYMACRO(CleverSysLog SL(L"Editor::ProcessKey(KEY_SHIFT*)"));
			_SVS(SysLog(L"[%d] SelStart=%d, SelEnd=%d", __LINE__, SelStart, SelEnd));
			UnmarkEmptyBlock();		// уберем выделение, если его размер равен 0
			_bg.SetNeedCheckUnmark(true);
			CurLine->GetRealSelection(SelStart, SelEnd);

			if (Flags.Check(FEDITOR_CURPOSCHANGEDBYPLUGIN)) {
				if (SelStart != -1
						&& (CurPos < SelStart ||					// если курсор до выделения
								(SelEnd != -1
										&& (CurPos > SelEnd ||		// ... после выделения
												(CurPos > SelStart && CurPos < SelEnd))))
						&& CurPos < CurLine->GetLength())			// ... внутри выдления
					Flags.Clear(FEDITOR_MARKINGVBLOCK | FEDITOR_MARKINGBLOCK);

				Flags.Clear(FEDITOR_CURPOSCHANGEDBYPLUGIN);
			}

			_SVS(SysLog(L"[%d] SelStart=%d, SelEnd=%d", __LINE__, SelStart, SelEnd));

			if (!Flags.Check(FEDITOR_MARKINGBLOCK)) {
				UnmarkBlock();
				Flags.Set(FEDITOR_MARKINGBLOCK);
				BlockStart = CurLine;
				BlockStartLine = NumLine;
				SelFirst = TRUE;
				SelStart = SelEnd = CurPos;
			} else {
				SelAtBeginning = CurLine == BlockStart && CurPos == SelStart;

				if (SelStart == -1) {
					SelStart = SelEnd = CurPos;
				}
			}

			_SVS(SysLog(L"[%d] SelStart=%d, SelEnd=%d", __LINE__, SelStart, SelEnd));
		}
	}

	switch (Key) {
		case KEY_CTRLSHIFTPGUP:
		case KEY_CTRLSHIFTNUMPAD9:
		case KEY_CTRLSHIFTHOME:
		case KEY_CTRLSHIFTNUMPAD7: {
			Lock();
			Pasting++;

			while (CurLine != TopList) {
				ProcessKey(KEY_SHIFTPGUP);
			}

			if (Key == KEY_CTRLSHIFTHOME || Key == KEY_CTRLSHIFTNUMPAD7)
				ProcessKey(KEY_SHIFTHOME);

			Pasting--;
			Unlock();
			Show();
			return TRUE;
		}
		case KEY_CTRLSHIFTPGDN:
		case KEY_CTRLSHIFTNUMPAD3:
		case KEY_CTRLSHIFTEND:
		case KEY_CTRLSHIFTNUMPAD1: {
			Lock();
			Pasting++;

			while (CurLine != EndList) {
				ProcessKey(KEY_SHIFTPGDN);
			}

			/*
				$ 06.02.2002 IS
				Принудительно сбросим флаг того, что позиция изменена плагином.
				Для чего:
					при выполнении "ProcessKey(KEY_SHIFTPGDN)" (см. чуть выше)
					позиция плагины (в моем случае - колорер) могут дергать
					ECTL_SETPOSITION, в результате чего выставляется флаг
					FEDITOR_CURPOSCHANGEDBYPLUGIN. А при обработке KEY_SHIFTEND
					выделение в подобном случае начинается с нуля, что сводит на нет
					предыдущее выполнение KEY_SHIFTPGDN.
			*/
			Flags.Clear(FEDITOR_CURPOSCHANGEDBYPLUGIN);

			if (Key == KEY_CTRLSHIFTEND || Key == KEY_CTRLSHIFTNUMPAD1)
				ProcessKey(KEY_SHIFTEND);

			Pasting--;
			Unlock();
			Show();
			return TRUE;
		}
		case KEY_SHIFTPGUP:
		case KEY_SHIFTNUMPAD9: {
			Pasting++;
			Lock();

			for (I = Y1; I < Y2; I++) {
				ProcessKey(KEY_SHIFTUP);

				if (!EdOpt.CursorBeyondEOL) {
					if (CurLine->GetCurPos() > CurLine->GetLength()) {
						CurLine->SetCurPos(CurLine->GetLength());
					}
				}
			}

			Pasting--;
			Unlock();
			Show();
			return TRUE;
		}
		case KEY_SHIFTPGDN:
		case KEY_SHIFTNUMPAD3: {
			Pasting++;
			Lock();

			for (I = Y1; I < Y2; I++) {
				ProcessKey(KEY_SHIFTDOWN);

				if (!EdOpt.CursorBeyondEOL) {
					if (CurLine->GetCurPos() > CurLine->GetLength()) {
						CurLine->SetCurPos(CurLine->GetLength());
					}
				}
			}

			Pasting--;
			Unlock();
			Show();
			return TRUE;
		}
		case KEY_SHIFTHOME:
		case KEY_SHIFTNUMPAD7: {
			Pasting++;
			Lock();

			if (SelAtBeginning) {
				CurLine->Select(0, SelEnd);
			} else {
				if (!SelStart) {
					CurLine->Select(-1, 0);
				} else {
					CurLine->Select(0, SelStart);
				}
			}

			ProcessKey(KEY_HOME);
			Pasting--;
			Unlock();
			Show();
			return TRUE;
		}
		case KEY_SHIFTEND:
		case KEY_SHIFTNUMPAD1: {
			{
				int LeftPos = CurLine->GetLeftPos();
				Pasting++;
				Lock();
				int CurLength = CurLine->GetLength();

				if (!SelAtBeginning || SelFirst) {
					CurLine->Select(SelStart, CurLength);
				} else {
					if (SelEnd != -1)
						CurLine->Select(SelEnd, CurLength);
					else
						CurLine->Select(CurLength, -1);
				}

				CurLine->ObjWidth = XX2 - X1;
				ProcessKey(KEY_END);
				Pasting--;
				Unlock();

				if (EdOpt.PersistentBlocks)
					Show();
				else {
					CurLine->FastShow();
					ShowEditor(LeftPos == CurLine->GetLeftPos());
				}
			}
			return TRUE;
		}
		case KEY_SHIFTLEFT:
		case KEY_SHIFTNUMPAD4: {
			_SVS(CleverSysLog SL(L"case KEY_SHIFTLEFT"));

			if (!CurPos && !CurLine->m_prev)
				return TRUE;

			if (!CurPos)				// курсор в начале строки
			{
				if (SelAtBeginning)		// курсор в начале блока
				{
					BlockStart = CurLine->m_prev;
					CurLine->m_prev->Select(CurLine->m_prev->GetLength(), -1);
				} else		// курсор в конце блока
				{
					CurLine->Select(-1, 0);
					CurLine->m_prev->GetRealSelection(SelStart, SelEnd);
					CurLine->m_prev->Select(SelStart, CurLine->m_prev->GetLength());
				}
			} else {
				if (SelAtBeginning || SelFirst) {
					CurLine->Select(CurLine->CalcPosBwdTo(SelStart), SelEnd);
				} else {
					CurLine->Select(SelStart, CurLine->CalcPosBwdTo(SelEnd));
				}
			}

			int LeftPos = CurLine->GetLeftPos();
			Edit *OldCur = CurLine;
			int _OldNumLine = NumLine;
			Pasting++;
			ProcessKey(KEY_LEFT);
			Pasting--;

			if (_OldNumLine != NumLine) {
				BlockStartLine = NumLine;
			}

			ShowEditor(OldCur == CurLine && LeftPos == CurLine->GetLeftPos());
			return TRUE;
		}
		case KEY_SHIFTRIGHT:
		case KEY_SHIFTNUMPAD6: {
			_SVS(CleverSysLog SL(L"case KEY_SHIFTRIGHT"));

			if (!CurLine->m_next && CurPos == CurLine->GetLength() && !EdOpt.CursorBeyondEOL) {
				return TRUE;
			}

			if (SelAtBeginning) {
				CurLine->Select(CurLine->CalcPosFwdTo(SelStart), SelEnd);
			} else {
				CurLine->Select(SelStart, CurLine->CalcPosFwdTo(SelEnd));
			}

			Edit *OldCur = CurLine;
			int OldLeft = CurLine->GetLeftPos();
			Pasting++;
			ProcessKey(KEY_RIGHT);
			Pasting--;

			if (OldCur != CurLine) {
				if (SelAtBeginning) {
					OldCur->Select(-1, 0);
					BlockStart = CurLine;
					BlockStartLine = NumLine;
				} else {
					OldCur->Select(SelStart, -1);
				}
			}

			ShowEditor(OldCur == CurLine && OldLeft == CurLine->GetLeftPos());
			return TRUE;
		}
		case KEY_CTRLSHIFTLEFT:
		case KEY_CTRLSHIFTNUMPAD4: {
			_SVS(CleverSysLog SL(L"case KEY_CTRLSHIFTLEFT"));
			_SVS(SysLog(L"[%d] Pasting=%d, SelEnd=%d", __LINE__, Pasting, SelEnd));
			{
				int SkipSpace = TRUE;
				Pasting++;
				Lock();
				int CurPos;

				for (;;) {
					const wchar_t *Str;
					int Length;
					CurLine->GetBinaryString(&Str, nullptr, Length);
					/*
						$ 12.11.2002 DJ
						обеспечим корректную работу Ctrl-Shift-Left за концом строки
					*/
					CurPos = CurLine->GetCurPos();

					if (CurPos > Length) {
						int SelStartPos = CurPos;
						CurLine->ProcessKey(KEY_END);
						CurPos = CurLine->GetCurPos();

						if (CurLine->SelStart >= 0) {
							if (!SelAtBeginning)
								CurLine->Select(CurLine->SelStart, CurPos);
							else
								CurLine->Select(CurPos, CurLine->SelEnd);
						} else
							CurLine->Select(CurPos, SelStartPos);
					}

					if (!CurPos)
						break;

					if (IsSpace(Str[CurPos - 1]) || IsWordDiv(EdOpt.strWordDiv, Str[CurPos - 1])) {
						if (SkipSpace) {
							ProcessKey(KEY_SHIFTLEFT);
							continue;
						} else
							break;
					}

					SkipSpace = FALSE;
					ProcessKey(KEY_SHIFTLEFT);
				}

				Pasting--;
				Unlock();
				Show();
			}
			return TRUE;
		}
		case KEY_CTRLSHIFTRIGHT:
		case KEY_CTRLSHIFTNUMPAD6: {
			_SVS(CleverSysLog SL(L"case KEY_CTRLSHIFTRIGHT"));
			_SVS(SysLog(L"[%d] Pasting=%d, SelEnd=%d", __LINE__, Pasting, SelEnd));
			{
				int SkipSpace = TRUE;
				Pasting++;
				Lock();
				int CurPos;

				for (;;) {
					const wchar_t *Str;
					int Length;
					CurLine->GetBinaryString(&Str, nullptr, Length);
					CurPos = CurLine->GetCurPos();

					if (CurPos >= Length)
						break;

					if (IsSpace(Str[CurPos]) || IsWordDiv(EdOpt.strWordDiv, Str[CurPos])) {
						if (SkipSpace) {
							ProcessKey(KEY_SHIFTRIGHT);
							continue;
						} else
							break;
					}

					SkipSpace = FALSE;
					ProcessKey(KEY_SHIFTRIGHT);
				}

				Pasting--;
				Unlock();
				Show();
			}
			return TRUE;
		}
		case KEY_SHIFTDOWN:
		case KEY_SHIFTNUMPAD2: {
			if (!CurLine->m_next)
				return TRUE;

			CurPos = CurLine->RealPosToCell(CurPos);

			if (SelAtBeginning)		// Снимаем выделение
			{
				if (SelEnd == -1) {
					CurLine->Select(-1, 0);
					BlockStart = CurLine->m_next;
					BlockStartLine = NumLine + 1;
				} else {
					CurLine->Select(SelEnd, -1);
				}

				CurLine->m_next->GetRealSelection(SelStart, SelEnd);

				if (SelStart != -1)
					SelStart = CurLine->m_next->RealPosToCell(SelStart);

				if (SelEnd != -1)
					SelEnd = CurLine->m_next->RealPosToCell(SelEnd);

				if (SelStart == -1) {
					SelStart = 0;
					SelEnd = CurPos;
				} else {
					if (SelEnd != -1 && SelEnd < CurPos) {
						SelStart = SelEnd;
						SelEnd = CurPos;
					} else {
						SelStart = CurPos;
					}
				}

				if (SelStart != -1)
					SelStart = CurLine->m_next->CellPosToReal(SelStart);

				if (SelEnd != -1)
					SelEnd = CurLine->m_next->CellPosToReal(SelEnd);

				/*
				if(!EdOpt.CursorBeyondEOL && SelEnd>CurLine->m_next->GetLength())
				{
					SelEnd=CurLine->m_next->GetLength();
				}
				if(!EdOpt.CursorBeyondEOL && SelStart>CurLine->m_next->GetLength())
				{
					SelStart=CurLine->m_next->GetLength();
				}
				*/
			} else		// расширяем выделение
			{
				CurLine->Select(SelStart, -1);
				SelStart = CurLine->m_next->CellPosToReal(0);
				SelEnd = CurLine->m_next->CellPosToReal(CurPos);
			}

			if (!EdOpt.CursorBeyondEOL && SelEnd > CurLine->m_next->GetLength()) {
				SelEnd = CurLine->m_next->GetLength();
			}

			if (!EdOpt.CursorBeyondEOL && SelStart > CurLine->m_next->GetLength()) {
				SelStart = CurLine->m_next->GetLength();
			}

			//			if(!SelStart && !SelEnd)
			//				CurLine->m_next->Select(-1,0);
			//			else
			CurLine->m_next->Select(SelStart, SelEnd);
			Down();
			Show();
			return TRUE;
		}
		case KEY_SHIFTUP:
		case KEY_SHIFTNUMPAD8: {
			if (!CurLine->m_prev)
				return 0;

			if (SelAtBeginning || SelFirst)		// расширяем выделение
			{
				CurLine->Select(0, SelEnd);
				SelStart = CurLine->RealPosToCell(CurPos);

				if (!EdOpt.CursorBeyondEOL
						&& CurLine->m_prev->CellPosToReal(SelStart) > CurLine->m_prev->GetLength()) {
					SelStart = CurLine->m_prev->RealPosToCell(CurLine->m_prev->GetLength());
				}

				SelStart = CurLine->m_prev->CellPosToReal(SelStart);
				CurLine->m_prev->Select(SelStart, -1);
				BlockStart = CurLine->m_prev;
				BlockStartLine = NumLine - 1;
			} else		// снимаем выделение
			{
				CurPos = CurLine->RealPosToCell(CurPos);

				if (!SelStart) {
					CurLine->Select(-1, 0);
				} else {
					CurLine->Select(0, SelStart);
				}

				CurLine->m_prev->GetRealSelection(SelStart, SelEnd);

				if (SelStart != -1)
					SelStart = CurLine->m_prev->RealPosToCell(SelStart);

				if (SelEnd != -1)
					SelEnd = CurLine->m_prev->RealPosToCell(SelEnd);

				if (SelStart == -1) {
					BlockStart = CurLine->m_prev;
					BlockStartLine = NumLine - 1;
					SelStart = CurLine->m_prev->CellPosToReal(CurPos);
					SelEnd = -1;
				} else {
					if (CurPos < SelStart) {
						SelEnd = SelStart;
						SelStart = CurPos;
					} else {
						SelEnd = CurPos;
					}

					SelStart = CurLine->m_prev->CellPosToReal(SelStart);
					SelEnd = CurLine->m_prev->CellPosToReal(SelEnd);

					if (!EdOpt.CursorBeyondEOL && SelEnd > CurLine->m_prev->GetLength()) {
						SelEnd = CurLine->m_prev->GetLength();
					}

					if (!EdOpt.CursorBeyondEOL && SelStart > CurLine->m_prev->GetLength()) {
						SelStart = CurLine->m_prev->GetLength();
					}
				}

				CurLine->m_prev->Select(SelStart, SelEnd);
			}

			Up();
			Show();
			return TRUE;
		}
		case KEY_CTRLADD: {
			Copy(TRUE);
			return TRUE;
		}
		case KEY_CTRLA: {
			UnmarkBlock();
			SelectAll();
			return TRUE;
		}
		case KEY_CTRLU: {
			UnmarkMacroBlock();
			UnmarkBlock();
			return TRUE;
		}
		case KEY_CTRLC:
		case KEY_CTRLINS:
		case KEY_CTRLNUMPAD0: {
			if (/*!EdOpt.PersistentBlocks && */ !BlockStart && !VBlockStart) {
				BlockStart = CurLine;
				BlockStartLine = NumLine;
				CurLine->AddSelect(0, -1);
				Show();
			}

			Copy(FALSE);
			return TRUE;
		}
		case KEY_CTRLP:
		case KEY_CTRLM: {
			if (Flags.Check(FEDITOR_LOCKMODE))
				return TRUE;

			if (BlockStart || VBlockStart) {
				int SelStart, SelEnd;
				CurLine->GetSelection(SelStart, SelEnd);
				Pasting++;
				bool OldUseInternalClipboard = Clipboard::SetUseInternalClipboardState(true);
				ProcessKey(Key == KEY_CTRLP ? KEY_CTRLINS : KEY_SHIFTDEL);

				/*
					$ 10.04.2001 SVS
					^P/^M - некорректно работали: уловие для CurPos должно быть ">=",
					а не "меньше".
				*/
				if (Key == KEY_CTRLM && SelStart != -1 && SelEnd != -1) {
					if (CurPos >= SelEnd)
						CurLine->SetCurPos(CurPos - (SelEnd - SelStart));
					else
						CurLine->SetCurPos(CurPos);
				}

				ProcessKey(KEY_SHIFTINS);
				Pasting--;
				EmptyInternalClipboard();
				Clipboard::SetUseInternalClipboardState(OldUseInternalClipboard);
				/*
					$ 08.02.2001 SKV
					всё делалось с pasting'ом, поэтому redraw плагинам не ушел.
					сделаем его.
				*/
				Show();
			}

			return TRUE;
		}
		case KEY_CTRLX:
		case KEY_SHIFTDEL:
		case KEY_SHIFTNUMDEL:
		case KEY_SHIFTDECIMAL: {
			Copy(FALSE);
			[[fallthrough]];
		}
		case KEY_CTRLD: {
			if (Flags.Check(FEDITOR_LOCKMODE))
				return TRUE;

			Flags.Clear(FEDITOR_MARKINGVBLOCK | FEDITOR_MARKINGBLOCK);
			DeleteBlock();
			Show();
			return TRUE;
		}
		case KEY_CTRLV:
		case KEY_SHIFTINS:
		case KEY_SHIFTNUMPAD0: {
			ProcessPasteEvent();
			return TRUE;
		}
		case KEY_LEFT:
		case KEY_NUMPAD4: {
			Flags.Set(FEDITOR_NEWUNDO);

			if (!CurPos && CurLine->m_prev) {
				Up();
				Show();
				CurLine->ProcessKey(KEY_END);
				Show();
			} else {
				int LeftPos = CurLine->GetLeftPos();
				CurLine->ProcessKey(KEY_LEFT);
				ShowEditor(LeftPos == CurLine->GetLeftPos());
			}

			return TRUE;
		}
		case KEY_INS:
		case KEY_NUMPAD0: {
			Flags.Swap(FEDITOR_OVERTYPE);
			Show();
			return TRUE;
		}
		case KEY_NUMDEL:
		case KEY_DEL: {
			if (!Flags.Check(FEDITOR_LOCKMODE)) {
				// Del в самой последней позиции ничего не удаляет, поэтому не модифицируем...
				if (!CurLine->m_next && CurPos >= CurLine->GetLength() && !BlockStart && !VBlockStart)
					return TRUE;

				/*
					$ 07.03.2002 IS
					Снимем выделение, если блок все равно пустой
				*/
				if (!Pasting)
					UnmarkEmptyBlock();

				if (!Pasting && EdOpt.DelRemovesBlocks && (BlockStart || VBlockStart))
					DeleteBlock();
				else {
					if (CurPos >= CurLine->GetLength()) {
						AddUndoData(UNDO_BEGIN);
						AddUndoData(UNDO_EDIT, CurLine->GetStringAddr(), CurLine->GetEOL(), NumLine,
								CurLine->GetCurPos(), CurLine->GetLength());

						if (!CurLine->m_next)
							CurLine->SetEOL(L"");
						else {
							int SelStart, SelEnd, NextSelStart, NextSelEnd;
							int Length = CurLine->GetLength();
							CurLine->GetSelection(SelStart, SelEnd);
							CurLine->m_next->GetSelection(NextSelStart, NextSelEnd);
							const wchar_t *Str;
							int NextLength;
							CurLine->m_next->GetBinaryString(&Str, nullptr, NextLength);
							CurLine->InsertBinaryString(Str, NextLength);
							CurLine->SetEOL(CurLine->m_next->GetEOL());
							CurLine->SetCurPos(CurPos);
							DeleteString(CurLine->m_next, NumLine + 1, TRUE, NumLine + 1);

							if (!NextLength)
								CurLine->SetEOL(L"");

							if (NextSelStart != -1) {
								if (SelStart == -1) {
									CurLine->Select(Length + NextSelStart,
											NextSelEnd == -1 ? -1 : Length + NextSelEnd);
									BlockStart = CurLine;
									BlockStartLine = NumLine;
								} else
									CurLine->Select(SelStart, NextSelEnd == -1 ? -1 : Length + NextSelEnd);
							}
						}

						AddUndoData(UNDO_END);
					} else {
						AddUndoData(UNDO_EDIT, CurLine->GetStringAddr(), CurLine->GetEOL(), NumLine,
								CurLine->GetCurPos(), CurLine->GetLength());
						CurLine->ProcessKey(KEY_DEL);
					}

					TextChanged(1);
				}

				Show();
			}

			return TRUE;
		}
		case KEY_BS: {
			if (!Flags.Check(FEDITOR_LOCKMODE)) {
				// Bs в самом начале нихрена ничего не удаляет, посему не будем выставлять
				if (!CurLine->m_prev && !CurPos && !BlockStart && !VBlockStart)
					return TRUE;

				TextChanged(1);
				int IsDelBlock = FALSE;

				if (EdOpt.BSLikeDel) {
					if (!Pasting && EdOpt.DelRemovesBlocks
							&& (BlockStart || (VBlockStart && /* #279 */ VBlockSizeX > 0 && VBlockSizeY > 0)))
						IsDelBlock = TRUE;
				} else {
					if (!Pasting && !EdOpt.PersistentBlocks && BlockStart)
						IsDelBlock = TRUE;
				}

				if (IsDelBlock)
					DeleteBlock();
				else if (!CurPos && CurLine->m_prev) {
					Pasting++;
					Up();
					CurLine->ProcessKey(KEY_CTRLEND);
					ProcessKey(KEY_DEL);
					Pasting--;
				} else {
					AddUndoData(UNDO_EDIT, CurLine->GetStringAddr(), CurLine->GetEOL(), NumLine,
							CurLine->GetCurPos(), CurLine->GetLength());
					CurLine->ProcessKey(KEY_BS);
				}

				Show();
			}

			return TRUE;
		}
		case KEY_CTRLBS: {
			if (!Flags.Check(FEDITOR_LOCKMODE)) {
				TextChanged(1);

				if (!Pasting && !EdOpt.PersistentBlocks && BlockStart)
					DeleteBlock();
				else if (!CurPos && CurLine->m_prev)
					ProcessKey(KEY_BS);
				else {
					AddUndoData(UNDO_EDIT, CurLine->GetStringAddr(), CurLine->GetEOL(), NumLine,
							CurLine->GetCurPos(), CurLine->GetLength());
					CurLine->ProcessKey(KEY_CTRLBS);
				}

				Show();
			}

			return TRUE;
		}
		case KEY_UP:
		case KEY_NUMPAD8: {
			{
				Flags.Set(FEDITOR_NEWUNDO);
				int PrevMaxPos = MaxRightPos;
				Edit *LastTopScreen = TopScreen;
				Up();

				if (TopScreen == LastTopScreen)
					ShowEditor(TRUE);
				else
					Show();

				if (PrevMaxPos > CurLine->GetCellCurPos()) {
					CurLine->SetCellCurPos(PrevMaxPos);
					CurLine->FastShow();
					CurLine->SetCellCurPos(PrevMaxPos);
					Show();
				}
			}
			return TRUE;
		}
		case KEY_DOWN:
		case KEY_NUMPAD2: {
			{
				Flags.Set(FEDITOR_NEWUNDO);
				int PrevMaxPos = MaxRightPos;
				Edit *LastTopScreen = TopScreen;
				Down();

				if (TopScreen == LastTopScreen)
					ShowEditor(TRUE);
				else
					Show();

				if (PrevMaxPos > CurLine->GetCellCurPos()) {
					CurLine->SetCellCurPos(PrevMaxPos);
					CurLine->FastShow();
					CurLine->SetCellCurPos(PrevMaxPos);
					Show();
				}
			}
			return TRUE;
		}
		case KEY_MSWHEEL_UP:
		case (KEY_MSWHEEL_UP | KEY_ALT): {
			int Roll = Key & KEY_ALT ? 1 : Opt.MsWheelDeltaEdit;

			for (int i = 0; i < Roll; i++)
				ProcessKey(KEY_CTRLUP);

			return TRUE;
		}
		case KEY_MSWHEEL_DOWN:
		case (KEY_MSWHEEL_DOWN | KEY_ALT): {
			int Roll = Key & KEY_ALT ? 1 : Opt.MsWheelDeltaEdit;

			for (int i = 0; i < Roll; i++)
				ProcessKey(KEY_CTRLDOWN);

			return TRUE;
		}
		case KEY_MSWHEEL_LEFT:
		case (KEY_MSWHEEL_LEFT | KEY_ALT): {
			int Roll = Key & KEY_ALT ? 1 : Opt.MsHWheelDeltaEdit;

			for (int i = 0; i < Roll; i++)
				ProcessKey(KEY_LEFT);

			return TRUE;
		}
		case KEY_MSWHEEL_RIGHT:
		case (KEY_MSWHEEL_RIGHT | KEY_ALT): {
			int Roll = Key & KEY_ALT ? 1 : Opt.MsHWheelDeltaEdit;

			for (int i = 0; i < Roll; i++)
				ProcessKey(KEY_RIGHT);

			return TRUE;
		}
		case KEY_CTRLUP:
		case KEY_CTRLNUMPAD8: {
			Flags.Set(FEDITOR_NEWUNDO);
			ScrollUp();
			Show();
			return TRUE;
		}
		case KEY_CTRLDOWN:
		case KEY_CTRLNUMPAD2: {
			Flags.Set(FEDITOR_NEWUNDO);
			ScrollDown();
			Show();
			return TRUE;
		}
		case KEY_PGUP:
		case KEY_NUMPAD9: {
			Flags.Set(FEDITOR_NEWUNDO);

			for (I = Y1; I < Y2; I++)
				ScrollUp();

			Show();
			return TRUE;
		}
		case KEY_PGDN:
		case KEY_NUMPAD3: {
			Flags.Set(FEDITOR_NEWUNDO);

			for (I = Y1; I < Y2; I++)
				ScrollDown();

			Show();
			return TRUE;
		}
		case KEY_CTRLHOME:
		case KEY_CTRLNUMPAD7:
		case KEY_CTRLPGUP:
		case KEY_CTRLNUMPAD9: {
			{
				Flags.Set(FEDITOR_NEWUNDO);
				int StartPos = CurLine->GetCellCurPos();
				NumLine = 0;
				TopScreen = CurLine = TopList;

				if (Key == KEY_CTRLHOME || Key == KEY_CTRLNUMPAD7)
					CurLine->SetCurPos(0);
				else
					CurLine->SetCellCurPos(StartPos);

				Show();
			}
			return TRUE;
		}
		case KEY_CTRLEND:
		case KEY_CTRLNUMPAD1:
		case KEY_CTRLPGDN:
		case KEY_CTRLNUMPAD3: {
			{
				Flags.Set(FEDITOR_NEWUNDO);
				int StartPos = CurLine->GetCellCurPos();
				NumLine = NumLastLine - 1;
				CurLine = EndList;

				for (TopScreen = CurLine, I = Y1; I < Y2 && TopScreen->m_prev; I++) {
					TopScreen->SetPosition(X1, I, XX2, I);
					TopScreen = TopScreen->m_prev;
				}

				CurLine->SetLeftPos(0);

				if (Key == KEY_CTRLEND || Key == KEY_CTRLNUMPAD1) {
					CurLine->SetCurPos(CurLine->GetLength());
					CurLine->FastShow();
				} else
					CurLine->SetCellCurPos(StartPos);

				Show();
			}
			return TRUE;
		}
		case KEY_NUMENTER:
		case KEY_ENTER: {
			if (Pasting || !ShiftPressed || CtrlObject->Macro.IsExecuting()) {
				if (!Pasting && !EdOpt.PersistentBlocks && BlockStart)
					DeleteBlock();

				Flags.Set(FEDITOR_NEWUNDO);
				InsertString();
				CurLine->FastShow();
				Show();
			}

			return TRUE;
		}
		case KEY_CTRLN: {
			Flags.Set(FEDITOR_NEWUNDO);

			while (CurLine != TopScreen) {
				CurLine = CurLine->m_prev;
				NumLine--;
			}

			CurLine->SetCurPos(CurPos);
			Show();
			return TRUE;
		}
		case KEY_CTRLE: {
			{
				Flags.Set(FEDITOR_NEWUNDO);
				Edit *CurPtr = TopScreen;
				int CurLineFound = FALSE;

				for (I = Y1; I < Y2; I++) {
					if (!CurPtr->m_next)
						break;

					if (CurPtr == CurLine)
						CurLineFound = TRUE;

					if (CurLineFound)
						NumLine++;

					CurPtr = CurPtr->m_next;
				}

				CurLine = CurPtr;
				CurLine->SetCurPos(CurPos);
				Show();
			}
			return TRUE;
		}
		case KEY_CTRLL: {
			Flags.Swap(FEDITOR_LOCKMODE);

			if (HostFileEditor)
				HostFileEditor->ShowStatus();

			return TRUE;
		}
		case KEY_CTRLY: {
			DeleteString(CurLine, NumLine, FALSE, NumLine);
			Show();
			return TRUE;
		}
		case KEY_F7: {
			int ReplaceMode0 = ReplaceMode;
			int ReplaceAll0 = ReplaceAll;
			ReplaceMode = ReplaceAll = FALSE;

			if (!Search(FALSE)) {
				ReplaceMode = ReplaceMode0;
				ReplaceAll = ReplaceAll0;
			}

			return TRUE;
		}
		case KEY_CTRLF7: {
			if (!Flags.Check(FEDITOR_LOCKMODE)) {
				int ReplaceMode0 = ReplaceMode;
				int ReplaceAll0 = ReplaceAll;
				ReplaceMode = TRUE;
				ReplaceAll = FALSE;

				if (!Search(FALSE)) {
					ReplaceMode = ReplaceMode0;
					ReplaceAll = ReplaceAll0;
				}
			}

			return TRUE;
		}
		case KEY_SHIFTF7: {
			/*
				$ 20.09.2000 SVS
				При All после нажатия Shift-F7 надобно снова спросить...
				ReplaceAll=FALSE;
			*/
			/*
				$ 07.05.2001 IS
				Сказано в хелпе "Shift-F7 Продолжить _поиск_"
				ReplaceMode=FALSE;
			*/
			Flags.Clear(FEDITOR_MARKINGVBLOCK | FEDITOR_MARKINGBLOCK);
			Search(TRUE);
			return TRUE;
		}
		case KEY_ALTF7: {
			Flags.Clear(FEDITOR_MARKINGVBLOCK | FEDITOR_MARKINGBLOCK);
			int LastSearchReversePrev = LastSearchReverse;
			LastSearchReverse = !LastSearchReverse;
			Search(TRUE);
			LastSearchReverse = LastSearchReversePrev;
			return TRUE;
		}
		/*case KEY_F8:
		{
			Flags.Set(FEDITOR_TABLECHANGEDBYUSER);
			if ((AnsiText=!AnsiText))
			{
				int UseUnicode=FALSE;
				GetTable(&TableSet,TRUE,TableNum,UseUnicode);
			}
			TableNum=0;
			UseDecodeTable=AnsiText;
			SetStringsTable();
			if (HostFileEditor) HostFileEditor->ChangeEditKeyBar();
			Show();
			return TRUE;
		} */ //BUGBUGBUG
		/*case KEY_SHIFTF8:
		{
			{
				int UseUnicode=FALSE;
				int GetTableCode=GetTable(&TableSet,FALSE,TableNum,UseUnicode);
				if (GetTableCode!=-1)
				{
					Flags.Set(FEDITOR_TABLECHANGEDBYUSER);
					UseDecodeTable=GetTableCode;
					AnsiText=FALSE;
					SetStringsTable();
					if (HostFileEditor) HostFileEditor->ChangeEditKeyBar();
					Show();
				}
			}
			return TRUE; //BUGBUGBUG
		} */
		case KEY_F11: {
			/*
				if(!Flags.Check(FEDITOR_DIALOGMEMOEDIT))
				{
					CtrlObject->Plugins.CurEditor=HostFileEditor; // this;
					if (CtrlObject->Plugins.CommandsMenu(MODALTYPE_EDITOR,0,"Editor"))
						*PluginTitle=0;
					Show();
				}
			*/
			return TRUE;
		}
		case KEY_CTRLSHIFTZ:
		case KEY_ALTBS:
		case KEY_CTRLZ: {
			if (!Flags.Check(FEDITOR_LOCKMODE)) {
				Lock();
				Undo(Key == KEY_CTRLSHIFTZ);
				Flags.Set(FEDITOR_NEWUNDO);
				Unlock();
				Show();
			}

			return TRUE;
		}
		case KEY_ALTF8: {
			{
				GoToPosition();

				// <GOTO_UNMARK:1>
				if (!EdOpt.PersistentBlocks)
					UnmarkBlock();

				// </GOTO_UNMARK>
				Show();
			}
			return TRUE;
		}
		case KEY_ALTU: {
			if (!Flags.Check(FEDITOR_LOCKMODE)) {
				BlockLeft();
				Show();
			}

			return TRUE;
		}
		case KEY_ALTI: {
			if (!Flags.Check(FEDITOR_LOCKMODE)) {
				BlockRight();
				Show();
			}

			return TRUE;
		}
		case KEY_ALTSHIFTLEFT:
		case KEY_ALTSHIFTNUMPAD4:
		case KEY_ALTLEFT: {
			if (!CurPos)
				return TRUE;

			if (!Flags.Check(FEDITOR_MARKINGVBLOCK))
				BeginVBlockMarking();

			Pasting++;
			{
				int Delta = CurLine->GetCellCurPos() - CurLine->RealPosToCell(CurPos - 1);

				if (CurLine->GetCellCurPos() > VBlockX)
					VBlockSizeX-= Delta;
				else {
					VBlockX-= Delta;
					VBlockSizeX+= Delta;
				}

				/*
					$ 25.07.2000 tran
					остатки бага 22 - подправка при перебега за границу блока
				*/
				if (VBlockSizeX < 0) {
					VBlockSizeX = -VBlockSizeX;
					VBlockX-= VBlockSizeX;
				}

				ProcessKey(KEY_LEFT);
			}
			Pasting--;
			Show();
			//_D(SysLog(L"VBlockX=%i, VBlockSizeX=%i, GetLineCurPos=%i",VBlockX,VBlockSizeX,GetLineCurPos()));
			//_D(SysLog(L"~~~~~~~~~~~~~~~~ KEY_ALTLEFT END, VBlockY=%i:%i, VBlockX=%i:%i",VBlockY,VBlockSizeY,VBlockX,VBlockSizeX));
			return TRUE;
		}
		case KEY_ALTSHIFTRIGHT:
		case KEY_ALTSHIFTNUMPAD6:
		case KEY_ALTRIGHT: {
			/*
				$ 23.10.2000 tran
				вместо GetCellCurPos надо вызывать GetCurPos -
				сравнивать реальную позицию с реальной длиной
				а было сравнение видимой позицией с реальной длиной
			*/
			if (!EdOpt.CursorBeyondEOL && CurLine->GetCurPos() >= CurLine->GetLength())
				return TRUE;

			if (!Flags.Check(FEDITOR_MARKINGVBLOCK))
				BeginVBlockMarking();

			//_D(SysLog(L"---------------- KEY_ALTRIGHT, getLineCurPos=%i",GetLineCurPos()));
			Pasting++;
			{
				int Delta;
				/*
					$ 18.07.2000 tran
					встань в начало текста, нажми alt-right, alt-pagedown,
					выделится блок шириной в 1 колонку, нажми еще alt-right
					выделение сбросится
				*/
				int VisPos = CurLine->RealPosToCell(CurPos), NextVisPos = CurLine->RealPosToCell(CurPos + 1);
				//				_D(SysLog(L"CurPos=%i, VisPos=%i, NextVisPos=%i",
				//					CurPos,VisPos, NextVisPos); //,CurLine->GetCellCurPos()));
				Delta = NextVisPos - VisPos;
				//				_D(SysLog(L"Delta=%i",Delta));

				if (CurLine->GetCellCurPos() >= VBlockX + VBlockSizeX)
					VBlockSizeX+= Delta;
				else {
					VBlockX+= Delta;
					VBlockSizeX-= Delta;
				}

				/*
					$ 25.07.2000 tran
					остатки бага 22 - подправка при перебега за границу блока
				*/
				if (VBlockSizeX < 0) {
					VBlockSizeX = -VBlockSizeX;
					VBlockX-= VBlockSizeX;
				}

				ProcessKey(KEY_RIGHT);
				//_D(SysLog(L"VBlockX=%i, VBlockSizeX=%i, GetLineCurPos=%i",VBlockX,VBlockSizeX,GetLineCurPos()));
			}
			Pasting--;
			Show();
			//_D(SysLog(L"~~~~~~~~~~~~~~~~ KEY_ALTRIGHT END, VBlockY=%i:%i, VBlockX=%i:%i",VBlockY,VBlockSizeY,VBlockX,VBlockSizeX));
			return TRUE;
		}
		/*
			$ 29.06.2000 IG
			+ CtrlAltLeft, CtrlAltRight для вертикальный блоков
		*/
		case KEY_CTRLALTLEFT:
		case KEY_CTRLALTNUMPAD4: {
			{
				int SkipSpace = TRUE;
				Pasting++;
				Lock();

				for (;;) {
					const wchar_t *Str;
					int Length;
					CurLine->GetBinaryString(&Str, nullptr, Length);
					int CurPos = CurLine->GetCurPos();

					if (CurPos > Length) {
						CurLine->ProcessKey(KEY_END);
						CurPos = CurLine->GetCurPos();
					}

					if (!CurPos)
						break;

					if (IsSpace(Str[CurPos - 1]) || IsWordDiv(EdOpt.strWordDiv, Str[CurPos - 1])) {
						if (SkipSpace) {
							ProcessKey(KEY_ALTSHIFTLEFT);
							continue;
						} else
							break;
					}

					SkipSpace = FALSE;
					ProcessKey(KEY_ALTSHIFTLEFT);
				}

				Pasting--;
				Unlock();
				Show();
			}
			return TRUE;
		}
		case KEY_CTRLALTRIGHT:
		case KEY_CTRLALTNUMPAD6: {
			{
				int SkipSpace = TRUE;
				Pasting++;
				Lock();

				for (;;) {
					const wchar_t *Str;
					int Length;
					CurLine->GetBinaryString(&Str, nullptr, Length);
					int CurPos = CurLine->GetCurPos();

					if (CurPos >= Length)
						break;

					if (IsSpace(Str[CurPos]) || IsWordDiv(EdOpt.strWordDiv, Str[CurPos])) {
						if (SkipSpace) {
							ProcessKey(KEY_ALTSHIFTRIGHT);
							continue;
						} else
							break;
					}

					SkipSpace = FALSE;
					ProcessKey(KEY_ALTSHIFTRIGHT);
				}

				Pasting--;
				Unlock();
				Show();
			}
			return TRUE;
		}
		case KEY_ALTSHIFTUP:
		case KEY_ALTSHIFTNUMPAD8:
		case KEY_ALTUP: {
			if (!CurLine->m_prev)
				return TRUE;

			if (!Flags.Check(FEDITOR_MARKINGVBLOCK))
				BeginVBlockMarking();

			if (!EdOpt.CursorBeyondEOL && VBlockX >= CurLine->m_prev->RealPosToCell(CurLine->m_prev->GetLength()))
				return TRUE;

			Pasting++;

			if (NumLine > VBlockY)
				VBlockSizeY--;
			else {
				VBlockY--;
				VBlockSizeY++;
				VBlockStart = VBlockStart->m_prev;
				BlockStartLine--;
			}

			ProcessKey(KEY_UP);
			AdjustVBlock(CurVisPos);
			Pasting--;
			Show();
			//_D(SysLog(L"~~~~~~~~ ALT_PGUP, VBlockY=%i:%i, VBlockX=%i:%i",VBlockY,VBlockSizeY,VBlockX,VBlockSizeX));
			return TRUE;
		}
		case KEY_ALTSHIFTDOWN:
		case KEY_ALTSHIFTNUMPAD2:
		case KEY_ALTDOWN: {
			if (!CurLine->m_next)
				return TRUE;

			if (!Flags.Check(FEDITOR_MARKINGVBLOCK))
				BeginVBlockMarking();

			if (!EdOpt.CursorBeyondEOL && VBlockX >= CurLine->m_next->RealPosToCell(CurLine->m_next->GetLength()))
				return TRUE;

			Pasting++;

			if (NumLine >= VBlockY + VBlockSizeY - 1)
				VBlockSizeY++;
			else {
				VBlockY++;
				VBlockSizeY--;
				VBlockStart = VBlockStart->m_next;
				BlockStartLine++;
			}

			ProcessKey(KEY_DOWN);
			AdjustVBlock(CurVisPos);
			Pasting--;
			Show();
			//_D(SysLog(L"~~~~ Key_AltDOWN: VBlockY=%i:%i, VBlockX=%i:%i",VBlockY,VBlockSizeY,VBlockX,VBlockSizeX));
			return TRUE;
		}
		case KEY_ALTSHIFTHOME:
		case KEY_ALTSHIFTNUMPAD7:
		case KEY_ALTHOME: {
			Pasting++;
			Lock();

			while (CurLine->GetCurPos() > 0)
				ProcessKey(KEY_ALTSHIFTLEFT);

			Unlock();
			Pasting--;
			Show();
			return TRUE;
		}
		case KEY_ALTSHIFTEND:
		case KEY_ALTSHIFTNUMPAD1:
		case KEY_ALTEND: {
			Pasting++;
			Lock();

			if (CurLine->GetCurPos() < CurLine->GetLength())
				while (CurLine->GetCurPos() < CurLine->GetLength())
					ProcessKey(KEY_ALTSHIFTRIGHT);

			if (CurLine->GetCurPos() > CurLine->GetLength())
				while (CurLine->GetCurPos() > CurLine->GetLength())
					ProcessKey(KEY_ALTSHIFTLEFT);

			Unlock();
			Pasting--;
			Show();
			return TRUE;
		}
		case KEY_ALTSHIFTPGUP:
		case KEY_ALTSHIFTNUMPAD9:
		case KEY_ALTPGUP: {
			Pasting++;
			Lock();

			for (I = Y1; I < Y2; I++)
				ProcessKey(KEY_ALTSHIFTUP);

			Unlock();
			Pasting--;
			Show();
			return TRUE;
		}
		case KEY_ALTSHIFTPGDN:
		case KEY_ALTSHIFTNUMPAD3:
		case KEY_ALTPGDN: {
			Pasting++;
			Lock();

			for (I = Y1; I < Y2; I++)
				ProcessKey(KEY_ALTSHIFTDOWN);

			Unlock();
			Pasting--;
			Show();
			return TRUE;
		}
		case KEY_CTRLALTPGUP:
		case KEY_CTRLALTNUMPAD9:
		case KEY_CTRLALTHOME:
		case KEY_CTRLALTNUMPAD7: {
			Lock();
			Pasting++;

			Edit* PrevLine = nullptr;
			while (CurLine!=TopList && PrevLine!=CurLine) {
				PrevLine = CurLine;
				ProcessKey(KEY_ALTUP);
			}

			Pasting--;
			Unlock();
			Show();
			return TRUE;
		}
		case KEY_CTRLALTPGDN:
		case KEY_CTRLALTNUMPAD3:
		case KEY_CTRLALTEND:
		case KEY_CTRLALTNUMPAD1: {
			Lock();
			Pasting++;

			Edit* PrevLine = nullptr;
			while (CurLine!=EndList && PrevLine!=CurLine) {
				PrevLine = CurLine;
				ProcessKey(KEY_ALTDOWN);
			}

			Pasting--;
			Unlock();
			Show();
			return TRUE;
		}
		case KEY_CTRLALTBRACKET:		// Вставить реальный (разрешенный) путь из левой панели
		case KEY_CTRLALTBACKBRACKET:	// Вставить реальный (разрешенный) путь из правой панели
		case KEY_ALTSHIFTBRACKET:		// Вставить реальный (разрешенный) путь из активной панели
		case KEY_ALTSHIFTBACKBRACKET:	// Вставить реальный (разрешенный) путь из пассивной панели
		case KEY_CTRLBRACKET:			// Вставить путь из левой панели
		case KEY_CTRLBACKBRACKET:		// Вставить путь из правой панели
		case KEY_CTRLSHIFTBRACKET:		// Вставить путь из активной панели
		case KEY_CTRLSHIFTBACKBRACKET:	// Вставить путь из пассивной панели
		case KEY_CTRLSHIFTNUMENTER:
		case KEY_SHIFTNUMENTER:
		case KEY_CTRLSHIFTENTER:
		case KEY_SHIFTENTER: {
			if (!Flags.Check(FEDITOR_LOCKMODE)) {
				Pasting++;
				AddUndoData(UNDO_BEGIN);
				TextChanged(1);

				if (!EdOpt.PersistentBlocks && BlockStart) {
					Flags.Clear(FEDITOR_MARKINGVBLOCK | FEDITOR_MARKINGBLOCK);
					DeleteBlock();
				}

				AddUndoData(UNDO_EDIT, CurLine->GetStringAddr(), CurLine->GetEOL(), NumLine,
						CurLine->GetCurPos(), CurLine->GetLength());
				CurLine->ProcessKey(Key);
				Pasting--;
				AddUndoData(UNDO_END);
				Show();
			}

			return TRUE;
		}
		case KEY_CTRLQ: {
			if (!Flags.Check(FEDITOR_LOCKMODE)) {
				Flags.Set(FEDITOR_PROCESSCTRLQ);

				if (HostFileEditor)
					HostFileEditor->ShowStatus();

				Pasting++;
				TextChanged(1);

				if (!EdOpt.PersistentBlocks && BlockStart) {
					Flags.Clear(FEDITOR_MARKINGVBLOCK | FEDITOR_MARKINGBLOCK);
					DeleteBlock();
				}

				AddUndoData(UNDO_EDIT, CurLine->GetStringAddr(), CurLine->GetEOL(), NumLine,
						CurLine->GetCurPos(), CurLine->GetLength());
				CurLine->ProcessCtrlQ();
				Flags.Clear(FEDITOR_PROCESSCTRLQ);
				Pasting--;
				Show();
			}

			return TRUE;
		}
		case KEY_OP_SELWORD: {
			int OldCurPos = CurPos;
			int SStart, SEnd;
			Pasting++;
			Lock();
			UnmarkBlock();

			// CurLine->TableSet ??? => UseDecodeTable?CurLine->TableSet:nullptr !!!
			if (CalcWordFromString(CurLine->GetStringAddr(), CurPos, &SStart, &SEnd, EdOpt.strWordDiv)) {
				CurLine->Select(SStart, SEnd + (SEnd < CurLine->StrSize ? 1 : 0));

				if (CurLine->IsSelection()) {
					Flags.Set(FEDITOR_MARKINGBLOCK);
					BlockStart = CurLine;
					BlockStartLine = NumLine;
					// SelFirst=TRUE;
					SelStart = SStart;
					SelEnd = SEnd;
					// CurLine->ProcessKey(MCODE_OP_SELWORD);
				}
			}

			CurPos = OldCurPos;		// возвращаем обратно
			Pasting--;
			Unlock();
			Show();
			return TRUE;
		}
		case KEY_OP_PLAINTEXT: {
			if (!Flags.Check(FEDITOR_LOCKMODE)) {
				const wchar_t *Fmt = eStackAsString();
				FARString strTStr;

				strTStr = Fmt;

				// заменим L'\n' на L'\r' по правилам Paset ;-)
				ReplaceChars(strTStr, L'\n', L'\r');

				Pasting++;
				//_SVS(SysLogDump(Fmt,0,TStr,strlen(TStr),nullptr));
				TextChanged(1);
				BOOL IsBlock = VBlockStart || BlockStart;

				if (!EdOpt.PersistentBlocks && IsBlock) {
					Flags.Clear(FEDITOR_MARKINGVBLOCK | FEDITOR_MARKINGBLOCK);
					DeleteBlock();
				}

				// AddUndoData(UNDO_EDIT,CurLine->GetStringAddr(),CurLine->GetEOL(),NumLine,CurLine->GetCurPos(),CurLine->GetLength());
				Paste(strTStr);
				// if (!EdOpt.PersistentBlocks && IsBlock)
				UnmarkBlock();
				Pasting--;
				Show();
			}

			return TRUE;
		}
		default: {
			{
				if ((Key == KEY_CTRLDEL || Key == KEY_CTRLNUMDEL || Key == KEY_CTRLDECIMAL
							|| Key == KEY_CTRLT)
						&& CurPos >= CurLine->GetLength()) {
					/*
						$ 08.12.2000 skv
						- CTRL-DEL в начале строки при выделенном блоке и
						включенном EditorDelRemovesBlocks
					*/
					int save = EdOpt.DelRemovesBlocks;
					EdOpt.DelRemovesBlocks = 0;
					int ret = ProcessKey(KEY_DEL);
					EdOpt.DelRemovesBlocks = save;
					return ret;
				}

				if (!Pasting && !EdOpt.PersistentBlocks && BlockStart)
					if ((Key >= L' ' && WCHAR_IS_VALID(Key)) || Key == KEY_ADD || Key == KEY_SUBTRACT ||	// ??? 256 ???
							Key == KEY_MULTIPLY || Key == KEY_DIVIDE || Key == KEY_TAB) {
						DeleteBlock();
						/*
							$ 19.09.2002 SKV
							Однако надо.
							Иначе есди при надичии выделения набирать
							текст с шифтом флаги не сбросятся и следующий
							выделенный блок будет глючный.
						*/
						Flags.Clear(FEDITOR_MARKINGVBLOCK | FEDITOR_MARKINGBLOCK);
						Show();
					}

				int SkipCheckUndo = (Key == KEY_RIGHT || Key == KEY_NUMPAD6 || Key == KEY_CTRLLEFT
						|| Key == KEY_CTRLNUMPAD4 || Key == KEY_CTRLRIGHT || Key == KEY_CTRLNUMPAD6
						|| Key == KEY_HOME || Key == KEY_NUMPAD7 || Key == KEY_END || Key == KEY_NUMPAD1
						|| Key == KEY_CTRLS);

				if (Flags.Check(FEDITOR_LOCKMODE) && !SkipCheckUndo)
					return TRUE;

				if ((Key == KEY_CTRLLEFT || Key == KEY_CTRLNUMPAD4) && !CurLine->GetCurPos()) {
					Pasting++;
					ProcessKey(KEY_LEFT);
					Pasting--;
					/*
						$ 24.9.2001 SKV
						fix бага с ctrl-left в начале строки
						в блоке с переопределённым плагином фоном.
					*/
					ShowEditor(FALSE);
					// if(!Flags.Check(FEDITOR_DIALOGMEMOEDIT)){
					// CtrlObject->Plugins.CurEditor=HostFileEditor; // this;
					//_D(SysLog(L"%08d EE_REDRAW",__LINE__));
					// CtrlObject->Plugins.ProcessEditorEvent(EE_REDRAW,EEREDRAW_ALL);
					// }
					return TRUE;
				}

				if (((!EdOpt.CursorBeyondEOL && (Key == KEY_RIGHT || Key == KEY_NUMPAD6))
							|| Key == KEY_CTRLRIGHT || Key == KEY_CTRLNUMPAD6)
						&& CurLine->GetCurPos() >= CurLine->GetLength() && CurLine->m_next) {
					Pasting++;
					ProcessKey(KEY_HOME);
					ProcessKey(KEY_DOWN);
					Pasting--;

					if (!Flags.Check(FEDITOR_DIALOGMEMOEDIT)) {
						CtrlObject->Plugins.CurEditor = HostFileEditor;		// this;
						//_D(SysLog(L"%08d EE_REDRAW",__LINE__));
						_SYS_EE_REDRAW(SysLog(L"Editor::ProcessKey[%d](!EdOpt.CursorBeyondEOL): "
											  L"EE_REDRAW(EEREDRAW_ALL)",
								__LINE__));
						CtrlObject->Plugins.ProcessEditorEvent(EE_REDRAW, EEREDRAW_ALL);
					}

					/*
						$ 03.02.2001 SKV
						А то EEREDRAW_ALL то уходит, а на самом деле
						только текущая линия перерисовывается.
					*/
					ShowEditor(0);
					return TRUE;
				}

				const wchar_t *Str;

				wchar_t *CmpStr = 0;

				int Length, CurPos;

				if (!SkipCheckUndo) {
					CurLine->GetBinaryString(&Str, nullptr, Length);
					//CurPos = CurLine->GetCurPos();
					CmpStr = new wchar_t[Length + 1];
					wmemcpy(CmpStr, Str, Length);
					CmpStr[Length] = 0;
				}

				CurLine->GetBinaryString(&Str, nullptr, Length);

				CurPos = CurLine->GetCurPos();

				if (WCHAR_IS_VALID(Key) && CurPos > Length) {

					// detect space alignment by search for lines starting with space
					Edit *PrevLine = CurLine->m_prev;
					bool SpaceAligned = false;
					while (PrevLine) {
						if (PrevLine->GetLength()) {
							const wchar_t *PrevStr = L"\x00";
							int TmpLength;
							PrevLine->GetBinaryString(&PrevStr, nullptr, TmpLength);
							if (PrevStr[0] == ' ') {
								SpaceAligned = true;
								break;
							}
							if (PrevStr[0] == '\t') {
								break;
							}
						}
						PrevLine = PrevLine->m_prev;
					}

					// detect if there are any non-space chars in the current line
					bool NonSpaceFound = false;
					for (int I = 0; I < Length; I++) {
						if (!IsSpace(Str[I])) {
							NonSpaceFound = true;
							break;
						}
					}
						
					int TabPos = CurLine->GetCellCurPos();
					CurLine->SetCurPos(Length);

					for (int I = Length; I < CurPos; I++) {

						int NewTabPos = CurLine->GetCellCurPos();

						if (NewTabPos == TabPos)
							break;

						if (NewTabPos > TabPos) {
							CurLine->ProcessKey(KEY_BS);

							while (CurLine->GetCellCurPos() < TabPos)
								CurLine->ProcessKey(' ');

							break;
						}

						if (NewTabPos < TabPos)
							CurLine->ProcessKey(
								SpaceAligned ||
								NonSpaceFound ||
								GetConvertTabs() ||
								((I + 1 == CurPos) && (TabPos % EdOpt.TabSize))
									? ' ' : '\t'
							);
					}

					CurLine->SetCellCurPos(TabPos);
				}

				int LeftPos = CurLine->GetLeftPos();

				if (Key == KEY_OP_XLAT) {
					Xlat();
					Show();
					delete[] CmpStr;
					return TRUE;
				}

				// <comment> - это требуется для корректной работы логики блоков для Ctrl-K
				int PreSelStart, PreSelEnd;
				CurLine->GetSelection(PreSelStart, PreSelEnd);
				// </comment>
				// AY: Это что бы при FastShow LeftPos не становился в конец строки.
				CurLine->ObjWidth = XX2 - X1 + 1;

				if (CurLine->ProcessKey(Key)) {
					int SelStart, SelEnd;

					/*
						$ 17.09.2002 SKV
						Если находимся в середине блока,
						в начале строки, и нажимаем tab, который заменяется
						на пробелы, выделение съедет. Это фикс.
					*/
					if (Key == KEY_TAB && CurLine->GetConvertTabs() && BlockStart && BlockStart != CurLine) {
						CurLine->GetSelection(SelStart, SelEnd);
						CurLine->Select(SelStart == -1 ? -1 : 0, SelEnd);
					}

					if (!SkipCheckUndo) {
						const wchar_t *NewCmpStr;
						int NewLength;
						CurLine->GetBinaryString(&NewCmpStr, nullptr, NewLength);

						if (NewLength != Length || memcmp(CmpStr, NewCmpStr, Length * sizeof(wchar_t))) {
							// EOL? - CurLine->GetEOL() GlobalEOL ""
							AddUndoData(UNDO_EDIT, CmpStr, CurLine->GetEOL(), NumLine, CurPos, Length);
							TextChanged(1);
						}

						delete[] CmpStr;
					}

					// <Bug 794>
					// обработаем только первую и последнюю строку с блоком
					if (Key == KEY_CTRLK && EdOpt.PersistentBlocks) {
						if (CurLine == BlockStart) {
							if (CurPos) {
								CurLine->GetSelection(SelStart, SelEnd);

								// 1. блок за концом строки (CurPos был ближе к началу, чем SelStart)
								if ((SelEnd == -1 && PreSelStart > CurPos) || SelEnd > CurPos)
									SelStart = SelEnd = -1;		// в этом случае снимаем выделение

								// 2. CurPos внутри блока
								else if (SelEnd == -1 && PreSelEnd > CurPos && SelStart < CurPos)
									SelEnd = PreSelEnd;		// в этом случае усекаем блок

								// 3. блок остался слева от CurPos или выделение нужно снять (см. выше)
								if (SelEnd >= CurPos || SelStart == -1)
									CurLine->Select(SelStart, CurPos);
							} else {
								CurLine->Select(-1, -1);
								BlockStart = BlockStart->m_next;
							}
						} else		// ЗДЕСЬ ЗАСАДА !!! ЕСЛИ ВЫДЕЛЕННЫЙ БЛОК ДОСТАТОЧНО БОЛЬШОЙ (ПО СТРОКАМ), ТО ЦИКЛ ПЕРЕБОРА... МОЖЕТ ЗАТЯНУТЬ...
						{
							// найдем эту последнюю строку (и последняя ли она)
							Edit *CurPtrBlock = BlockStart, *CurPtrBlock2 = BlockStart;

							while (CurPtrBlock) {
								CurPtrBlock->GetRealSelection(SelStart, SelEnd);

								if (SelStart == -1)
									break;

								CurPtrBlock2 = CurPtrBlock;
								CurPtrBlock = CurPtrBlock->m_next;
							}

							if (CurLine == CurPtrBlock2) {
								if (CurPos) {
									CurLine->GetSelection(SelStart, SelEnd);
									CurLine->Select(SelStart, CurPos);
								} else {
									CurLine->Select(-1, -1);
									CurPtrBlock2 = CurPtrBlock2->m_next;
								}
							}
						}
					}

					// </Bug 794>
					ShowEditor(LeftPos == CurLine->GetLeftPos());
					return TRUE;
				} else if (!SkipCheckUndo)
					delete[] CmpStr;

				if (VBlockStart)
					Show();
			}
			return FALSE;
		}
	}
}

int Editor::ProcessMouse(MOUSE_EVENT_RECORD *MouseEvent)
{
	Edit *NewPtr;
	int NewDist, Dist;

	// Shift + Mouse click -> adhoc quick edit
	if ((MouseEvent->dwControlKeyState & SHIFT_PRESSED) != 0 && (MouseEvent->dwEventFlags & MOUSE_MOVED) == 0
			&& (MouseEvent->dwButtonState & FROM_LEFT_1ST_BUTTON_PRESSED) != 0) {
		WINPORT(BeginConsoleAdhocQuickEdit)();
		return TRUE;
	}

	// $ 28.12.2000 VVM - Щелчок мышкой снимает непостоянный блок всегда
	if ((MouseEvent->dwButtonState & 3)) {
		Flags.Clear(FEDITOR_MARKINGVBLOCK | FEDITOR_MARKINGBLOCK);

		if ((!EdOpt.PersistentBlocks) && (BlockStart || VBlockStart)) {
			UnmarkBlock();
			Show();
		}
	}

	if (EdOpt.ShowScrollBar && MouseEvent->dwMousePosition.X == X2
			&& !(MouseEvent->dwEventFlags & MOUSE_MOVED)) {
		if (MouseEvent->dwMousePosition.Y == Y1) {
			while (IsMouseButtonPressed()) {
				ProcessKey(KEY_CTRLUP);
			}
		} else if (MouseEvent->dwMousePosition.Y == Y2) {
			while (IsMouseButtonPressed()) {
				ProcessKey(KEY_CTRLDOWN);
			}
		} else {
			while (IsMouseButtonPressed())
				GoToLine((NumLastLine - 1) * (MouseY - Y1) / (Y2 - Y1));
		}

		return TRUE;
	}

	if (MouseEvent->dwButtonState & FROM_LEFT_1ST_BUTTON_PRESSED) {
		static int EditorPrevDoubleClick = 0;
		static COORD EditorPrevPosition = {0, 0};

		if (WINPORT(GetTickCount)() - EditorPrevDoubleClick <= WINPORT(GetDoubleClickTime)()
				&& MouseEvent->dwEventFlags != MOUSE_MOVED
				&& EditorPrevPosition.X == MouseEvent->dwMousePosition.X
				&& EditorPrevPosition.Y == MouseEvent->dwMousePosition.Y) {
			CurLine->Select(0, CurLine->StrSize);

			if (CurLine->IsSelection()) {
				Flags.Set(FEDITOR_MARKINGBLOCK);
				BlockStart = CurLine;
				BlockStartLine = NumLine;
			}

			EditorPrevDoubleClick = 0;
			EditorPrevPosition.X = 0;
			EditorPrevPosition.Y = 0;
		}

		if (MouseEvent->dwEventFlags == DOUBLE_CLICK) {
			Flags.Clear(FEDITOR_MARKINGVBLOCK | FEDITOR_MARKINGBLOCK);

			if (BlockStart || VBlockStart)
				UnmarkBlock();

			ProcessKey(KEY_OP_SELWORD);
			EditorPrevDoubleClick = WINPORT(GetTickCount)();
			EditorPrevPosition = MouseEvent->dwMousePosition;
		} else {
			EditorPrevDoubleClick = 0;
			EditorPrevPosition.X = 0;
			EditorPrevPosition.Y = 0;
		}

		Show();
	}

	if (MouseEvent->dwButtonState == FROM_LEFT_2ND_BUTTON_PRESSED
			&& (MouseEvent->dwEventFlags & (DOUBLE_CLICK | MOUSE_MOVED | MOUSE_HWHEELED | MOUSE_WHEELED))
					== 0) {
		ProcessPasteEvent();
	}

	if (CurLine->ProcessMouse(MouseEvent)) {
		if (HostFileEditor)
			HostFileEditor->ShowStatus();

		if (VBlockStart)
			Show();
		else {
			if (!Flags.Check(FEDITOR_DIALOGMEMOEDIT)) {
				CtrlObject->Plugins.CurEditor = HostFileEditor;		// this;
				_SYS_EE_REDRAW(
						SysLog(L"Editor::ProcessMouse[%08d] ProcessEditorEvent(EE_REDRAW,EEREDRAW_LINE)",
								__LINE__));
				CtrlObject->Plugins.ProcessEditorEvent(EE_REDRAW, EEREDRAW_LINE);
			}
		}

		return TRUE;
	}

	if (!(MouseEvent->dwButtonState & 3))
		return FALSE;

	// scroll up
	if (MouseEvent->dwMousePosition.Y == Y1 - 1) {
		while (IsMouseButtonPressed() && MouseY == Y1 - 1)
			ProcessKey(KEY_UP);

		return TRUE;
	}

	// scroll down
	if (MouseEvent->dwMousePosition.Y == Y2 + 1) {
		while (IsMouseButtonPressed() && MouseY == Y2 + 1)
			ProcessKey(KEY_DOWN);

		return TRUE;
	}

	if (MouseEvent->dwMousePosition.X < X1 || MouseEvent->dwMousePosition.X > X2
			|| MouseEvent->dwMousePosition.Y < Y1 || MouseEvent->dwMousePosition.Y > Y2)
		return FALSE;

	NewDist = MouseEvent->dwMousePosition.Y - Y1;
	NewPtr = TopScreen;

	while (NewDist-- && NewPtr->m_next)
		NewPtr = NewPtr->m_next;

	Dist = CalcDistance(TopScreen, NewPtr, -1) - CalcDistance(TopScreen, CurLine, -1);

	if (Dist > 0)
		while (Dist--)
			Down();
	else
		while (Dist++)
			Up();

	CurLine->ProcessMouse(MouseEvent);
	Show();
	return TRUE;
}

int Editor::CalcDistance(Edit *From, Edit *To, int MaxDist)
{
	int Distance = 0;

	while (From != To && From->m_next && (MaxDist == -1 || MaxDist-- > 0)) {
		Distance++;
		From = From->m_next;
	}

	return (Distance);
}

void Editor::DeleteString(Edit *DelPtr, int LineNumber, int DeleteLast, int UndoLine)
{
	if (Flags.Check(FEDITOR_LOCKMODE))
		return;

	/*
		$ 16.12.2000 OT
		CtrlY на последней строке с выделенным вертикальным блоком не снимал выделение
	*/
	if (VBlockStart && NumLine < VBlockY + VBlockSizeY) {
		if (NumLine < VBlockY) {
			if (VBlockY > 0) {
				VBlockY--;
				BlockStartLine--;
			}
		} else if (--VBlockSizeY <= 0)
			VBlockStart = nullptr;
	}

	TextChanged(1);

	if (!DelPtr->m_next && (!DeleteLast || !DelPtr->m_prev)) {
		AddUndoData(UNDO_EDIT, DelPtr->GetStringAddr(), DelPtr->GetEOL(), UndoLine, DelPtr->GetCurPos(),
				DelPtr->GetLength());
		DelPtr->SetString(L"");
		return;
	}

	for (size_t I = 0; I < ARRAYSIZE(SavePos.Line); I++)
		if (SavePos.Line[I] != POS_NONE && UndoLine < static_cast<int>(SavePos.Line[I]))
			SavePos.Line[I]--;

	if (StackPos) {
		InternalEditorStackBookMark *sb_temp = StackPos, *sb_new;

		while (sb_temp->prev)
			sb_temp = sb_temp->prev;

		while (sb_temp) {
			sb_new = sb_temp->next;

			if (UndoLine < static_cast<int>(sb_temp->Line))
				sb_temp->Line--;
			else {
				if (UndoLine == static_cast<int>(sb_temp->Line))
					DeleteStackBookmark(sb_temp);
			}

			sb_temp = sb_new;
		}
	}

	NumLastLine--;

	if (LastGetLine) {
		if (LineNumber <= LastGetLineNumber) {
			if (LineNumber == LastGetLineNumber) {
				LastGetLine = LastGetLine->m_prev;
			}
			LastGetLineNumber--;
		}
	}

	if (CurLine == DelPtr) {
		int LeftPos, CurPos;
		CurPos = DelPtr->GetCellCurPos();
		LeftPos = DelPtr->GetLeftPos();

		if (DelPtr->m_next)
			CurLine = DelPtr->m_next;
		else {
			CurLine = DelPtr->m_prev;
			/*
				$ 04.11.2002 SKV
				Вроде как если это произошло, номер текущей строки надо изменить.
			*/
			NumLine--;
		}

		CurLine->SetLeftPos(LeftPos);
		CurLine->SetCellCurPos(CurPos);
	}

	if (DelPtr->m_prev) {
		DelPtr->m_prev->m_next = DelPtr->m_next;

		if (DelPtr == EndList)
			EndList = EndList->m_prev;
	}

	if (DelPtr->m_next)
		DelPtr->m_next->m_prev = DelPtr->m_prev;

	if (DelPtr == TopScreen) {
		if (TopScreen->m_next)
			TopScreen = TopScreen->m_next;
		else
			TopScreen = TopScreen->m_prev;
	}

	if (DelPtr == TopList)
		TopList = TopList->m_next;

	if (DelPtr == BlockStart) {
		BlockStart = BlockStart->m_next;

		// Mantis#0000316: Не работает копирование строки
		if (BlockStart && !BlockStart->IsSelection())
			BlockStart = nullptr;
	}

	if (DelPtr == VBlockStart)
		VBlockStart = VBlockStart->m_next;

	if (UndoLine != -1)
		AddUndoData(UNDO_DELSTR, DelPtr->GetStringAddr(), DelPtr->GetEOL(), UndoLine, 0, DelPtr->GetLength());

	delete DelPtr;
}

void Editor::InsertString()
{
	if (Flags.Check(FEDITOR_LOCKMODE))
		return;

	/*
		$ 10.08.2000 skv
		There is only one return - if new will fail.
		In this case things are really bad.
		Move TextChanged to the end of functions
		AFTER all modifications are made.
	*/
	//	TextChanged(1);
	Edit *NewString;
	Edit *SrcIndent = nullptr;
	int SelStart, SelEnd;
	int CurPos;
	int NewLineEmpty = TRUE;
	NewString = InsertString(nullptr, 0, CurLine, NumLine);

	if (!NewString)
		return;

	// NewString->SetTables(UseDecodeTable ? &TableSet:nullptr); // ??
	int Length;
	const wchar_t *CurLineStr;
	const wchar_t *EndSeq;
	CurLine->GetBinaryString(&CurLineStr, &EndSeq, Length);

	/*
		$ 13.01.2002 IS
		Если не был определен тип конца строки, то считаем что конец строки
		у нас равен DOS_EOL_fmt и установим его явно.
	*/
	if (!*EndSeq)
		CurLine->SetEOL(*GlobalEOL ? GlobalEOL : NATIVE_EOLW);

	CurPos = CurLine->GetCurPos();
	CurLine->GetSelection(SelStart, SelEnd);

	for (size_t I = 0; I < ARRAYSIZE(SavePos.Line); I++)
		if (SavePos.Line[I] != POS_NONE
				&& (NumLine < (int)SavePos.Line[I] || (NumLine == (int)SavePos.Line[I] && !CurPos)))
			SavePos.Line[I]++;

	if (StackPos) {
		InternalEditorStackBookMark *sb_temp = StackPos;

		while (sb_temp->prev)
			sb_temp = sb_temp->prev;

		while (sb_temp) {
			if (NumLine < static_cast<int>(sb_temp->Line)
					|| (NumLine == static_cast<int>(sb_temp->Line) && !CurPos))
				sb_temp->Line++;

			sb_temp = sb_temp->next;
		}
	}

	int IndentPos = 0;

	if (EdOpt.AutoIndent && !Pasting && !BracketedPasteMode) {
		Edit *PrevLine = CurLine;

		while (PrevLine) {
			const wchar_t *Str;
			int Length, Found = FALSE;
			PrevLine->GetBinaryString(&Str, nullptr, Length);

			for (int I = 0; I < Length; I++)
				if (!IsSpace(Str[I])) {
					PrevLine->SetCurPos(I);
					IndentPos = PrevLine->GetCellCurPos();
					SrcIndent = PrevLine;
					Found = TRUE;
					break;
				}

			if (Found)
				break;

			PrevLine = PrevLine->m_prev;
		}
	}

	int SpaceOnly = TRUE;

	if (CurPos < Length) {
		if (IndentPos > 0)
			for (int I = 0; I < CurPos; I++)
				if (!IsSpace(CurLineStr[I])) {
					SpaceOnly = FALSE;
					break;
				}

		NewString->SetBinaryString(&CurLineStr[CurPos], Length - CurPos);

		for (int i0 = 0; i0 < Length - CurPos; i0++) {
			if (!IsSpace(CurLineStr[i0 + CurPos])) {
				NewLineEmpty = FALSE;
				break;
			}
		}

		AddUndoData(UNDO_BEGIN);
		AddUndoData(UNDO_EDIT, CurLine->GetStringAddr(), CurLine->GetEOL(), NumLine, CurLine->GetCurPos(),
				CurLine->GetLength());
		// EOL? - CurLine->GetEOL()  GlobalEOL   ""
		AddUndoData(UNDO_INSSTR, nullptr, EndList == CurLine ? L"" : GlobalEOL, NumLine + 1, 0);
		AddUndoData(UNDO_END);
		wchar_t *NewCurLineStr = (wchar_t *)malloc((CurPos + 1) * sizeof(wchar_t));

		if (!NewCurLineStr)
			return;

		wmemcpy(NewCurLineStr, CurLineStr, CurPos);
		NewCurLineStr[CurPos] = 0;
		int StrSize = CurPos;

		if (EdOpt.AutoIndent && NewLineEmpty) {
			RemoveTrailingSpaces(NewCurLineStr);
			StrSize = StrLength(NewCurLineStr);
		}

		CurLine->SetBinaryString(NewCurLineStr, StrSize);
		CurLine->SetEOL(EndSeq);
		free(NewCurLineStr);
	} else {
		NewString->SetString(L"");
		// EOL? - CurLine->GetEOL() GlobalEOL ""
		AddUndoData(UNDO_INSSTR, nullptr, L"", NumLine + 1, 0);
	}

	if (VBlockStart && NumLine < VBlockY + VBlockSizeY) {
		if (NumLine < VBlockY) {
			VBlockY++;
			BlockStartLine++;
		} else
			VBlockSizeY++;
	}

	if (SelStart != -1 && (SelEnd == -1 || CurPos < SelEnd)) {
		if (CurPos >= SelStart) {
			CurLine->Select(SelStart, -1);
			NewString->Select(0, SelEnd == -1 ? -1 : SelEnd - CurPos);
		} else {
			CurLine->Select(-1, 0);
			NewString->Select(SelStart - CurPos, SelEnd == -1 ? -1 : SelEnd - CurPos);
			BlockStart = NewString;
			BlockStartLine++;
		}
	} else if (BlockStart && NumLine < BlockStartLine)
		BlockStartLine++;

	NewString->SetEOL(EndSeq);
	CurLine->SetCurPos(0);

	if (CurLine == EndList)
		EndList = NewString;

	Down();

	if (IndentPos > 0) {
		int OrgIndentPos = IndentPos;
		ShowEditor(FALSE);
		CurLine->GetBinaryString(&CurLineStr, nullptr, Length);

		if (SpaceOnly) {
			int Decrement = 0;

			for (int I = 0; I < IndentPos && I < Length; I++) {
				if (!IsSpace(CurLineStr[I]))
					break;

				if (CurLineStr[I] == L' ')
					Decrement++;
				else {
					int TabPos = CurLine->RealPosToCell(I);
					Decrement+= EdOpt.TabSize - (TabPos % EdOpt.TabSize);
				}
			}

			IndentPos-= Decrement;
		}

		if (IndentPos > 0) {
			if (CurLine->GetLength() || !EdOpt.CursorBeyondEOL) {
				CurLine->ProcessKey(KEY_HOME);
				int SaveOvertypeMode = CurLine->GetOvertypeMode();
				CurLine->SetOvertypeMode(FALSE);
				const wchar_t *PrevStr = nullptr;
				int PrevLength = 0;

				if (SrcIndent) {
					SrcIndent->GetBinaryString(&PrevStr, nullptr, PrevLength);
				}

				for (int I = 0; CurLine->GetCellCurPos() < IndentPos; I++) {
					if (SrcIndent && I < PrevLength && IsSpace(PrevStr[I])) {
						CurLine->ProcessKey(PrevStr[I]);
					} else {
						CurLine->ProcessKey(KEY_SPACE);
					}
				}

				while (CurLine->GetCellCurPos() > IndentPos)
					CurLine->ProcessKey(KEY_BS);

				CurLine->SetOvertypeMode(SaveOvertypeMode);
			}

			CurLine->SetCellCurPos(IndentPos);
		}

		CurLine->GetBinaryString(&CurLineStr, nullptr, Length);
		CurPos = CurLine->GetCurPos();

		if (SpaceOnly) {
			int NewPos = 0;

			for (int I = 0; I < Length; I++) {
				NewPos = I;

				if (!IsSpace(CurLineStr[I]))
					break;
			}

			if (NewPos > OrgIndentPos)
				NewPos = OrgIndentPos;

			if (NewPos > CurPos)
				CurLine->SetCurPos(NewPos);
		}
	}

	TextChanged(1);
}

void Editor::Down()
{
	// TODO: "Свертка" - если учесть "!Flags.Check(FSCROBJ_VISIBLE)", то крутить надо до следующей видимой строки
	Edit *CurPtr;
	int LeftPos, CurPos, Y;

	if (!CurLine->m_next)
		return;

	for (Y = 0, CurPtr = TopScreen; CurPtr && CurPtr != CurLine; CurPtr = CurPtr->m_next)
		Y++;

	if (Y >= Y2 - Y1)
		TopScreen = TopScreen->m_next;

	CurPos = CurLine->GetCellCurPos();
	LeftPos = CurLine->GetLeftPos();
	CurLine = CurLine->m_next;
	NumLine++;
	CurLine->SetLeftPos(LeftPos);
	CurLine->SetCellCurPos(CurPos);
}

void Editor::ScrollDown()
{
	// TODO: "Свертка" - если учесть "!Flags.Check(FSCROBJ_VISIBLE)", то крутить надо до следующей видимой строки
	int LeftPos, CurPos;

	if (!CurLine->m_next || !TopScreen->m_next)
		return;

	if (!EdOpt.AllowEmptySpaceAfterEof && CalcDistance(TopScreen, EndList, Y2 - Y1) < Y2 - Y1) {
		Down();
		return;
	}

	TopScreen = TopScreen->m_next;
	CurPos = CurLine->GetCellCurPos();
	LeftPos = CurLine->GetLeftPos();
	CurLine = CurLine->m_next;
	NumLine++;
	CurLine->SetLeftPos(LeftPos);
	CurLine->SetCellCurPos(CurPos);
}

void Editor::Up()
{
	// TODO: "Свертка" - если учесть "!Flags.Check(FSCROBJ_VISIBLE)", то крутить надо до следующей видимой строки
	int LeftPos, CurPos;

	if (!CurLine->m_prev)
		return;

	if (CurLine == TopScreen)
		TopScreen = TopScreen->m_prev;

	CurPos = CurLine->GetCellCurPos();
	LeftPos = CurLine->GetLeftPos();
	CurLine = CurLine->m_prev;
	NumLine--;
	CurLine->SetLeftPos(LeftPos);
	CurLine->SetCellCurPos(CurPos);
}

void Editor::ScrollUp()
{
	// TODO: "Свертка" - если учесть "!Flags.Check(FSCROBJ_VISIBLE)", то крутить надо до следующей видимой строки
	int LeftPos, CurPos;

	if (!CurLine->m_prev)
		return;

	if (!TopScreen->m_prev) {
		Up();
		return;
	}

	TopScreen = TopScreen->m_prev;
	CurPos = CurLine->GetCellCurPos();
	LeftPos = CurLine->GetLeftPos();
	CurLine = CurLine->m_prev;
	NumLine--;
	CurLine->SetLeftPos(LeftPos);
	CurLine->SetCellCurPos(CurPos);
}

/*
	$ 21.01.2001 SVS
	Диалоги поиска/замены выведен из Editor::Search
	в отдельную функцию GetSearchReplaceString
	(файл stddlg.cpp)
*/
BOOL Editor::Search(int Next)
{
	Edit *CurPtr, *TmpPtr;
	FARString strSearchStr, strReplaceStr;
	static FARString strLastReplaceStr;
	// static int LastSuccessfulReplaceMode=0;
	FARString strMsgStr;
	const wchar_t *TextHistoryName = L"SearchText", *ReplaceHistoryName = L"ReplaceText";
	int CurPos, Case, WholeWords, ReverseSearch, SelectFound, Regexp, Match, NewNumLine, UserBreak;

	if (Next && strLastSearchStr.IsEmpty())
		return TRUE;

	strSearchStr = strLastSearchStr;
	strReplaceStr = strLastReplaceStr;
	Case = LastSearchCase;
	WholeWords = LastSearchWholeWords;
	ReverseSearch = LastSearchReverse;
	SelectFound = LastSearchSelFound;
	Regexp = LastSearchRegexp;

	if (!Next) {
		if (EdOpt.SearchPickUpWord) {
			int StartPickPos = -1, EndPickPos = -1;
			const wchar_t *Ptr = CalcWordFromString(CurLine->GetStringAddr(), CurLine->GetCurPos(),
					&StartPickPos, &EndPickPos, GetWordDiv());

			if (Ptr) {
				FARString strWord(Ptr, (size_t)EndPickPos - StartPickPos + 1);
				strSearchStr = strWord;
			}
		}

		if (!GetSearchReplaceString(ReplaceMode, &strSearchStr, &strReplaceStr, TextHistoryName,
					ReplaceHistoryName, &Case, &WholeWords, &ReverseSearch, &SelectFound, &Regexp,
					L"EditorSearch"))
			return FALSE;
	}

	strLastSearchStr = strSearchStr;
	strLastReplaceStr = strReplaceStr;
	LastSearchCase = Case;
	LastSearchWholeWords = WholeWords;
	LastSearchReverse = ReverseSearch;
	LastSearchSelFound = SelectFound;
	LastSearchRegexp = Regexp;

	if (strSearchStr.IsEmpty())
		return TRUE;

	// LastSuccessfulReplaceMode=ReplaceMode;

	if (!EdOpt.PersistentBlocks || (SelectFound && !ReplaceMode))
		UnmarkBlock();

	{
		// SaveScreen SaveScr;
		TPreRedrawFuncGuard preRedrawFuncGuard(Editor::PR_EditorShowMsg);
		strMsgStr = strSearchStr;
		InsertQuote(strMsgStr);
		SetCursorType(FALSE, -1);
		Match = 0;
		UserBreak = 0;
		CurPos = CurLine->GetCurPos();
		/*
			$ 16.10.2000 tran
			CurPos увеличивается при следующем поиске
			$ 28.11.2000 SVS
			"О, это не ощибка - это свойство моей программы" :-)
			Новое поведение стало подконтрольным
			$ 21.12.2000 SVS
			- В предыдущем исправлении было задано неверное условие для
			правила EditorF7Rules
			$ 10.06.2001 IS
			- Баг: зачем-то при продолжении _обратного_ поиска прокручивались на шаг
			_вперед_.
		*/

		/*
			$ 09.11.2001 IS
			проклятое место, блин.
			опять фиксим, т.к. не соответствует заявленному
		*/
		if (!ReverseSearch && Next)
			CurPos++;

		NewNumLine = NumLine;
		CurPtr = CurLine;
		DWORD StartTime = WINPORT(GetTickCount)();
		int StartLine = NumLine;
		wakeful W;

		while (CurPtr) {
			DWORD CurTime = WINPORT(GetTickCount)();

			if (CurTime - StartTime > RedrawTimeout) {
				StartTime = CurTime;

				strMsgStr = strSearchStr;
				InsertQuote(strMsgStr);
				SetCursorType(FALSE, -1);
				int Total = ReverseSearch ? StartLine : NumLastLine - StartLine;
				int Current = abs(NewNumLine - StartLine);
				EditorShowMsg(Msg::EditSearchTitle, Msg::EditSearchingFor, strMsgStr, ToPercent64(Current, Total));

				if (CheckForEscSilent()) {
					if (ConfirmAbortOp()) {
						UserBreak = TRUE;
						break;
					}
				}
			}

			int SearchLength = 0;
			FARString strReplaceStrCurrent(ReplaceMode ? strReplaceStr : L"");
			if (Regexp) {
				ReplaceStrings(strReplaceStrCurrent, L"\\r", L"\r");
				ReplaceStrings(strReplaceStrCurrent, L"\\n", L"\r");
				ReplaceStrings(strReplaceStrCurrent, L"\\t", L"\t");
			}

			if (CurPtr->Search(strSearchStr, strReplaceStrCurrent, CurPos, Case, WholeWords, ReverseSearch,
						Regexp, &SearchLength)) {
				if (SelectFound && !ReplaceMode) {
					Pasting++;
					Lock();
					UnmarkBlock();
					Flags.Set(FEDITOR_MARKINGBLOCK);
					int iFoundPos = CurPtr->GetCurPos();
					CurPtr->Select(iFoundPos, iFoundPos + SearchLength);
					BlockStart = CurPtr;
					BlockStartLine = NewNumLine;
					Unlock();
					Pasting--;
				}

				int Skip = FALSE;
				/*
					$ 24.01.2003 KM
					! По окончании поиска отступим от верха экрана на треть отображаемой высоты.
					$ 15.04.2003 VVM
					Отступим на четверть и проверим на перекрытие диалогом замены
				*/
				int FromTop = (ScrY - 2) / 4;

				if (FromTop < 0 || FromTop >= ((ScrY - 5) / 2 - 2))
					FromTop = 0;

				TmpPtr = CurLine = CurPtr;

				for (int i = 0; i < FromTop; i++) {
					if (TmpPtr->m_prev)
						TmpPtr = TmpPtr->m_prev;
					else
						break;
				}

				TopScreen = TmpPtr;
				NumLine = NewNumLine;
				int LeftPos = CurPtr->GetLeftPos();
				int CellCurPos = CurPtr->GetCellCurPos();

				if (ObjWidth > 8 && CellCurPos - LeftPos + SearchLength > ObjWidth - 8)
					CurPtr->SetLeftPos(CellCurPos + SearchLength - ObjWidth + 8);

				if (ReplaceMode) {
					int MsgCode = 0;

					if (!ReplaceAll) {
						Show();
						SHORT CurX, CurY;
						GetCursorPos(CurX, CurY);
						ScrBuf.ApplyColor(CurX, CurY,
								CurPtr->RealPosToCell(CurPtr->CellPosToReal(CurX) + SearchLength) - 1, CurY,
								FarColorToReal(COL_EDITORSELECTEDTEXT));
						FARString strQSearchStr(CurPtr->GetStringAddr() + CurPtr->GetCurPos(), SearchLength),
								strQReplaceStr = strReplaceStrCurrent;
						InsertQuote(strQSearchStr);
						InsertQuote(strQReplaceStr);
						ReplaceStrings(strQReplaceStr, L"\r", L"\x2424"); // ␤
						ReplaceStrings(strQReplaceStr, L"\t", L"\x2192"); // →
						PreRedrawItem pitem = PreRedraw.Pop();
						MsgCode = Message(0, 4, Msg::EditReplaceTitle, Msg::EditAskReplace, strQSearchStr,
								Msg::EditAskReplaceWith, strQReplaceStr, Msg::EditReplace,
								Msg::EditReplaceAll, Msg::EditSkip, Msg::EditCancel);
						PreRedraw.Push(pitem);

						if (MsgCode == 1)
							ReplaceAll = TRUE;

						if (MsgCode == 2)
							Skip = TRUE;

						if (MsgCode < 0 || MsgCode == 3) {
							UserBreak = TRUE;
							break;
						}
					}

					if (!MsgCode || MsgCode == 1) {
						Pasting++;

						/*
							$ 15.08.2000 skv
							If Replace FARString doesn't contain control symbols (tab and return),
							processed with fast method, otherwise use improved old one.
						*/
						if (strReplaceStrCurrent.Contains(L'\t') || strReplaceStrCurrent.Contains(L'\r')) {
							int SaveOvertypeMode = Flags.Check(FEDITOR_OVERTYPE);
							Flags.Set(FEDITOR_OVERTYPE);
							CurLine->SetOvertypeMode(TRUE);
							// int CurPos=CurLine->GetCurPos();

							int I = 0;
							for (; SearchLength && strReplaceStrCurrent[I]; I++, SearchLength--) {
								unsigned int Ch = strReplaceStrCurrent[I];

								if (Ch == KEY_TAB) {
									Flags.Clear(FEDITOR_OVERTYPE);
									CurLine->SetOvertypeMode(FALSE);
									ProcessKey(KEY_DEL);
									ProcessKey(KEY_TAB);
									Flags.Set(FEDITOR_OVERTYPE);
									CurLine->SetOvertypeMode(TRUE);
									continue;
								}

								/*
									$ 24.05.2002 SKV
									Если реплэйсим на Enter, то overtype не спасёт.
									Нужно сначала удалить то, что заменяем.
								*/
								if (Ch == L'\r') {
									ProcessKey(KEY_DEL);
								}

								if (Ch != KEY_BS && !(Ch == KEY_DEL || Ch == KEY_NUMDEL))
									ProcessKey(Ch);
							}

							if (!SearchLength) {
								Flags.Clear(FEDITOR_OVERTYPE);
								CurLine->SetOvertypeMode(FALSE);

								for (; strReplaceStrCurrent[I]; I++) {
									unsigned Ch = strReplaceStrCurrent[I];

									if (Ch != KEY_BS && !(Ch == KEY_DEL || Ch == KEY_NUMDEL))
										ProcessKey(Ch);
								}
							} else {
								for (; SearchLength; SearchLength--) {
									ProcessKey(KEY_DEL);
								}
							}

							int Cnt = 0;
							const wchar_t *Tmp = strReplaceStrCurrent;

							while ((Tmp = wcschr(Tmp, L'\r'))) {
								Cnt++;
								Tmp++;
							}

							if (Cnt > 0) {
								CurPtr = CurLine;
								NewNumLine+= Cnt;
							}

							Flags.Change(FEDITOR_OVERTYPE, SaveOvertypeMode);
						} else {
							/* Fast method */
							const wchar_t *Str, *Eol;
							int StrLen, NewStrLen;
							int SStrLen = SearchLength, RStrLen = (int)strReplaceStrCurrent.GetLength();
							CurLine->GetBinaryString(&Str, &Eol, StrLen);
							int EolLen = StrLength(Eol);
							NewStrLen = StrLen;
							NewStrLen-= SStrLen;
							NewStrLen+= RStrLen;
							NewStrLen+= EolLen;
							wchar_t *NewStr = new wchar_t[NewStrLen + 1];
							int CurPos = CurLine->GetCurPos();
							wmemcpy(NewStr, Str, CurPos);
							wmemcpy(NewStr + CurPos, strReplaceStrCurrent, RStrLen);
							wmemcpy(NewStr + CurPos + RStrLen, Str + CurPos + SStrLen,
									StrLen - CurPos - SStrLen);
							wmemcpy(NewStr + NewStrLen - EolLen, Eol, EolLen);
							AddUndoData(UNDO_EDIT, CurLine->GetStringAddr(), CurLine->GetEOL(), NumLine,
									CurLine->GetCurPos(), CurLine->GetLength());
							CurLine->SetBinaryString(NewStr, NewStrLen);
							CurLine->SetCurPos(CurPos + RStrLen);

							if (SelectFound && !ReplaceMode) {
								UnmarkBlock();
								Flags.Set(FEDITOR_MARKINGBLOCK);
								CurPtr->Select(CurPos, CurPos + RStrLen);
								BlockStart = CurPtr;
								BlockStartLine = NewNumLine;
							}

							delete[] NewStr;
							TextChanged(1);
						}

						/* skv$*/
						// AY: В этом нет никакой надобности и оно приводит к не правильному
						// позиционированию при Replace
						// if (ReverseSearch)
						// CurLine->SetCurPos(CurPos);
						Pasting--;
					}
				}

				Match = 1;

				if (!ReplaceMode)
					break;

				CurPos = CurLine->GetCurPos();

				if (Skip)
					if (!ReverseSearch)
						CurPos++;
			} else {
				if (ReverseSearch) {
					CurPtr = CurPtr->m_prev;

					if (!CurPtr)
						break;

					CurPos = CurPtr->GetLength();
					NewNumLine--;
				} else {
					CurPos = 0;
					CurPtr = CurPtr->m_next;
					NewNumLine++;
				}
			}
		}
	}
	Show();

	if (!Match && !UserBreak)
		Message(MSG_WARNING, 1, Msg::EditSearchTitle, Msg::EditNotFound, strMsgStr, Msg::Ok);

	return TRUE;
}

void Editor::Paste(const wchar_t *Src)
{
	if (Flags.Check(FEDITOR_LOCKMODE))
		return;

	wchar_t *ClipText = (wchar_t *)Src;
	BOOL IsDeleteClipText = FALSE;

	if (!ClipText) {
		Clipboard clip;

		if (!clip.Open())
			return;

		bool IsVertical = false;
		ClipText = clip.Paste(IsVertical);
		if (ClipText && IsVertical) {
			VPaste(ClipText);
			clip.Close();
			return;
		}

		clip.Close();

		IsDeleteClipText = TRUE;
	}

	if (ClipText && *ClipText) {
		AddUndoData(UNDO_BEGIN);
		Flags.Set(FEDITOR_NEWUNDO);
		TextChanged(1);
		int SaveOvertype = Flags.Check(FEDITOR_OVERTYPE);
		UnmarkBlock();
		Pasting++;
		Lock();

		if (Flags.Check(FEDITOR_OVERTYPE)) {
			Flags.Clear(FEDITOR_OVERTYPE);
			CurLine->SetOvertypeMode(FALSE);
		}

		BlockStart = CurLine;
		BlockStartLine = NumLine;
		/*
			$ 19.05.2001 IS
			Решение проблемы непрошеной конвертации табуляции (которая должна быть
			добавлена в начало строки при автоотступе) в пробелы.
		*/
		int StartPos = CurLine->GetCurPos();
		int oldAutoIndent = EdOpt.AutoIndent;

		for (int I = 0; ClipText[I];) {
			if (ClipText[I] == L'\n' || ClipText[I] == L'\r') {
				CurLine->Select(StartPos, -1);
				StartPos = 0;
				EdOpt.AutoIndent = FALSE;
				ProcessKey(KEY_ENTER);

				if (ClipText[I] == L'\r' && ClipText[I + 1] == L'\n')
					I++;

				I++;
			} else {
				if (EdOpt.AutoIndent)		// первый символ вставим так, чтобы
				{							// сработал автоотступ
											// учтём, что заменять табы на пробелы при вставке не надо

					int OldTabsMode = GetConvertTabs();
					SetConvertTabs(0);

					// ProcessKey(UseDecodeTable?TableSet.DecodeTable[(unsigned)ClipText[I]]:ClipText[I]); //BUGBUG
					ProcessKey(ClipText[I]);	// BUGBUG
					I++;
					StartPos = CurLine->GetCurPos();

					if (StartPos)
						StartPos--;

					SetConvertTabs(OldTabsMode);
				}

				int Pos = I;

				while (ClipText[Pos] && ClipText[Pos] != L'\n' && ClipText[Pos] != L'\r')
					Pos++;

				if (Pos > I) {
					const wchar_t *Str;
					int Length, CurPos;
					CurLine->GetBinaryString(&Str, nullptr, Length);
					CurPos = CurLine->GetCurPos();
					// EOL? - CurLine->GetEOL() GlobalEOL ""
					AddUndoData(UNDO_EDIT, Str, CurLine->GetEOL(), NumLine, CurPos, Length);
					CurLine->InsertBinaryString(&ClipText[I], Pos - I);
				}

				I = Pos;
			}
		}

		EdOpt.AutoIndent = oldAutoIndent;
		CurLine->Select(StartPos, CurLine->GetCurPos());
		/* IS $ */

		if (SaveOvertype) {
			Flags.Set(FEDITOR_OVERTYPE);
			CurLine->SetOvertypeMode(TRUE);
		}

		Pasting--;
		Unlock();
		AddUndoData(UNDO_END);
	}

	if (IsDeleteClipText)
		free(ClipText);
}

void Editor::Copy(int Append)
{
	if (VBlockStart) {
		VCopy(Append);
		return;
	}

	wchar_t *CopyData = nullptr;

	Clipboard clip;

	if (!clip.Open())
		return;

	if (Append)
		CopyData = clip.Paste();

	if ((CopyData = Block2Text(CopyData))) {
		clip.Copy(CopyData);
		free(CopyData);
	}

	clip.Close();
}

wchar_t *Editor::Block2Text(wchar_t *ptrInitData)
{
	size_t DataSize = 0;

	if (ptrInitData)
		DataSize = wcslen(ptrInitData);

	size_t TotalChars = DataSize;
	int StartSel, EndSel;
	for (Edit *Ptr = BlockStart; Ptr; Ptr = Ptr->m_next) {
		Ptr->GetSelection(StartSel, EndSel);
		if (StartSel == -1)
			break;
		if (EndSel == -1) {
			TotalChars+= Ptr->GetLength() - StartSel;
			TotalChars+= wcslen(NATIVE_EOLW);	// CRLF
		} else
			TotalChars+= EndSel - StartSel;
	}
	TotalChars++;	// '\0'

	wchar_t *CopyData = (wchar_t *)malloc(TotalChars * sizeof(wchar_t));

	if (!CopyData) {
		if (ptrInitData)
			free(ptrInitData);

		return nullptr;
	}

	if (ptrInitData) {
		wcscpy(CopyData, ptrInitData);
		free(ptrInitData);
	} else {
		*CopyData = 0;
	}

	for (Edit *Ptr = BlockStart; Ptr; Ptr = Ptr->m_next) {
		Ptr->GetSelection(StartSel, EndSel);
		if (StartSel == -1)
			break;

		int Length;
		if (EndSel == -1)
			Length = Ptr->GetLength() - StartSel;
		else
			Length = EndSel - StartSel;

		Ptr->GetSelString(CopyData + DataSize, Length + 1);
		DataSize+= Length;

		if (EndSel == -1) {
			wcscpy(CopyData + DataSize, NATIVE_EOLW);
			DataSize+= wcslen(NATIVE_EOLW);
		}
	}

	return CopyData;
}

void Editor::DeleteBlock()
{
	if (Flags.Check(FEDITOR_LOCKMODE))
		return;

	if (VBlockStart) {
		DeleteVBlock();
		return;
	}

	Edit *CurPtr = BlockStart;
	AddUndoData(UNDO_BEGIN);

	for (int i = BlockStartLine; CurPtr; i++) {
		TextChanged(1);
		int StartSel, EndSel;
		/*
			$ 17.09.2002 SKV
			меняем на Real что б ловить выделение за концом строки.
		*/
		CurPtr->GetRealSelection(StartSel, EndSel);

		if (EndSel != -1 && EndSel > CurPtr->GetLength())
			EndSel = -1;

		if (StartSel == -1)
			break;

		if (!StartSel && EndSel == -1) {
			Edit *NextLine = CurPtr->m_next;
			DeleteString(CurPtr, i, FALSE, BlockStartLine);

			if (BlockStartLine < NumLine)
				NumLine--;

			if (NextLine) {
				CurPtr = NextLine;
				continue;
			} else
				break;
		}

		int Length = CurPtr->GetLength();

		if (StartSel || EndSel)
			AddUndoData(UNDO_EDIT, CurPtr->GetStringAddr(), CurPtr->GetEOL(), BlockStartLine,
					CurPtr->GetCurPos(), CurPtr->GetLength());

		/*
			$ 17.09.2002 SKV
			опять про выделение за концом строки.
			InsertBinaryString добавит trailing space'ов
		*/
		if (StartSel > Length) {
			Length = StartSel;
			CurPtr->SetCurPos(Length);
			CurPtr->InsertBinaryString(L"", 0);
		}

		const wchar_t *CurStr, *EndSeq;

		CurPtr->GetBinaryString(&CurStr, &EndSeq, Length);

		// дальше будет realloc, поэтому тут malloc.
		wchar_t *TmpStr = (wchar_t *)malloc((Length + 3) * sizeof(wchar_t));

		wmemcpy(TmpStr, CurStr, Length);

		TmpStr[Length] = 0;

		int DeleteNext = FALSE;

		if (EndSel == -1) {
			EndSel = Length;

			if (CurPtr->m_next)
				DeleteNext = TRUE;
		}

		// wmemmove(TmpStr+StartSel,TmpStr+EndSel,StrLength(TmpStr+EndSel)+1);
		wmemmove(TmpStr + StartSel, TmpStr + EndSel, Length - EndSel + 1);
		int CurPos = StartSel;
		/*
		if (CurPos>=StartSel)
		{
			CurPos-=(EndSel-StartSel);
			if (CurPos<StartSel)
				CurPos=StartSel;
		}
		*/
		Length-= EndSel - StartSel;

		if (DeleteNext) {
			const wchar_t *NextStr, *EndSeq;
			int NextLength, NextStartSel, NextEndSel;
			CurPtr->m_next->GetSelection(NextStartSel, NextEndSel);

			if (NextStartSel == -1)
				NextEndSel = 0;

			if (NextEndSel == -1)
				EndSel = -1;
			else {
				CurPtr->m_next->GetBinaryString(&NextStr, &EndSeq, NextLength);
				NextLength-= NextEndSel;

				if (NextLength > 0) {
					TmpStr = (wchar_t *)realloc(TmpStr, (Length + NextLength + 3) * sizeof(wchar_t));
					wmemcpy(TmpStr + Length, NextStr + NextEndSel, NextLength);
					Length+= NextLength;
				}
			}

			if (CurLine == CurPtr->m_next) {
				CurLine = CurPtr;
				NumLine--;
			}

			if (CurLine == CurPtr && CurPtr->m_next && CurPtr->m_next == TopScreen) {
				TopScreen = CurPtr;
			}

			DeleteString(CurPtr->m_next, i, FALSE, BlockStartLine + 1);

			if (BlockStartLine + 1 < NumLine)
				NumLine--;
		}

		int EndLength = StrLength(EndSeq);
		wmemcpy(TmpStr + Length, EndSeq, EndLength);
		Length+= EndLength;
		CurPtr->SetBinaryString(TmpStr, Length);
		free(TmpStr);
		CurPtr->SetCurPos(CurPos);

		if (DeleteNext && EndSel == -1) {
			CurPtr->Select(CurPtr->GetLength(), -1);
		} else {
			CurPtr->Select(-1, 0);
			CurPtr = CurPtr->m_next;
			BlockStartLine++;
		}
	}

	AddUndoData(UNDO_END);
	BlockStart = nullptr;
}

void Editor::UnmarkBlock()
{
	if (!BlockStart && !VBlockStart)
		return;

	VBlockStart = nullptr;
	_SVS(SysLog(L"[%d] Editor::UnmarkBlock()", __LINE__));
	Flags.Clear(FEDITOR_MARKINGVBLOCK | FEDITOR_MARKINGBLOCK);

	while (BlockStart) {
		int StartSel, EndSel;
		BlockStart->GetSelection(StartSel, EndSel);

		if (StartSel == -1) {
			/*
				$ 24.06.2002 SKV
				Если в текущей строки нет выделения,
				это еще не значит что мы в конце.
				Это может быть только начало :)
			*/
			if (BlockStart->m_next) {
				BlockStart->m_next->GetSelection(StartSel, EndSel);

				if (StartSel == -1) {
					break;
				}
			} else
				break;
		}

		BlockStart->Select(-1, 0);
		BlockStart = BlockStart->m_next;
	}

	BlockStart = nullptr;
	Show();
}

/*
	$ 07.03.2002 IS
	Удалить выделение, если оно пустое (выделено ноль символов в ширину)
*/
void Editor::UnmarkEmptyBlock()
{
	_SVS(SysLog(L"[%d] Editor::UnmarkEmptyBlock()", __LINE__));

	if (BlockStart || VBlockStart)		// присутствует выделение
	{
		int Lines = 0, StartSel, EndSel;
		Edit *Block = BlockStart;

		if (VBlockStart) {
			if (VBlockSizeX)
				Lines = VBlockSizeY;
		} else
			while (Block)		// пробегаем по всем выделенным строкам
			{
				Block->GetRealSelection(StartSel, EndSel);

				if (StartSel == -1)
					break;

				if (StartSel != EndSel)		// выделено сколько-то символов
				{
					++Lines;				// увеличим счетчик непустых строк
					break;
				}

				Block = Block->m_next;
			}

		if (!Lines)			// если выделено ноль символов в ширину, то
			UnmarkBlock();	// перестанем морочить голову и снимем выделение
	}
}

void Editor::UnmarkMacroBlock()
{
	MBlockStart = nullptr;
	MBlockStartX = -1;
}

void Editor::GoToLine(int Line)
{
	if (Line != NumLine) {
		bool bReverse = false;
		int LastNumLine = NumLine;
		int CurScrLine = CalcDistance(TopScreen, CurLine, -1);
		int CurPos = CurLine->GetCellCurPos();
		int LeftPos = CurLine->GetLeftPos();

		if (Line < NumLine) {
			if (Line > NumLine / 2) {
				bReverse = true;
			} else {
				CurLine = TopList;
				NumLine = 0;
			}
		} else {
			if (Line > (NumLine + (NumLastLine - NumLine) / 2)) {
				bReverse = true;
				CurLine = EndList;
				NumLine = NumLastLine - 1;
			}
		}

		if (bReverse) {
			for (; NumLine > Line && CurLine->m_prev; NumLine--)
				CurLine = CurLine->m_prev;
		} else {
			for (; NumLine < Line && CurLine->m_next; NumLine++)
				CurLine = CurLine->m_next;
		}

		CurScrLine+= NumLine - LastNumLine;

		if (CurScrLine < 0 || CurScrLine > Y2 - Y1)
			TopScreen = CurLine;

		CurLine->SetLeftPos(LeftPos);
		CurLine->SetCellCurPos(CurPos);
	}

	//<GOTO_UNMARK:2>
	//	if (!EdOpt.PersistentBlocks)
	//		UnmarkBlock();
	//</GOTO_UNMARK>
	Show();
	return;
}

void Editor::GoToPosition()
{
	DialogBuilder Builder(Msg::EditGoToLine, L"EditorGotoPos");
	FARString strData;
	Builder.AddEditField(&strData, 28, L"LineNumber",
			DIF_FOCUS | DIF_HISTORY | DIF_USELASTHISTORY | DIF_NOAUTOCOMPLETE);
	Builder.AddOKCancel();
	Builder.ShowDialog();
	if (!strData.IsEmpty()) {
		int LeftPos = CurLine->GetCellCurPos() + 1;
		int CurPos = CurLine->GetCurPos();

		int NewLine = 0, NewCol = 0;
		GetRowCol(strData, &NewLine, &NewCol);
		GoToLine(NewLine);

		if (NewCol == -1) {
			CurLine->SetCellCurPos(CurPos);
			CurLine->SetLeftPos(LeftPos);
		} else {
			CurLine->SetCellCurPos(NewCol);
		}
		Show();
	}
}

void Editor::GetRowCol(const wchar_t *_argv, int *row, int *col)
{
	int x = 0xffff, y;
	int l;
	wchar_t *argvx = 0;
	int LeftPos = CurLine->GetCellCurPos() + 1;
	FARString strArg = _argv;
	// что бы не оставить "врагу" выбора - только то, что мы хотим ;-)
	// "прибьем" все внешние пробелы.
	RemoveExternalSpaces(strArg);
	wchar_t *argv = strArg.GetBuffer();
	// получаем индекс вхождения любого разделителя
	// в искомой строке
	l = (int)wcscspn(argv, L",:;. ");
	// если разделителя нету, то l=strlen(argv)

	if (l < StrLength(argv))	// Варианты: "row,col" или ",col"?
	{
		argv[l] = L'\0';		// Вместо разделителя впиндюлим "конец строки" :-)
		argvx = argv + l + 1;
		x = _wtoi(argvx);
	}

	y = _wtoi(argv);

	// + переход на проценты
	if (wcschr(argv, L'%'))
		y = NumLastLine * y / 100;

	// вычисляем относительность
	if (argv[0] == L'-' || argv[0] == L'+')
		y = NumLine + y + 1;

	if (argvx) {
		if (argvx[0] == L'-' || argvx[0] == L'+') {
			x = LeftPos + x;
		}
	}

	strArg.ReleaseBuffer();
	// теперь загоним результат назад
	*row = y;

	if (x != 0xffff)
		*col = x;
	else
		*col = LeftPos;

	(*row)--;

	if (*row < 0)			// если ввели ",Col"
		*row = NumLine;		// то переходим на текущую строку и колонку

	(*col)--;

	if (*col < -1)
		*col = -1;

	return;
}

void Editor::AddUndoData(int Type, const wchar_t *Str, const wchar_t *Eol, int StrNum, int StrPos, int Length)
{
	if (Flags.Check(FEDITOR_DISABLEUNDO))
		return;

	if (StrNum == -1)
		StrNum = NumLine;

	for (EditorUndoData *u = UndoData.Next(UndoPos); u;) {
		if (u == UndoSavePos) {
			UndoSavePos = nullptr;
			Flags.Set(FEDITOR_UNDOSAVEPOSLOST);
		}

		EditorUndoData *nu = UndoData.Next(u);
		UndoData.Delete(u);
		u = nu;
	}

	EditorUndoData *PrevUndo = UndoData.Last();

	if (Type == UNDO_END) {
		if (PrevUndo && PrevUndo->Type != UNDO_BEGIN)
			PrevUndo = UndoData.Prev(PrevUndo);

		if (PrevUndo && PrevUndo->Type == UNDO_BEGIN) {
			UndoData.Delete(PrevUndo);
			UndoPos = UndoData.Last();

			if (PrevUndo == UndoSavePos)
				UndoSavePos = UndoPos;

			return;
		}
	}

	if (Type == UNDO_EDIT && !Flags.Check(FEDITOR_NEWUNDO)) {
		if (PrevUndo && PrevUndo->Type == UNDO_EDIT && StrNum == PrevUndo->StrNum
				&& (abs(StrPos - PrevUndo->StrPos) <= 1 || abs(StrPos - LastChangeStrPos) <= 1)) {
			LastChangeStrPos = StrPos;
			return;
		}
	}

	Flags.Clear(FEDITOR_NEWUNDO);
	UndoPos = UndoData.Push();
	UndoPos->SetData(Type, Str, Eol, StrNum, StrPos, Length);

	if (EdOpt.UndoSize > 0) {
		while (!UndoData.Empty()
				&& (UndoData.Count() > static_cast<size_t>(EdOpt.UndoSize) || UndoSkipLevel > 0)) {
			EditorUndoData *u = UndoData.First();

			if (u->Type == UNDO_BEGIN)
				++UndoSkipLevel;

			if (u->Type == UNDO_END && UndoSkipLevel > 0)
				--UndoSkipLevel;

			if (!UndoSavePos)
				Flags.Set(FEDITOR_UNDOSAVEPOSLOST);

			if (u == UndoSavePos)
				UndoSavePos = nullptr;

			UndoData.Delete(u);
		}

		UndoPos = UndoData.Last();
	}
}

void Editor::Undo(int redo)
{
	EditorUndoData *ustart = redo ? UndoData.Next(UndoPos) : UndoPos;

	if (!ustart)
		return;

	TextChanged(1);
	Flags.Set(FEDITOR_DISABLEUNDO);
	int level = 0;
	EditorUndoData *uend;

	for (uend = ustart; uend; uend = redo ? UndoData.Next(uend) : UndoData.Prev(uend)) {
		if (uend->Type == UNDO_BEGIN || uend->Type == UNDO_END) {
			int l = uend->Type == UNDO_BEGIN ? -1 : 1;
			level+= redo ? -l : l;
		}

		if (level <= 0)
			break;
	}

	if (level)
		uend = ustart;

	UnmarkBlock();
	EditorUndoData *ud = ustart;

	for (;;) {
		if (ud->Type != UNDO_BEGIN && ud->Type != UNDO_END)
			GoToLine(ud->StrNum);

		switch (ud->Type) {
			case UNDO_INSSTR:
				ud->SetData(UNDO_DELSTR, CurLine->GetStringAddr(), CurLine->GetEOL(), ud->StrNum, ud->StrPos,
						CurLine->GetLength());
				DeleteString(CurLine, NumLine, TRUE, NumLine > 0 ? NumLine - 1 : NumLine);
				break;
			case UNDO_DELSTR:
				ud->Type = UNDO_INSSTR;
				Pasting++;

				if (NumLine < ud->StrNum) {
					ProcessKey(KEY_END);
					ProcessKey(KEY_ENTER);
				} else {
					ProcessKey(KEY_HOME);
					ProcessKey(KEY_ENTER);
					ProcessKey(KEY_UP);
				}

				Pasting--;

				if (ud->Str) {
					CurLine->SetString(ud->Str, ud->Length);
					CurLine->SetEOL(ud->EOL);	// необходимо дополнительно выставлять, т.к. SetString вызывает Edit::SetBinaryString и... дальше по тексту
				}

				break;
			case UNDO_EDIT: {
				EditorUndoData tmp;
				tmp.SetData(UNDO_EDIT, CurLine->GetStringAddr(), CurLine->GetEOL(), ud->StrNum, ud->StrPos,
						CurLine->GetLength());

				if (ud->Str) {
					CurLine->SetString(ud->Str, ud->Length);
					CurLine->SetEOL(ud->EOL);	// необходимо дополнительно выставлять, т.к. SetString вызывает Edit::SetBinaryString и... дальше по тексту
				}

				CurLine->SetCurPos(ud->StrPos);
				ud->SetData(tmp.Type, tmp.Str, tmp.EOL, tmp.StrNum, tmp.StrPos, tmp.Length);
				break;
			}
		}

		if (ud == uend)
			break;

		ud = redo ? UndoData.Next(ud) : UndoData.Prev(ud);
	}

	UndoPos = redo ? ud : UndoData.Prev(ud);

	if (!Flags.Check(FEDITOR_UNDOSAVEPOSLOST) && UndoPos == UndoSavePos)
		TextChanged(0);

	Flags.Clear(FEDITOR_DISABLEUNDO);
}

void Editor::SelectAll()
{
	Edit *CurPtr;
	BlockStart = TopList;
	BlockStartLine = 0;

	for (CurPtr = TopList; CurPtr; CurPtr = CurPtr->m_next)
		if (CurPtr->m_next)
			CurPtr->Select(0, -1);
		else
			CurPtr->Select(0, CurPtr->GetLength());

	Show();
}

void Editor::SetStartPos(int LineNum, int CharNum)
{
	StartLine = LineNum ? LineNum : 1;
	StartChar = CharNum ? CharNum : 1;
}

BOOL Editor::IsFileChanged() const
{
	return Flags.Check(FEDITOR_MODIFIED | FEDITOR_WASCHANGED);
}

BOOL Editor::IsFileModified() const
{
	return Flags.Check(FEDITOR_MODIFIED);
}

// используется в FileEditor
long Editor::GetCurPos()
{
	Edit *CurPtr = TopList;
	long TotalSize = 0;

	while (CurPtr != TopScreen) {
		const wchar_t *SaveStr, *EndSeq;
		int Length;
		CurPtr->GetBinaryString(&SaveStr, &EndSeq, Length);
		TotalSize+= Length + StrLength(EndSeq);
		CurPtr = CurPtr->m_next;
	}

	return (TotalSize);
}

/*
void Editor::SetStringsTable()
{
	Edit *CurPtr=TopList;
	while (CurPtr)
	{
		CurPtr->SetTables(UseDecodeTable ? &TableSet:nullptr);
		CurPtr=CurPtr->m_next;
	}
}
*/

void Editor::BlockLeft()
{
	if (VBlockStart) {
		VBlockShift(TRUE);
		return;
	}

	Edit *CurPtr = BlockStart;
	int LineNum = BlockStartLine;
	/*
		$ 14.02.2001 VVM
		+ При отсутствии блока AltU/AltI сдвигают текущую строчку
	*/
	int MoveLine = 0;

	if (!CurPtr) {
		MoveLine = 1;
		CurPtr = CurLine;
		LineNum = NumLine;
	}

	AddUndoData(UNDO_BEGIN);

	while (CurPtr) {
		int StartSel, EndSel;
		CurPtr->GetSelection(StartSel, EndSel);

		/*
			$ 14.02.2001 VVM
			+ Блока нет - сделаем его искусственно
		*/
		if (MoveLine) {
			StartSel = 0;
			EndSel = -1;
		}

		if (StartSel == -1)
			break;

		int Length = CurPtr->GetLength();
		wchar_t *TmpStr = new wchar_t[Length + EdOpt.TabSize + 5];
		const wchar_t *CurStr, *EndSeq;
		CurPtr->GetBinaryString(&CurStr, &EndSeq, Length);
		Length--;

		if (*CurStr == L' ')
			wmemcpy(TmpStr, CurStr + 1, Length);
		else if (*CurStr == L'\t') {
			wmemset(TmpStr, L' ', EdOpt.TabSize - 1);
			wmemcpy(TmpStr + EdOpt.TabSize - 1, CurStr + 1, Length);
			Length+= EdOpt.TabSize - 1;
		}

		if ((EndSel == -1 || EndSel > StartSel) && IsSpace(*CurStr)) {
			int EndLength = StrLength(EndSeq);
			wmemcpy(TmpStr + Length, EndSeq, EndLength);
			Length+= EndLength;
			TmpStr[Length] = 0;
			// EOL? - CurLine->GetEOL() GlobalEOL ""
			AddUndoData(UNDO_EDIT, CurStr, CurPtr->GetEOL(), LineNum, 0, CurPtr->GetLength());
			int CurPos = CurPtr->GetCurPos();
			CurPtr->SetBinaryString(TmpStr, Length);
			CurPtr->SetCurPos(CurPos > 0 ? CurPos - 1 : CurPos);

			if (!MoveLine)
				CurPtr->Select(StartSel > 0 ? StartSel - 1 : StartSel, EndSel > 0 ? EndSel - 1 : EndSel);

			TextChanged(1);
		}

		delete[] TmpStr;
		CurPtr = CurPtr->m_next;
		LineNum++;
		MoveLine = 0;
	}

	AddUndoData(UNDO_END);
}

void Editor::BlockRight()
{
	if (VBlockStart) {
		VBlockShift(FALSE);
		return;
	}

	Edit *CurPtr = BlockStart;
	int LineNum = BlockStartLine;
	/*
		$ 14.02.2001 VVM
		+ При отсутствии блока AltU/AltI сдвигают текущую строчку
	*/
	int MoveLine = 0;

	if (!CurPtr) {
		MoveLine = 1;
		CurPtr = CurLine;
		LineNum = NumLine;
	}

	AddUndoData(UNDO_BEGIN);

	while (CurPtr) {
		int StartSel, EndSel;
		CurPtr->GetSelection(StartSel, EndSel);

		/*
			$ 14.02.2001 VVM
			+ Блока нет - сделаем его искусственно
		*/
		if (MoveLine) {
			StartSel = 0;
			EndSel = -1;
		}

		if (StartSel == -1)
			break;

		int Length = CurPtr->GetLength();
		wchar_t *TmpStr = new wchar_t[Length + 5];
		const wchar_t *CurStr, *EndSeq;
		CurPtr->GetBinaryString(&CurStr, &EndSeq, Length);
		*TmpStr = L' ';
		wmemcpy(TmpStr + 1, CurStr, Length);
		Length++;

		if (EndSel == -1 || EndSel > StartSel) {
			int EndLength = StrLength(EndSeq);
			wmemcpy(TmpStr + Length, EndSeq, EndLength);
			TmpStr[Length + EndLength] = 0;
			// EOL? - CurLine->GetEOL() GlobalEOL ""
			AddUndoData(UNDO_EDIT, CurStr, CurPtr->GetEOL(), LineNum, 0, CurPtr->GetLength());
			int CurPos = CurPtr->GetCurPos();

			if (Length > 1)
				CurPtr->SetBinaryString(TmpStr, Length + EndLength);

			CurPtr->SetCurPos(CurPos + 1);

			if (!MoveLine)
				CurPtr->Select(StartSel > 0 ? StartSel + 1 : StartSel, EndSel > 0 ? EndSel + 1 : EndSel);

			TextChanged(1);
		}

		delete[] TmpStr;
		CurPtr = CurPtr->m_next;
		LineNum++;
		MoveLine = 0;
	}

	AddUndoData(UNDO_END);
}

void Editor::DeleteVBlock()
{
	if (Flags.Check(FEDITOR_LOCKMODE) || VBlockSizeX <= 0 || VBlockSizeY <= 0)
		return;

	AddUndoData(UNDO_BEGIN);

	if (!EdOpt.PersistentBlocks) {
		Edit *CurPtr = CurLine;
		Edit *NewTopScreen = TopScreen;

		while (CurPtr) {
			if (CurPtr == VBlockStart) {
				TopScreen = NewTopScreen;
				CurLine = CurPtr;
				CurPtr->SetCellCurPos(VBlockX);
				break;
			}

			NumLine--;

			if (NewTopScreen == CurPtr && CurPtr->m_prev)
				NewTopScreen = CurPtr->m_prev;

			CurPtr = CurPtr->m_prev;
		}
	}

	Edit *CurPtr = VBlockStart;

	for (int Line = 0; CurPtr && Line < VBlockSizeY; Line++, CurPtr = CurPtr->m_next) {
		TextChanged(1);
		int TBlockX = CurPtr->CellPosToReal(VBlockX);
		int TBlockSizeX = CurPtr->CellPosToReal(VBlockX + VBlockSizeX) - CurPtr->CellPosToReal(VBlockX);
		const wchar_t *CurStr, *EndSeq;
		int Length;
		CurPtr->GetBinaryString(&CurStr, &EndSeq, Length);

		if (TBlockX >= Length)
			continue;

		AddUndoData(UNDO_EDIT, CurPtr->GetStringAddr(), CurPtr->GetEOL(), BlockStartLine + Line,
				CurPtr->GetCurPos(), CurPtr->GetLength());
		wchar_t *TmpStr = new wchar_t[Length + 3];
		int CurLength = TBlockX;
		wmemcpy(TmpStr, CurStr, TBlockX);

		if (Length > TBlockX + TBlockSizeX) {
			int CopySize = Length - (TBlockX + TBlockSizeX);
			wmemcpy(TmpStr + CurLength, CurStr + TBlockX + TBlockSizeX, CopySize);
			CurLength+= CopySize;
		}

		int EndLength = StrLength(EndSeq);
		wmemcpy(TmpStr + CurLength, EndSeq, EndLength);
		CurLength+= EndLength;
		int CurPos = CurPtr->GetCurPos();
		CurPtr->SetBinaryString(TmpStr, CurLength);

		if (CurPos > TBlockX) {
			CurPos-= TBlockSizeX;

			if (CurPos < TBlockX)
				CurPos = TBlockX;
		}

		CurPtr->SetCurPos(CurPos);
		delete[] TmpStr;
	}

	AddUndoData(UNDO_END);
	VBlockStart = nullptr;
}

void Editor::VCopy(int Append)
{
	wchar_t *CopyData = nullptr;

	Clipboard clip;

	if (!clip.Open())
		return;

	if (Append)
		CopyData = clip.Paste();

	if ((CopyData = VBlock2Text(CopyData))) {
		clip.Copy(CopyData, true);
		free(CopyData);
	}

	clip.Close();
}

wchar_t *Editor::VBlock2Text(wchar_t *ptrInitData)
{
	size_t DataSize = 0;

	if (ptrInitData)
		DataSize = wcslen(ptrInitData);

	// RealPos всегда <= TabPos, поэтому берём максимальный размер буфера
	size_t TotalChars = DataSize + (VBlockSizeX + wcslen(NATIVE_EOLW)) * VBlockSizeY + 1;

	wchar_t *CopyData = (wchar_t *)malloc(TotalChars * sizeof(wchar_t));

	if (!CopyData) {
		if (ptrInitData)
			free(ptrInitData);

		return nullptr;
	}

	if (ptrInitData) {
		wcscpy(CopyData, ptrInitData);
		free(ptrInitData);
	} else {
		*CopyData = 0;
	}

	Edit *CurPtr = VBlockStart;

	for (int Line = 0; CurPtr && Line < VBlockSizeY; Line++, CurPtr = CurPtr->m_next) {
		int TBlockX = CurPtr->CellPosToReal(VBlockX);
		int TBlockSizeX = CurPtr->CellPosToReal(VBlockX + VBlockSizeX) - TBlockX;
		const wchar_t *CurStr, *EndSeq;
		int Length;
		CurPtr->GetBinaryString(&CurStr, &EndSeq, Length);

		if (Length > TBlockX) {
			int CopySize = Length - TBlockX;

			if (CopySize > TBlockSizeX)
				CopySize = TBlockSizeX;

			wmemcpy(CopyData + DataSize, CurStr + TBlockX, CopySize);

			if (CopySize < TBlockSizeX)
				wmemset(CopyData + DataSize + CopySize, L' ', TBlockSizeX - CopySize);
		} else {
			wmemset(CopyData + DataSize, L' ', TBlockSizeX);
		}

		DataSize+= TBlockSizeX;
		wcscpy(CopyData + DataSize, NATIVE_EOLW);
		DataSize+= wcslen(NATIVE_EOLW);
	}

	return CopyData;
}

void Editor::VPaste(wchar_t *ClipText)
{
	if (Flags.Check(FEDITOR_LOCKMODE))
		return;

	if (*ClipText) {
		AddUndoData(UNDO_BEGIN);
		Flags.Set(FEDITOR_NEWUNDO);
		TextChanged(1);
		int SaveOvertype = Flags.Check(FEDITOR_OVERTYPE);
		UnmarkBlock();
		Pasting++;
		Lock();

		if (Flags.Check(FEDITOR_OVERTYPE)) {
			Flags.Clear(FEDITOR_OVERTYPE);
			CurLine->SetOvertypeMode(FALSE);
		}

		VBlockStart = CurLine;
		BlockStartLine = NumLine;
		int StartPos = CurLine->GetCellCurPos();
		VBlockX = StartPos;
		VBlockSizeX = 0;
		VBlockY = NumLine;
		VBlockSizeY = 0;
		Edit *SavedTopScreen = TopScreen;

		for (int I = 0; ClipText[I]; I++)
			if (ClipText[I] != '\r' && ClipText[I] != '\n') {
				ProcessKey(ClipText[I]);
			} else {
				const size_t EolLength = (ClipText[I] == '\r' || ClipText[I + 1] == '\n') ? 2 : 1;

				int CurWidth = CurLine->GetCellCurPos() - StartPos;

				if (CurWidth > VBlockSizeX)
					VBlockSizeX = CurWidth;

				VBlockSizeY++;

				if (!CurLine->m_next) {
					if (ClipText[I + EolLength]) {
						ProcessKey(KEY_END);
						ProcessKey(KEY_ENTER);

						// Mantis 0002966: Неправильная вставка вертикального блока в конце файла
						for (int I = 0; I < StartPos; I++)
							ProcessKey(L' ');					}
				} else {
					ProcessKey(KEY_DOWN);
					CurLine->SetCellCurPos(StartPos);
					CurLine->SetOvertypeMode(FALSE);
				}

				I+= EolLength - 1;
				continue;
			}

		int CurWidth = CurLine->GetCellCurPos() - StartPos;

		if (CurWidth > VBlockSizeX)
			VBlockSizeX = CurWidth;

		if (!VBlockSizeY)
			VBlockSizeY++;

		if (SaveOvertype) {
			Flags.Set(FEDITOR_OVERTYPE);
			CurLine->SetOvertypeMode(TRUE);
		}

		TopScreen = SavedTopScreen;
		CurLine = VBlockStart;
		NumLine = BlockStartLine;
		CurLine->SetCellCurPos(StartPos);
		Pasting--;
		Unlock();
		AddUndoData(UNDO_END);
	}

	free(ClipText);
}

void Editor::VBlockShift(int Left)
{
	if (Flags.Check(FEDITOR_LOCKMODE) || (Left && !VBlockX) || VBlockSizeX <= 0 || VBlockSizeY <= 0)
		return;

	Edit *CurPtr = VBlockStart;
	AddUndoData(UNDO_BEGIN);

	for (int Line = 0; CurPtr && Line < VBlockSizeY; Line++, CurPtr = CurPtr->m_next) {
		TextChanged(1);
		int TBlockX = CurPtr->CellPosToReal(VBlockX);
		int TBlockSizeX = CurPtr->CellPosToReal(VBlockX + VBlockSizeX) - CurPtr->CellPosToReal(VBlockX);
		const wchar_t *CurStr, *EndSeq;
		int Length;
		CurPtr->GetBinaryString(&CurStr, &EndSeq, Length);

		if (TBlockX > Length)
			continue;

		if ((Left && CurStr[TBlockX - 1] == L'\t')
				|| (!Left && TBlockX + TBlockSizeX < Length && CurStr[TBlockX + TBlockSizeX] == L'\t')) {
			CurPtr->ExpandTabs();
			CurPtr->GetBinaryString(&CurStr, &EndSeq, Length);
			TBlockX = CurPtr->CellPosToReal(VBlockX);
			TBlockSizeX = CurPtr->CellPosToReal(VBlockX + VBlockSizeX) - CurPtr->CellPosToReal(VBlockX);
		}

		AddUndoData(UNDO_EDIT, CurPtr->GetStringAddr(), CurPtr->GetEOL(), BlockStartLine + Line,
				CurPtr->GetCurPos(), CurPtr->GetLength());
		int StrLen = Max(Length, TBlockX + TBlockSizeX + !Left);
		wchar_t *TmpStr = new wchar_t[StrLen + 3];
		wmemset(TmpStr, L' ', StrLen);
		wmemcpy(TmpStr, CurStr, Length);

		if (Left) {
			WCHAR Ch = TmpStr[TBlockX - 1];

			for (int I = TBlockX; I < TBlockX + TBlockSizeX; I++)
				TmpStr[I - 1] = TmpStr[I];

			TmpStr[TBlockX + TBlockSizeX - 1] = Ch;
		} else {
			int Ch = TmpStr[TBlockX + TBlockSizeX];

			for (int I = TBlockX + TBlockSizeX - 1; I >= TBlockX; I--)
				TmpStr[I + 1] = TmpStr[I];

			TmpStr[TBlockX] = Ch;
		}

		while (StrLen > 0 && TmpStr[StrLen - 1] == L' ')
			StrLen--;

		int EndLength = StrLength(EndSeq);
		wmemcpy(TmpStr + StrLen, EndSeq, EndLength);
		StrLen+= EndLength;
		CurPtr->SetBinaryString(TmpStr, StrLen);
		delete[] TmpStr;
	}

	VBlockX+= Left ? -1 : 1;
	CurLine->SetCellCurPos(Left ? VBlockX : VBlockX + VBlockSizeX);
	AddUndoData(UNDO_END);
}

int Editor::EditorControl(int Command, void *Param)
{
	_ECTLLOG(CleverSysLog SL(L"Editor::EditorControl()"));
	_ECTLLOG(SysLog(L"Command=%ls Param=[%d/0x%08X]", _ECTL_ToName(Command), Param, Param));

	switch (Command) {
		case ECTL_GETSTRING: {
			EditorGetString *GetString = (EditorGetString *)Param;

			if (GetString) {
				Edit *CurPtr = GetStringByNumber(GetString->StringNumber);

				if (!CurPtr) {
					_ECTLLOG(SysLog(L"EditorGetString => GetStringByNumber(%d) return nullptr",
							GetString->StringNumber));
					return FALSE;
				}

				CurPtr->GetBinaryString(const_cast<const wchar_t **>(&GetString->StringText),
						const_cast<const wchar_t **>(&GetString->StringEOL), GetString->StringLength);
				GetString->SelStart = -1;
				GetString->SelEnd = 0;
				int DestLine = GetString->StringNumber;

				if (DestLine == -1)
					DestLine = NumLine;

				if (BlockStart) {
					CurPtr->GetRealSelection(GetString->SelStart, GetString->SelEnd);
				} else if (VBlockStart && DestLine >= VBlockY && DestLine < VBlockY + VBlockSizeY) {
					GetString->SelStart = CurPtr->CellPosToReal(VBlockX);
					GetString->SelEnd = GetString->SelStart + CurPtr->CellPosToReal(VBlockX + VBlockSizeX)
							- CurPtr->CellPosToReal(VBlockX);
				}

				_ECTLLOG(SysLog(L"EditorGetString{"));
				_ECTLLOG(SysLog(L"  StringNumber    =%d", GetString->StringNumber));
				_ECTLLOG(SysLog(L"  StringText      ='%ls'", GetString->StringText));
				_ECTLLOG(SysLog(L"  StringEOL       ='%ls'",
						GetString->StringEOL ? _SysLog_LinearDump((LPBYTE)GetString->StringEOL,
								StrLength(GetString->StringEOL))
											: L"(null)"));
				_ECTLLOG(SysLog(L"  StringLength    =%d", GetString->StringLength));
				_ECTLLOG(SysLog(L"  SelStart        =%d", GetString->SelStart));
				_ECTLLOG(SysLog(L"  SelEnd          =%d", GetString->SelEnd));
				_ECTLLOG(SysLog(L"}"));
				return TRUE;
			}

			break;
		}
		case ECTL_INSERTSTRING: {
			if (Flags.Check(FEDITOR_LOCKMODE)) {
				_ECTLLOG(SysLog(L"FEDITOR_LOCKMODE!"));
				return FALSE;
			} else {
				int Indent = Param && *(int *)Param != FALSE;

				if (!Indent)
					Pasting++;

				Flags.Set(FEDITOR_NEWUNDO);
				InsertString();

				if (!Indent)
					Pasting--;
			}

			return TRUE;
		}
		case ECTL_INSERTTEXT: {
			if (!Param)
				return FALSE;

			_ECTLLOG(SysLog(L"(const wchar_t *)Param='%ls'", (const wchar_t *)Param));

			if (Flags.Check(FEDITOR_LOCKMODE)) {
				_ECTLLOG(SysLog(L"FEDITOR_LOCKMODE!"));
				return FALSE;
			} else {
				const wchar_t *Str = (const wchar_t *)Param;
				Pasting++;
				Lock();

				while (*Str)
					ProcessKey(*(Str++));

				Unlock();
				Pasting--;
			}

			return TRUE;
		}
		case ECTL_SETSTRING: {
			EditorSetString *SetString = (EditorSetString *)Param;

			if (!SetString)
				break;

			_ECTLLOG(SysLog(L"EditorSetString{"));
			_ECTLLOG(SysLog(L"  StringNumber    =%d", SetString->StringNumber));
			_ECTLLOG(SysLog(L"  StringText      ='%ls'", SetString->StringText));
			_ECTLLOG(SysLog(L"  StringEOL       ='%ls'",
					SetString->StringEOL ? _SysLog_LinearDump((LPBYTE)SetString->StringEOL,
							StrLength(SetString->StringEOL))
										: L"(null)"));
			_ECTLLOG(SysLog(L"  StringLength    =%d", SetString->StringLength));
			_ECTLLOG(SysLog(L"}"));

			if (Flags.Check(FEDITOR_LOCKMODE)) {
				_ECTLLOG(SysLog(L"FEDITOR_LOCKMODE!"));
				break;
			} else {
				/*
					$ 06.08.2002 IS
					Проверяем корректность StringLength и вернем FALSE, если оно меньше нуля.
				*/
				int Length = SetString->StringLength;

				if (Length < 0) {
					_ECTLLOG(SysLog(L"SetString->StringLength < 0"));
					return FALSE;
				}

				Edit *CurPtr = GetStringByNumber(SetString->StringNumber);

				if (!CurPtr) {
					_ECTLLOG(SysLog(L"GetStringByNumber(%d) return nullptr", SetString->StringNumber));
					return FALSE;
				}

				const wchar_t *EOL = SetString->StringEOL ? SetString->StringEOL : GlobalEOL;

				int LengthEOL = StrLength(EOL);

				wchar_t *NewStr = (wchar_t *)malloc((Length + LengthEOL + 1) * sizeof(wchar_t));

				if (!NewStr) {
					_ECTLLOG(SysLog(L"malloc(%d) return nullptr", Length + LengthEOL + 1));
					return FALSE;
				}

				int DestLine = SetString->StringNumber;

				if (DestLine == -1)
					DestLine = NumLine;

				wmemcpy(NewStr, SetString->StringText, Length);
				wmemcpy(NewStr + Length, EOL, LengthEOL);
				AddUndoData(UNDO_EDIT, CurPtr->GetStringAddr(), CurPtr->GetEOL(), DestLine,
						CurPtr->GetCurPos(), CurPtr->GetLength());
				int CurPos = CurPtr->GetCurPos();
				CurPtr->SetBinaryString(NewStr, Length + LengthEOL);
				CurPtr->SetCurPos(CurPos);
				TextChanged(1);		// 10.08.2000 skv - Modified->TextChanged
				free(NewStr);
			}

			return TRUE;
		}
		case ECTL_DELETESTRING: {
			if (Flags.Check(FEDITOR_LOCKMODE)) {
				_ECTLLOG(SysLog(L"FEDITOR_LOCKMODE!"));
				return FALSE;
			}

			DeleteString(CurLine, NumLine, FALSE, NumLine);
			return TRUE;
		}
		case ECTL_DELETECHAR: {
			if (Flags.Check(FEDITOR_LOCKMODE)) {
				_ECTLLOG(SysLog(L"FEDITOR_LOCKMODE!"));
				return FALSE;
			}

			Pasting++;
			ProcessKey(KEY_DEL);
			Pasting--;
			return TRUE;
		}
		case ECTL_GETINFO: {
			EditorInfo *Info = (EditorInfo *)Param;

			if (Info) {
				Info->EditorID = Editor::EditorID;
				Info->WindowSizeX = ObjWidth;
				Info->WindowSizeY = Y2 - Y1 + 1;
				Info->TotalLines = NumLastLine;
				Info->CurLine = NumLine;
				Info->CurPos = CurLine->GetCurPos();
				Info->CurTabPos = CurLine->GetCellCurPos();
				Info->TopScreenLine = NumLine - CalcDistance(TopScreen, CurLine, -1);
				Info->LeftPos = CurLine->GetLeftPos();
				Info->Overtype = Flags.Check(FEDITOR_OVERTYPE);
				Info->BlockType = VBlockStart ? BTYPE_COLUMN : BlockStart ? BTYPE_STREAM : BTYPE_NONE;
				Info->BlockStartLine = Info->BlockType == BTYPE_NONE ? 0 : BlockStartLine;
				Info->Options = 0;

				if (EdOpt.ExpandTabs == EXPAND_ALLTABS)
					Info->Options|= EOPT_EXPANDALLTABS;

				if (EdOpt.ExpandTabs == EXPAND_NEWTABS)
					Info->Options|= EOPT_EXPANDONLYNEWTABS;

				if (EdOpt.PersistentBlocks)
					Info->Options|= EOPT_PERSISTENTBLOCKS;

				if (EdOpt.DelRemovesBlocks)
					Info->Options|= EOPT_DELREMOVESBLOCKS;

				if (EdOpt.AutoIndent)
					Info->Options|= EOPT_AUTOINDENT;

				if (EdOpt.SavePos)
					Info->Options|= EOPT_SAVEFILEPOSITION;

				if (EdOpt.AutoDetectCodePage)
					Info->Options|= EOPT_AUTODETECTCODEPAGE;

				if (EdOpt.CursorBeyondEOL)
					Info->Options|= EOPT_CURSORBEYONDEOL;

				if (EdOpt.ShowWhiteSpace)
					Info->Options|= EOPT_SHOWWHITESPACE;

				Info->TabSize = EdOpt.TabSize;
				Info->BookMarkCount = POSCACHE_BOOKMARK_COUNT;
				Info->CurState = Flags.Check(FEDITOR_LOCKMODE) ? ECSTATE_LOCKED : 0;
				Info->CurState|= !Flags.Check(FEDITOR_MODIFIED) ? ECSTATE_SAVED : 0;
				Info->CurState|= Flags.Check(FEDITOR_MODIFIED | FEDITOR_WASCHANGED) ? ECSTATE_MODIFIED : 0;
				Info->CodePage = m_codepage;
				return TRUE;
			}

			_ECTLLOG(SysLog(L"Error: !Param"));
			return FALSE;
		}
		case ECTL_SETPOSITION: {
			// "Вначале было слово..."
			if (Param) {
				// ...а вот теперь поработаем с тем, что передалаи
				EditorSetPosition *Pos = (EditorSetPosition *)Param;
				_ECTLLOG(SysLog(L"EditorSetPosition{"));
				_ECTLLOG(SysLog(L"  CurLine       = %d", Pos->CurLine));
				_ECTLLOG(SysLog(L"  CurPos        = %d", Pos->CurPos));
				_ECTLLOG(SysLog(L"  CurTabPos     = %d", Pos->CurTabPos));
				_ECTLLOG(SysLog(L"  TopScreenLine = %d", Pos->TopScreenLine));
				_ECTLLOG(SysLog(L"  LeftPos       = %d", Pos->LeftPos));
				_ECTLLOG(SysLog(L"  Overtype      = %d", Pos->Overtype));
				_ECTLLOG(SysLog(L"}"));
				Lock();
				int CurPos = CurLine->GetCurPos();

				// выставим флаг об изменении поз (если надо)
				if ((Pos->CurLine >= 0 || Pos->CurPos >= 0)
						&& (Pos->CurLine != NumLine || Pos->CurPos != CurPos))
					Flags.Set(FEDITOR_CURPOSCHANGEDBYPLUGIN);

				if (Pos->CurLine >= 0)		// поменяем строку
				{
					if (Pos->CurLine == NumLine - 1)
						Up();
					else if (Pos->CurLine == NumLine + 1)
						Down();
					else
						GoToLine(Pos->CurLine);
				}

				if (Pos->TopScreenLine >= 0 && Pos->TopScreenLine <= NumLine) {
					TopScreen = CurLine;

					for (int I = NumLine; I > 0 && NumLine - I < Y2 - Y1 && I != Pos->TopScreenLine; I--)
						TopScreen = TopScreen->m_prev;
				}

				if (Pos->CurPos >= 0)
					CurLine->SetCurPos(Pos->CurPos);

				if (Pos->CurTabPos >= 0)
					CurLine->SetCellCurPos(Pos->CurTabPos);

				if (Pos->LeftPos >= 0)
					CurLine->SetLeftPos(Pos->LeftPos);

				/*
					$ 30.08.2001 IS
					Изменение режима нужно выставлять сразу, в противном случае приходят
					глюки, т.к. плагинописатель думает, что режим изменен, и ведет себя
					соответствующе, в результате чего получает неопределенное поведение.
				*/
				if (Pos->Overtype >= 0) {
					Flags.Change(FEDITOR_OVERTYPE, Pos->Overtype);
					CurLine->SetOvertypeMode(Flags.Check(FEDITOR_OVERTYPE));
				}

				Unlock();
				return TRUE;
			}

			_ECTLLOG(SysLog(L"Error: !Param"));
			break;
		}
		case ECTL_SELECT: {
			if (Param) {
				EditorSelect *Sel = (EditorSelect *)Param;
				_ECTLLOG(SysLog(L"EditorSelect{"));
				_ECTLLOG(SysLog(L"  BlockType     =%ls (%d)",
						(Sel->BlockType == BTYPE_NONE
										? L"BTYPE_NONE"
										: (Sel->BlockType == BTYPE_STREAM
														? L""
														: (Sel->BlockType == BTYPE_COLUMN ? L"BTYPE_COLUMN"
																						: L"BTYPE_?????"))),
						Sel->BlockType));
				_ECTLLOG(SysLog(L"  BlockStartLine=%d", Sel->BlockStartLine));
				_ECTLLOG(SysLog(L"  BlockStartPos =%d", Sel->BlockStartPos));
				_ECTLLOG(SysLog(L"  BlockWidth    =%d", Sel->BlockWidth));
				_ECTLLOG(SysLog(L"  BlockHeight   =%d", Sel->BlockHeight));
				_ECTLLOG(SysLog(L"}"));

				if (Sel->BlockType == BTYPE_NONE || Sel->BlockStartPos == -1) {
					UnmarkBlock();
					return TRUE;
				}

				if (Sel->BlockHeight < 1) {
					_ECTLLOG(SysLog(L"Error: EditorSelect::BlockHeight < 1"));
					return FALSE;
				}

				Edit *CurPtr = GetStringByNumber(Sel->BlockStartLine);

				if (!CurPtr) {
					_ECTLLOG(SysLog(L"Error: start line BlockStartLine=%d not found, GetStringByNumber(%d) "
									L"return nullptr",
							Sel->BlockStartLine, Sel->BlockStartLine));
					return FALSE;
				}

				UnmarkBlock();

				if (Sel->BlockType == BTYPE_STREAM) {
					Flags.Set(FEDITOR_MARKINGBLOCK);
					BlockStart = CurPtr;

					if ((BlockStartLine = Sel->BlockStartLine) == -1)
						BlockStartLine = NumLine;

					for (int i = 0; i < Sel->BlockHeight; i++) {
						int SelStart = i ? 0 : Sel->BlockStartPos;
						int SelEnd = (i < Sel->BlockHeight - 1) ? -1 : Sel->BlockStartPos + Sel->BlockWidth;
						CurPtr->Select(SelStart, SelEnd);
						CurPtr = CurPtr->m_next;

						if (!CurPtr)
							return TRUE;	// ранее было FALSE
					}
				} else if (Sel->BlockType == BTYPE_COLUMN) {
					Flags.Set(FEDITOR_MARKINGVBLOCK);
					VBlockStart = CurPtr;

					if ((BlockStartLine = Sel->BlockStartLine) == -1)
						BlockStartLine = NumLine;

					VBlockX = Sel->BlockStartPos;

					if ((VBlockY = Sel->BlockStartLine) == -1)
						VBlockY = NumLine;

					VBlockSizeX = Sel->BlockWidth;
					VBlockSizeY = Sel->BlockHeight;

					if (VBlockSizeX < 0) {
						VBlockSizeX = -VBlockSizeX;
						VBlockX-= VBlockSizeX;

						if (VBlockX < 0)
							VBlockX = 0;
					}
				}

				return TRUE;
			}

			_ECTLLOG(SysLog(L"Error: !Param"));
			break;
		}
		case ECTL_REDRAW: {
			Show();
			ScrBuf.Flush();
			return TRUE;
		}
		case ECTL_TABTOREAL: {
			if (Param) {
				EditorConvertPos *ecp = (EditorConvertPos *)Param;
				Edit *CurPtr = GetStringByNumber(ecp->StringNumber);

				if (!CurPtr) {
					_ECTLLOG(SysLog(L"GetStringByNumber(%d) return nullptr", ecp->StringNumber));
					return FALSE;
				}

				ecp->DestPos = CurPtr->CellPosToReal(ecp->SrcPos);
				_ECTLLOG(SysLog(L"EditorConvertPos{"));
				_ECTLLOG(SysLog(L"  StringNumber =%d", ecp->StringNumber));
				_ECTLLOG(SysLog(L"  SrcPos       =%d", ecp->SrcPos));
				_ECTLLOG(SysLog(L"  DestPos      =%d", ecp->DestPos));
				_ECTLLOG(SysLog(L"}"));
				return TRUE;
			}

			break;
		}
		case ECTL_REALTOTAB: {
			if (Param) {
				EditorConvertPos *ecp = (EditorConvertPos *)Param;
				Edit *CurPtr = GetStringByNumber(ecp->StringNumber);

				if (!CurPtr) {
					_ECTLLOG(SysLog(L"GetStringByNumber(%d) return nullptr", ecp->StringNumber));
					return FALSE;
				}

				ecp->DestPos = CurPtr->RealPosToCell(ecp->SrcPos);
				_ECTLLOG(SysLog(L"EditorConvertPos{"));
				_ECTLLOG(SysLog(L"  StringNumber =%d", ecp->StringNumber));
				_ECTLLOG(SysLog(L"  SrcPos       =%d", ecp->SrcPos));
				_ECTLLOG(SysLog(L"  DestPos      =%d", ecp->DestPos));
				_ECTLLOG(SysLog(L"}"));
				return TRUE;
			}

			break;
		}
		case ECTL_EXPANDTABS: {
			if (Flags.Check(FEDITOR_LOCKMODE)) {
				_ECTLLOG(SysLog(L"FEDITOR_LOCKMODE!"));
				return FALSE;
			} else {
				int StringNumber = *(int *)Param;
				Edit *CurPtr = GetStringByNumber(StringNumber);

				if (!CurPtr) {
					_ECTLLOG(SysLog(L"GetStringByNumber(%d) return nullptr", StringNumber));
					return FALSE;
				}

				AddUndoData(UNDO_EDIT, CurPtr->GetStringAddr(), CurPtr->GetEOL(), StringNumber,
						CurPtr->GetCurPos(), CurPtr->GetLength());
				CurPtr->ExpandTabs();
			}

			return TRUE;
		}
		// TODO: Если DI_MEMOEDIT не будет юзать раскаску, то должно выполняется в FileEditor::EditorControl(), в диалоге - нафиг ненать
		case ECTL_ADDTRUECOLOR:
		case ECTL_ADDCOLOR: {
			if (Param) {
				const EditorColor *col = (EditorColor *)Param;
				_ECTLLOG(SysLog(L"EditorColor{"));
				_ECTLLOG(SysLog(L"  StringNumber=%d", col->StringNumber));
				_ECTLLOG(SysLog(L"  ColorItem   =%d (0x%08X)", col->ColorItem, col->ColorItem));
				_ECTLLOG(SysLog(L"  StartPos    =%d", col->StartPos));
				_ECTLLOG(SysLog(L"  EndPos      =%d", col->EndPos));
				_ECTLLOG(SysLog(L"  Color       =%d (0x%08X)", col->Color, col->Color));
				_ECTLLOG(SysLog(L"}"));
				ColorItem newcol{0};
				newcol.StartPos = col->StartPos + (col->StartPos != -1 ? X1 : 0);
				newcol.EndPos = col->EndPos + X1;
				newcol.Color = col->Color;
				Edit *CurPtr = GetStringByNumber(col->StringNumber);

				if (!CurPtr) {
					_ECTLLOG(SysLog(L"GetStringByNumber(%d) return nullptr", col->StringNumber));
					return FALSE;
				}

				if (!col->Color)
					return (CurPtr->DeleteColor(newcol.StartPos));

				if (Command == ECTL_ADDTRUECOLOR) {
					const EditorTrueColor *tcol = (EditorTrueColor *)Param;
					FarTrueColorToAttributes(newcol.Color, tcol->TrueColor);
				}
				CurPtr->AddColor(&newcol);
				return TRUE;
			}

			break;
		}
		// TODO: Если DI_MEMOEDIT не будет юзать раскаску, то должно выполняется в FileEditor::EditorControl(), в диалоге - нафиг ненать
		case ECTL_GETTRUECOLOR:
		case ECTL_GETCOLOR: {
			if (Param) {
				EditorColor *col = (EditorColor *)Param;
				Edit *CurPtr = GetStringByNumber(col->StringNumber);

				if (!CurPtr) {
					_ECTLLOG(SysLog(L"GetStringByNumber(%d) return nullptr", col->StringNumber));
					return FALSE;
				}

				ColorItem curcol;

				if (!CurPtr->GetColor(&curcol, col->ColorItem)) {
					_ECTLLOG(SysLog(L"GetColor() return nullptr"));
					return FALSE;
				}

				col->StartPos = curcol.StartPos - X1;
				col->EndPos = curcol.EndPos - X1;
				col->Color = curcol.Color & 0xffff;
				if (Command == ECTL_GETTRUECOLOR) {
					EditorTrueColor *tcol = (EditorTrueColor *)Param;
					FarTrueColorFromAttributes(tcol->TrueColor, curcol.Color);
				}
				_ECTLLOG(SysLog(L"EditorColor{"));
				_ECTLLOG(SysLog(L"  StringNumber=%d", col->StringNumber));
				_ECTLLOG(SysLog(L"  ColorItem   =%d (0x%08X)", col->ColorItem, col->ColorItem));
				_ECTLLOG(SysLog(L"  StartPos    =%d", col->StartPos));
				_ECTLLOG(SysLog(L"  EndPos      =%d", col->EndPos));
				_ECTLLOG(SysLog(L"  Color       =%d (0x%08X)", col->Color, col->Color));
				_ECTLLOG(SysLog(L"}"));
				return TRUE;
			}

			break;
		}
		// должно выполняется в FileEditor::EditorControl()
		case ECTL_PROCESSKEY: {
			_ECTLLOG(SysLog(L"Key = %ls", _FARKEY_ToName((DWORD)Param)));
			ProcessKey((int)(INT_PTR)Param);
			return TRUE;
		}
		/*
			$ 16.02.2001 IS
			Изменение некоторых внутренних настроек редактора. Param указывает на
			структуру EditorSetParameter
		*/
		case ECTL_SETPARAM: {
			if (Param) {
				EditorSetParameter *espar = (EditorSetParameter *)Param;
				int rc = TRUE;
				_ECTLLOG(SysLog(L"EditorSetParameter{"));
				_ECTLLOG(SysLog(L"  Type        =%ls", _ESPT_ToName(espar->Type)));

				switch (espar->Type) {
					case ESPT_GETWORDDIV:
						_ECTLLOG(SysLog(L"  wszParam    =(%p)", espar->Param.wszParam));

						if (espar->Param.wszParam && espar->Size)
							far_wcsncpy(espar->Param.wszParam, EdOpt.strWordDiv, espar->Size);

						rc = (int)EdOpt.strWordDiv.GetLength() + 1;
						break;
					case ESPT_SETWORDDIV:
						_ECTLLOG(SysLog(L"  wszParam    =[%ls]", espar->Param.wszParam));
						SetWordDiv((!espar->Param.wszParam || !*espar->Param.wszParam)
										? Opt.strWordDiv.CPtr()
										: espar->Param.wszParam);
						break;
					case ESPT_TABSIZE:
						_ECTLLOG(SysLog(L"  iParam      =%d", espar->Param.iParam));
						SetTabSize(espar->Param.iParam);
						break;
					case ESPT_EXPANDTABS:
						_ECTLLOG(SysLog(L"  iParam      =%ls", espar->Param.iParam ? L"On" : L"Off"));
						SetConvertTabs(espar->Param.iParam);
						break;
					case ESPT_AUTOINDENT:
						_ECTLLOG(SysLog(L"  iParam      =%ls", espar->Param.iParam ? L"On" : L"Off"));
						SetAutoIndent(espar->Param.iParam);
						break;
					case ESPT_CURSORBEYONDEOL:
						_ECTLLOG(SysLog(L"  iParam      =%ls", espar->Param.iParam ? L"On" : L"Off"));
						SetCursorBeyondEOL(espar->Param.iParam);
						break;
					case ESPT_CHARCODEBASE:
						_ECTLLOG(SysLog(L"  iParam      =%ls",
								(!espar->Param.iParam
												? L"0 (Oct)"
												: (espar->Param.iParam == 1
																? L"1 (Dec)"
																: (espar->Param.iParam == 2 ? L"2 (Hex)"
																							: L"?????")))));
						SetCharCodeBase(espar->Param.iParam);
						break;
						/* $ 07.08.2001 IS сменить кодировку из плагина */
					case ESPT_CODEPAGE: {
						// BUGBUG
						if ((UINT)espar->Param.iParam == CP_AUTODETECT) {
							rc = FALSE;
						} else if (!IsCodePageSupported(espar->Param.iParam)) {
							rc = FALSE;
						} else {
							if (HostFileEditor) {
								HostFileEditor->SetCodePage(espar->Param.iParam);
								HostFileEditor->CodepageChangedByUser();
							} else {
								SetCodePage(espar->Param.iParam);
							}

							Show();
						}
					} break;
					/* $ 29.10.2001 IS изменение настройки "Сохранять позицию файла" */
					case ESPT_SAVEFILEPOSITION:
						_ECTLLOG(SysLog(L"  iParam      =%ls", espar->Param.iParam ? L"On" : L"Off"));
						SetSavePosMode(espar->Param.iParam, -1);
						break;
						/* $ 23.03.2002 IS запретить/отменить изменение файла */
					case ESPT_LOCKMODE:
						_ECTLLOG(SysLog(L"  iParam      =%ls", espar->Param.iParam ? L"On" : L"Off"));
						Flags.Change(FEDITOR_LOCKMODE, espar->Param.iParam);
						break;
					case ESPT_SHOWWHITESPACE:
						SetShowWhiteSpace(espar->Param.iParam);
						break;
					default:
						_ECTLLOG(SysLog(L"}"));
						return FALSE;
				}

				_ECTLLOG(SysLog(L"}"));
				return rc;
			}

			return FALSE;
		}
		// Убрать флаг редактора "осуществляется выделение блока"
		case ECTL_TURNOFFMARKINGBLOCK: {
			Flags.Clear(FEDITOR_MARKINGVBLOCK | FEDITOR_MARKINGBLOCK);
			return TRUE;
		}
		case ECTL_DELETEBLOCK: {
			if (Flags.Check(FEDITOR_LOCKMODE) || !(VBlockStart || BlockStart)) {

				_ECTLLOG(if (Flags.Check(FEDITOR_LOCKMODE)) SysLog(L"FEDITOR_LOCKMODE!"));

				_ECTLLOG(if (!(VBlockStart || BlockStart)) SysLog(L"Not selected block!"));

				return FALSE;
			}

			Flags.Clear(FEDITOR_MARKINGVBLOCK | FEDITOR_MARKINGBLOCK);
			DeleteBlock();
			Show();
			return TRUE;
		}
		case ECTL_UNDOREDO: {
			if (Param) {
				EditorUndoRedo *eur = (EditorUndoRedo *)Param;

				switch (eur->Command) {
					case EUR_BEGIN:
						AddUndoData(UNDO_BEGIN);
						return TRUE;
					case EUR_END:
						AddUndoData(UNDO_END);
						return TRUE;
					case EUR_UNDO:
					case EUR_REDO:
						Lock();
						Undo(eur->Command == EUR_REDO);
						Unlock();
						return TRUE;
				}
			}

			return FALSE;
		}
	}

	return FALSE;
}

int Editor::SetBookmark(DWORD Pos)
{
	if (Pos < POSCACHE_BOOKMARK_COUNT) {
		SavePos.Line[Pos] = NumLine;
		SavePos.Cursor[Pos] = CurLine->GetCurPos();
		SavePos.LeftPos[Pos] = CurLine->GetLeftPos();
		SavePos.ScreenLine[Pos] = CalcDistance(TopScreen, CurLine, -1);
		return TRUE;
	}

	return FALSE;
}

int Editor::GotoBookmark(DWORD Pos)
{
	if (Pos < POSCACHE_BOOKMARK_COUNT) {
		if (SavePos.Line[Pos] != POS_NONE) {
			GoToLine(static_cast<int>(SavePos.Line[Pos]));
			CurLine->SetCurPos(static_cast<int>(SavePos.Cursor[Pos]));
			CurLine->SetLeftPos(static_cast<int>(SavePos.LeftPos[Pos]));
			TopScreen = CurLine;

			for (DWORD I = 0; I < SavePos.ScreenLine[Pos] && TopScreen->m_prev; I++)
				TopScreen = TopScreen->m_prev;

			if (!EdOpt.PersistentBlocks)
				UnmarkBlock();

			Show();
		}

		return TRUE;
	}

	return FALSE;
}

int Editor::ClearStackBookmarks()
{
	NewStackPos = FALSE;

	if (StackPos) {
		InternalEditorStackBookMark *sb_prev = StackPos->prev, *sb_next;

		while (StackPos) {
			sb_next = StackPos->next;
			free(StackPos);
			StackPos = sb_next;
		}

		StackPos = sb_prev;

		while (StackPos) {
			sb_prev = StackPos->prev;
			free(StackPos);
			StackPos = sb_prev;
		}
	}

	return TRUE;
}

int Editor::DeleteStackBookmark(InternalEditorStackBookMark *sb_delete)
{
	NewStackPos = FALSE;

	if (sb_delete) {
		if (sb_delete->next)
			sb_delete->next->prev = sb_delete->prev;

		if (sb_delete->prev)
			sb_delete->prev->next = sb_delete->next;

		if (StackPos == sb_delete)
			StackPos = (sb_delete->next) ? sb_delete->next : sb_delete->prev;

		free(sb_delete);
		return TRUE;
	}

	return FALSE;
}

int Editor::RestoreStackBookmark()
{
	NewStackPos = FALSE;

	if (StackPos && ((int)StackPos->Line != NumLine || (int)StackPos->Cursor != CurLine->GetCurPos())) {
		GoToLine(StackPos->Line);
		CurLine->SetCurPos(StackPos->Cursor);
		CurLine->SetLeftPos(StackPos->LeftPos);
		TopScreen = CurLine;

		for (DWORD I = 0; I < StackPos->ScreenLine && TopScreen->m_prev; I++)
			TopScreen = TopScreen->m_prev;

		if (!EdOpt.PersistentBlocks)
			UnmarkBlock();

		Show();
		return TRUE;
	}

	return FALSE;
}

int Editor::AddStackBookmark(BOOL blNewPos)
{
	InternalEditorStackBookMark *sb_old = StackPos;

	if (StackPos && StackPos->next) {
		StackPos = StackPos->next;
		StackPos->prev = 0;
		ClearStackBookmarks();
		StackPos = sb_old;
		StackPos->next = 0;
	}

	InternalEditorStackBookMark *sb_new =
			(InternalEditorStackBookMark *)malloc(sizeof(InternalEditorStackBookMark));

	if (sb_new) {
		if (StackPos)
			StackPos->next = sb_new;

		StackPos = sb_new;
		StackPos->prev = sb_old;
		StackPos->next = 0;
		StackPos->Line = NumLine;
		StackPos->Cursor = CurLine->GetCurPos();
		StackPos->LeftPos = CurLine->GetLeftPos();
		StackPos->ScreenLine = CalcDistance(TopScreen, CurLine, -1);
		NewStackPos = blNewPos;		// We had to save current position, if we will go to previous bookmark (by default)
		return TRUE;
	}

	return FALSE;
}

InternalEditorStackBookMark *Editor::PointerToFirstStackBookmark(int *piCount)
{
	InternalEditorStackBookMark *sb_temp = StackPos;
	int iCount = 0;

	if (sb_temp) {
		for (; sb_temp->prev; iCount++)
			sb_temp = sb_temp->prev;
	}

	if (piCount)
		*piCount = iCount;

	return sb_temp;
}

InternalEditorStackBookMark *Editor::PointerToLastStackBookmark(int *piCount)
{
	InternalEditorStackBookMark *sb_temp = StackPos;
	int iCount = 0;

	if (sb_temp) {
		for (; sb_temp->next; iCount++)
			sb_temp = sb_temp->next;
		iCount++;
	}

	if (piCount)
		*piCount+= iCount;

	return sb_temp;
}

InternalEditorStackBookMark *Editor::PointerToStackBookmark(int iIdx)	// Returns null_ptr if failed!
{
	InternalEditorStackBookMark *sb_temp = StackPos;

	if (iIdx != -1 && sb_temp)		// -1 == current
	{
		while (sb_temp->prev)
			sb_temp = sb_temp->prev;

		for (int i = 0; i != iIdx && sb_temp; i++)
			sb_temp = sb_temp->next;
	}

	return sb_temp;
}

int Editor::BackStackBookmark()
{
	if (StackPos) {
		if (NewStackPos)	// If we had to save current position ...
		{
			NewStackPos = FALSE;
			// ... if current bookmark is last and current_position != bookmark_position
			// save current position as new bookmark
			if (!StackPos->next
					&& ((int)StackPos->Line != NumLine || (int)StackPos->Cursor != CurLine->GetCurPos()))
				AddStackBookmark(FALSE);
		}

		return PrevStackBookmark();
	}

	return FALSE;
}

int Editor::PrevStackBookmark()
{
	if (StackPos) {
		if (StackPos->prev)		// If not first bookmark - go
		{
			StackPos = StackPos->prev;
		}

		return RestoreStackBookmark();
	}

	return FALSE;
}

int Editor::NextStackBookmark()
{
	if (StackPos) {
		if (StackPos->next)		// If not last bookmark - go
		{
			StackPos = StackPos->next;
		}

		return RestoreStackBookmark();
	}

	return FALSE;
}

int Editor::LastStackBookmark()
{
	if (StackPos) {
		StackPos = PointerToLastStackBookmark();
		return RestoreStackBookmark();
	}

	return FALSE;
}

int Editor::GotoStackBookmark(int iIdx)
{
	if (StackPos) {
		InternalEditorStackBookMark *sb_temp = PointerToStackBookmark(iIdx);
		if (sb_temp) {
			StackPos = sb_temp;
			return RestoreStackBookmark();
		}
	}

	return FALSE;
}

int Editor::PushStackBookMark()
{
	StackPos = PointerToLastStackBookmark();
	return AddStackBookmark(FALSE);
}

int Editor::PopStackBookMark()
{
	return (LastStackBookmark() && DeleteStackBookmark(StackPos));
}

int Editor::CurrentStackBookmarkIdx()
{
	int iIdx;
	if (PointerToFirstStackBookmark(&iIdx))
		return iIdx;
	return -1;
}

int Editor::GetStackBookmark(int iIdx, EditorBookMarks *Param)
{
	InternalEditorStackBookMark *sb_temp = PointerToStackBookmark(iIdx);

	if (sb_temp && Param) {
		if (Param->Line)
			Param->Line[0] = sb_temp->Line;
		if (Param->Cursor)
			Param->Cursor[0] = sb_temp->Cursor;
		if (Param->LeftPos)
			Param->LeftPos[0] = sb_temp->LeftPos;
		if (Param->ScreenLine)
			Param->ScreenLine[0] = sb_temp->ScreenLine;

		return TRUE;
	}

	return FALSE;
}

int Editor::GetStackBookmarks(EditorBookMarks *Param)
{
	int iCount = 0;

	if (StackPos) {
		InternalEditorStackBookMark *sb_temp = PointerToFirstStackBookmark(&iCount);
		PointerToLastStackBookmark(&iCount);

		if (Param) {
			if (Param->Line || Param->Cursor || Param->LeftPos || Param->ScreenLine) {
				for (int i = 0; i < iCount; i++) {
					if (Param->Line)
						Param->Line[i] = sb_temp->Line;

					if (Param->Cursor)
						Param->Cursor[i] = sb_temp->Cursor;

					if (Param->LeftPos)
						Param->LeftPos[i] = sb_temp->LeftPos;

					if (Param->ScreenLine)
						Param->ScreenLine[i] = sb_temp->ScreenLine;

					sb_temp = sb_temp->next;
				}
			} else
				iCount = 0;
		}
	}

	return iCount;
}

Edit *Editor::GetStringByNumber(int DestLine)
{
	if (DestLine == NumLine || DestLine < 0) {
		LastGetLine = CurLine;
		LastGetLineNumber = NumLine;
		return CurLine;
	}

	if (DestLine > NumLastLine)
		return nullptr;

	Edit *CurPtr = CurLine;
	int StartLine = NumLine;

	if (LastGetLine) {
		CurPtr = LastGetLine;
		StartLine = LastGetLineNumber;
	}

	bool Forward = (DestLine > StartLine && DestLine < StartLine + (NumLastLine - StartLine) / 2)
			|| (DestLine < StartLine / 2);

	if (DestLine > StartLine) {
		if (!Forward) {
			StartLine = NumLastLine - 1;
			CurPtr = EndList;
		}
	} else {
		if (Forward) {
			StartLine = 0;
			CurPtr = TopList;
		}
	}

	for (int Line = StartLine; Line != DestLine; Forward ? Line++ : Line--) {
		CurPtr = (Forward ? CurPtr->m_next : CurPtr->m_prev);
		if (!CurPtr) {
			LastGetLine = Forward ? TopList : EndList;
			LastGetLineNumber = Forward ? 0 : NumLastLine - 1;
			return nullptr;
		}
	}
	LastGetLine = CurPtr;
	LastGetLineNumber = DestLine;
	return CurPtr;
}

void Editor::SetReplaceMode(int Mode)
{
	::ReplaceMode = Mode;
}

int Editor::GetLineCurPos()
{
	return CurLine->GetCellCurPos();
}

void Editor::BeginVBlockMarking()
{
	UnmarkBlock();
	VBlockStart = CurLine;
	VBlockX = CurLine->GetCellCurPos();
	VBlockSizeX = 0;
	VBlockY = NumLine;
	VBlockSizeY = 1;
	Flags.Set(FEDITOR_MARKINGVBLOCK);
	BlockStartLine = NumLine;
	//_D(SysLog(L"BeginVBlockMarking, set vblock to VBlockY=%i:%i, VBlockX=%i:%i",VBlockY,VBlockSizeY,VBlockX,VBlockSizeX));
}

void Editor::AdjustVBlock(int PrevX)
{
	int x = GetLineCurPos();
	int c2;

	//_D(SysLog(L"AdjustVBlock, x=%i, vblock is VBlockY=%i:%i, VBlockX=%i:%i, PrevX=%i",x,VBlockY,VBlockSizeY,VBlockX,VBlockSizeX,PrevX));
	if (x == VBlockX + VBlockSizeX)		// ничего не случилось, никаких табуляций нет
		return;

	if (x > VBlockX)	// курсор убежал внутрь блока
	{
		VBlockSizeX = x - VBlockX;
		//_D(SysLog(L"x>VBlockX");
	} else if (x < VBlockX)		// курсор убежал за начало блока
	{
		c2 = VBlockX;

		if (PrevX > VBlockX)	// сдвигались вправо, а пришли влево
		{
			VBlockX = x;
			VBlockSizeX = c2 - x;	// меняем блок
		} else						// сдвигались влево и пришли еще больше влево
		{
			VBlockX = x;
			VBlockSizeX+= c2 - x;	// расширяем блок
		}

		//_D(SysLog(L"x<VBlockX"));
	} else if (x == VBlockX && x != PrevX) {
		VBlockSizeX = 0;	// ширина в 0, потому прыгнули прям на табуляцию
							//_D(SysLog(L"x==VBlockX && x!=PrevX"));
	}

	// примечание
	// случай x>VBLockX+VBlockSizeX не может быть
	// потому что курсор прыгает назад на табуляцию, но не вперед
	//_D(SysLog(L"AdjustVBlock, changed vblock VBlockY=%i:%i, VBlockX=%i:%i",VBlockY,VBlockSizeY,VBlockX,VBlockSizeX));
}

void Editor::Xlat()
{
	Edit *CurPtr;
	int Line;
	BOOL DoXlat = FALSE;
	AddUndoData(UNDO_BEGIN);

	if (VBlockStart) {
		CurPtr = VBlockStart;

		for (Line = 0; CurPtr && Line < VBlockSizeY; Line++, CurPtr = CurPtr->m_next) {
			int TBlockX = CurPtr->CellPosToReal(VBlockX);
			int TBlockSizeX = CurPtr->CellPosToReal(VBlockX + VBlockSizeX) - CurPtr->CellPosToReal(VBlockX);
			const wchar_t *CurStr, *EndSeq;
			int Length;
			CurPtr->GetBinaryString(&CurStr, &EndSeq, Length);
			int CopySize = Length - TBlockX;

			if (CopySize > TBlockSizeX)
				CopySize = TBlockSizeX;

			AddUndoData(UNDO_EDIT, CurPtr->GetStringAddr(), CurPtr->GetEOL(), BlockStartLine + Line,
					CurLine->GetCurPos(), CurPtr->GetLength());
			::Xlat(CurPtr->Str, TBlockX, TBlockX + CopySize, Opt.XLat.Flags);
		}

		DoXlat = TRUE;
	} else {
		Line = 0;
		CurPtr = BlockStart;

		/*
			$ 25.11.2000 IS
			Если нет выделения, то обработаем текущее слово. Слово определяется на
			основе специальной группы разделителей.
		*/
		if (CurPtr) {
			while (CurPtr) {
				int StartSel, EndSel;
				CurPtr->GetSelection(StartSel, EndSel);

				if (StartSel == -1)
					break;

				if (EndSel == -1)
					EndSel = CurPtr->GetLength();	// StrLength(CurPtr->Str);

				AddUndoData(UNDO_EDIT, CurPtr->GetStringAddr(), CurPtr->GetEOL(), BlockStartLine + Line,
						CurLine->GetCurPos(), CurPtr->GetLength());
				::Xlat(CurPtr->Str, StartSel, EndSel, Opt.XLat.Flags);
				Line++;
				CurPtr = CurPtr->m_next;
			}

			DoXlat = TRUE;
		} else {
			wchar_t *Str = CurLine->Str;
			int start = CurLine->GetCurPos(), end, StrSize = CurLine->GetLength();	// StrLength(Str);
			/*
				$ 10.12.2000 IS
				Обрабатываем только то слово, на котором стоит курсор, или то слово,
				что находится левее позиции курсора на 1 символ
			*/
			DoXlat = TRUE;

			if (IsWordDiv(Opt.XLat.strWordDivForXlat, Str[start])) {
				if (start)
					start--;

				DoXlat = (!IsWordDiv(Opt.XLat.strWordDivForXlat, Str[start]));
			}

			if (DoXlat) {
				while (start >= 0 && !IsWordDiv(Opt.XLat.strWordDivForXlat, Str[start]))
					start--;

				start++;
				end = start + 1;

				while (end < StrSize && !IsWordDiv(Opt.XLat.strWordDivForXlat, Str[end]))
					end++;

				AddUndoData(UNDO_EDIT, CurLine->GetStringAddr(), CurLine->GetEOL(), NumLine, start,
						CurLine->GetLength());
				::Xlat(Str, start, end, Opt.XLat.Flags);
			}
		}
	}

	AddUndoData(UNDO_END);

	if (DoXlat)
		TextChanged(1);
}
/* SVS $ */

/*
	$ 15.02.2001 IS
	Манипуляции с табуляцией на уровне всего загруженного файла.
	Может быть длительной во времени операцией, но тут уж, imho,
	ничего не поделать.
*/
// Обновим размер табуляции
void Editor::SetTabSize(int NewSize)
{
	if (NewSize<1 || NewSize>512 || NewSize==EdOpt.TabSize)
		return; /* Меняем размер табуляции только в том случае, если он
					на самом деле изменился */

	EdOpt.TabSize = NewSize;
	Edit *CurPtr = TopList;

	while (CurPtr) {
		CurPtr->SetTabSize(NewSize);
		CurPtr = CurPtr->m_next;
	}
}

// обновим режим пробелы вместо табуляции
// операция необратима, кстати, т.е. пробелы на табуляцию обратно не изменятся
void Editor::SetConvertTabs(int NewMode)
{
	if (NewMode != EdOpt.ExpandTabs)
	// Меняем режим только в том случае, если он на самом деле изменился
	{
		EdOpt.ExpandTabs = NewMode;
		Edit *CurPtr = TopList;

		while (CurPtr) {
			CurPtr->SetConvertTabs(NewMode);

			if (NewMode == EXPAND_ALLTABS)
				CurPtr->ExpandTabs();

			CurPtr = CurPtr->m_next;
		}
	}
}

void Editor::SetDelRemovesBlocks(int NewMode)
{
	if (NewMode != EdOpt.DelRemovesBlocks) {
		EdOpt.DelRemovesBlocks = NewMode;
		Edit *CurPtr = TopList;

		while (CurPtr) {
			CurPtr->SetDelRemovesBlocks(NewMode);
			CurPtr = CurPtr->m_next;
		}
	}
}

void Editor::SetShowWhiteSpace(int NewMode)
{
	if (NewMode != EdOpt.ShowWhiteSpace) {
		EdOpt.ShowWhiteSpace = NewMode;

		for (Edit *CurPtr = TopList; CurPtr; CurPtr = CurPtr->m_next) {
			CurPtr->SetShowWhiteSpace(NewMode);
		}
	}
}

void Editor::SetPersistentBlocks(int NewMode)
{
	if (NewMode != EdOpt.PersistentBlocks) {
		EdOpt.PersistentBlocks = NewMode;
		Edit *CurPtr = TopList;

		while (CurPtr) {
			CurPtr->SetPersistentBlocks(NewMode);
			CurPtr = CurPtr->m_next;
		}
	}
}

// "Курсор за пределами строки"
void Editor::SetCursorBeyondEOL(int NewMode)
{
	if (NewMode != EdOpt.CursorBeyondEOL) {
		EdOpt.CursorBeyondEOL = NewMode;
		Edit *CurPtr = TopList;

		while (CurPtr) {
			CurPtr->SetEditBeyondEnd(NewMode);
			CurPtr = CurPtr->m_next;
		}
	}

	/*
		$ 16.10.2001 SKV
		Если переключились туда сюда этот режим,
		то из-за этой штуки возникают нехилые глюки
		при выделении вертикальных блоков.
	*/
	if (EdOpt.CursorBeyondEOL) {
		MaxRightPos = 0;
	}
}

void Editor::GetSavePosMode(int &SavePos, int &SaveShortPos)
{
	SavePos = EdOpt.SavePos;
	SaveShortPos = EdOpt.SaveShortPos;
}

// передавайте в качестве значения параметра "-1" для параметра,
// который не нужно менять
void Editor::SetSavePosMode(int SavePos, int SaveShortPos)
{
	if (SavePos != -1)
		EdOpt.SavePos = SavePos;

	if (SaveShortPos != -1)
		EdOpt.SaveShortPos = SaveShortPos;
}

void Editor::EditorShowMsg(const wchar_t *Title, const wchar_t *Msg, const wchar_t *Name, int Percent)
{
	FARString strProgress;

	if (Percent > -1) {
		FormatString strPercent;
		strPercent << Percent;

		size_t PercentLength = Max(strPercent.strValue().GetLength(), (size_t)3);
		size_t Length =
				Max(Min(static_cast<int>(MAX_WIDTH_MESSAGE - 2), StrLength(Name)), 40) - PercentLength - 2;

		size_t CurPos = Min(Percent, 100) * Length / 100;
		strProgress.Reserve(Length);
		strProgress.Append(BoxSymbols[BS_X_DB], CurPos);
		strProgress.Append(BoxSymbols[BS_X_B0], Length - CurPos);
		FormatString strTmp;
		strTmp << L" " << fmt::Expand(PercentLength) << strPercent << L"%";
		strProgress+= strTmp;
	}

	Message(0, 0, Title, Msg, Name, strProgress.IsEmpty() ? nullptr : strProgress.CPtr());
	PreRedrawItem preRedrawItem = PreRedraw.Peek();
	preRedrawItem.Param.Param1 = (void *)Title;
	preRedrawItem.Param.Param2 = (void *)Msg;
	preRedrawItem.Param.Param3 = (void *)Name;
	preRedrawItem.Param.Param4 = (void *)(INT_PTR)(Percent);
	PreRedraw.SetParam(preRedrawItem.Param);
}

void Editor::PR_EditorShowMsg()
{
	PreRedrawItem preRedrawItem = PreRedraw.Peek();
	Editor::EditorShowMsg((wchar_t *)preRedrawItem.Param.Param1, (wchar_t *)preRedrawItem.Param.Param2,
			(wchar_t *)preRedrawItem.Param.Param3, (int)(INT_PTR)preRedrawItem.Param.Param4);
}

Edit *Editor::CreateString(const wchar_t *lpwszStr, int nLength)
{
	Edit *pEdit = new (std::nothrow) Edit(this, nullptr, lpwszStr ? false : true);

	if (pEdit) {
		pEdit->m_next = nullptr;
		pEdit->m_prev = nullptr;
		pEdit->SetTabSize(EdOpt.TabSize);
		pEdit->SetPersistentBlocks(EdOpt.PersistentBlocks);
		pEdit->SetConvertTabs(EdOpt.ExpandTabs);
		pEdit->SetCodePage(m_codepage);

		if (lpwszStr)
			pEdit->SetBinaryString(lpwszStr, nLength);

		pEdit->SetCurPos(0);
		pEdit->SetObjectColor(FarColorToReal(COL_EDITORTEXT), FarColorToReal(COL_EDITORSELECTEDTEXT));
		pEdit->SetEditorMode(TRUE);
		pEdit->SetWordDiv(EdOpt.strWordDiv);
		pEdit->SetShowWhiteSpace(EdOpt.ShowWhiteSpace);
	}

	return pEdit;
}

/*
bool Editor::AddString (const wchar_t *lpwszStr, int nLength)
{
	Edit *pNewEdit = CreateString (lpwszStr, nLength);

	if ( !pNewEdit )
		return false;

	if ( !TopList || !NumLastLine ) //???
		TopList = EndList = TopScreen = CurLine = pNewEdit;
	else
	{
		Edit *PrevPtr;

		EndList->m_next = pNewEdit;

		PrevPtr = EndList;
		EndList = EndList->m_next;
		EndList->m_prev = PrevPtr;
		EndList->m_next = nullptr;
	}

	NumLastLine++;

	return true;
}
*/

Edit *Editor::InsertString(const wchar_t *lpwszStr, int nLength, Edit *pAfter, int AfterLineNumber)
{
	Edit *pNewEdit = CreateString(lpwszStr, nLength);

	if (pNewEdit) {
		if (!TopList || !NumLastLine)	//???
			TopList = EndList = TopScreen = CurLine = pNewEdit;
		else {
			Edit *pWork = pAfter ? pAfter : EndList;
			Edit *pNext = pWork->m_next;
			pNewEdit->m_next = pNext;
			pNewEdit->m_prev = pWork;
			pWork->m_next = pNewEdit;

			if (pNext)
				pNext->m_prev = pNewEdit;

			if (!pAfter) {
				EndList = pNewEdit;
				AfterLineNumber = NumLastLine - 1;
			}
		}

		NumLastLine++;

		if (AfterLineNumber < LastGetLineNumber) {
			LastGetLineNumber++;
		}
	}

	return pNewEdit;
}

void Editor::EnableSaveTabSettings()
{
	SaveTabSettings = true;
}

void Editor::SetCacheParams(EditorCacheParams *pp)
{
	bool translateTabs = false;
	SavePos = pp->SavePos;
	// m_codepage = pp->Table; //BUGBUG!!!, LoadFile do it itself

	if (StartLine == -2)	// from Viewer!
	{
		Edit *CurPtr = TopList;
		long TotalSize = 0;

		while (CurPtr && CurPtr->m_next) {
			const wchar_t *SaveStr, *EndSeq;
			int Length;
			CurPtr->GetBinaryString(&SaveStr, &EndSeq, Length);
			TotalSize+= Length + StrLength(EndSeq);

			if (TotalSize > StartChar)
				break;

			CurPtr = CurPtr->m_next;
			NumLine++;
		}

		TopScreen = CurLine = CurPtr;

		if (NumLine == pp->Line - pp->ScreenLine) {
			Lock();

			for (DWORD I = 0; I < (DWORD)pp->ScreenLine; I++)
				ProcessKey(KEY_DOWN);

			CurLine->SetCellCurPos(pp->LinePos);
			Unlock();
		}

		CurLine->SetLeftPos(pp->LeftPos);
	} else {
		if (StartLine != -1 || EdOpt.SavePos) {
			if (StartLine != -1) {
				pp->Line = StartLine - 1;
				pp->ScreenLine = ObjHeight / 2;		// ScrY

				if (pp->ScreenLine > pp->Line)
					pp->ScreenLine = pp->Line;

				pp->LinePos = 0;
				if (StartChar > 0) {
					pp->LinePos = StartChar - 1;
					translateTabs = true;
				}
			}

			if (pp->ScreenLine > ObjHeight)		// ScrY //BUGBUG
				pp->ScreenLine = ObjHeight;		// ScrY;

			if (pp->Line >= pp->ScreenLine) {
				Lock();
				GoToLine(pp->Line - pp->ScreenLine);
				TopScreen = CurLine;

				for (int I = 0; I < pp->ScreenLine; I++)
					ProcessKey(KEY_DOWN);

				if (translateTabs)
					CurLine->SetCurPos(pp->LinePos);
				else
					CurLine->SetCellCurPos(pp->LinePos);
				CurLine->SetLeftPos(pp->LeftPos);
				Unlock();
			}
		}
		if (pp->TabSize > 0) {
			SetTabSize(pp->TabSize - 1);
			SaveTabSettings = true;
		}
		if (pp->ExpandTabs > 0) {
			SetConvertTabs(pp->ExpandTabs - 1);
			SaveTabSettings = true;
		}
		fprintf(stderr, "Editor::SetCacheParams: ExpandTabs=%d TabSize=%d\n", pp->ExpandTabs, pp->TabSize);
	}
}

void Editor::GetCacheParams(EditorCacheParams *pp)
{
	memset(pp, 0, sizeof(EditorCacheParams));
	memset(&pp->SavePos, 0xff, sizeof(InternalEditorBookMark));
	pp->Line = NumLine;
	pp->ScreenLine = CalcDistance(TopScreen, CurLine, -1);
	pp->LinePos = CurLine->GetCellCurPos();
	pp->LeftPos = CurLine->GetLeftPos();
	pp->CodePage = m_codepage;
	if (SaveTabSettings) {
		pp->ExpandTabs = GetConvertTabs() + 1;
		pp->TabSize = GetTabSize() + 1;
	}
	fprintf(stderr, "Editor::GetCacheParams: SaveTabSettings=%d ExpandTabs=%d TabSize=%d\n", SaveTabSettings,
			pp->ExpandTabs, pp->TabSize);

	if (Opt.EdOpt.SaveShortPos) {
		pp->SavePos = SavePos;
	}
}

bool Editor::SetCodePage(UINT codepage)
{
	if (m_codepage != codepage) {
		m_codepage = codepage;
		Edit *current = TopList;
		DWORD Result = 0;

		while (current) {
			Result|= current->SetCodePage(m_codepage);
			current = current->m_next;
		}

		Show();
		return !Result;		// BUGBUG, more details
	}

	return true;
}

UINT Editor::GetCodePage()
{
	return m_codepage;
}

void Editor::SetDialogParent(DWORD Sets) {}

void Editor::SetOvertypeMode(int Mode) {}

int Editor::GetOvertypeMode()
{
	return 0;
}

void Editor::SetEditBeyondEnd(int Mode) {}

void Editor::SetClearFlag(int Flag) {}

int Editor::GetClearFlag()
{
	return 0;
}

int Editor::GetCurCol()
{
	return CurLine->GetCurPos();
}

void Editor::SetCurPos(int NewCol, int NewRow)
{
	Lock();
	GoToLine(NewRow);
	CurLine->SetCellCurPos(NewCol);
	// CurLine->SetLeftPos(LeftPos); ???
	Unlock();
}

void Editor::SetCursorType(bool Visible, DWORD Size)
{
	CurLine->SetCursorType(Visible, Size);	//???
}

void Editor::GetCursorType(bool &Visible, DWORD &Size)
{
	CurLine->GetCursorType(Visible, Size);	//???
}

void Editor::SetObjectColor(uint64_t Color, uint64_t SelColor, uint64_t ColorUnChanged)
{
	for (Edit *CurPtr = TopList; CurPtr; CurPtr = CurPtr->m_next)	//???
		CurPtr->SetObjectColor(Color, SelColor, ColorUnChanged);
}

void Editor::DrawScrollbar()
{
	if (EdOpt.ShowScrollBar) {
		SetFarColor(COL_EDITORSCROLLBAR);
		XX2 = X2
				- (ScrollBarEx(X2, Y1, Y2 - Y1 + 1, NumLine - CalcDistance(TopScreen, CurLine, -1),
							NumLastLine)
								? 1
								: 0);
	}
}
