View Issue Details

IDProjectCategoryView StatusLast Update
0001766Far ManagerViewerpublic2011-10-11 11:20
Reporter2useven10 Assigned To2useven10  
PrioritynormalSeverityfeatureReproducibilityN/A
Status closedResolutionfixed 
Product Version3.0 
Fixed in Version3.0 
Summary0001766: Новая версия вьювера
DescriptionПредлагаю потестить новую версию вьювера (far3 и far2 1807).
Все изменения о которых я помню перечислены тут: http://sharapov-16.narod.ru/far/index.html
Собранные версии фар и измененные файлы там тоже есть.
Здесь патч пока не выкдадываю, хотелось бы получить feedback...
TagsNo tags attached.
Build2217

Activities

2useven10

2011-05-12 19:58

developer   bugnote:0006882

Last edited: 2011-05-12 19:59

Изменено (по памяти):
1. добавлен поиск регулярных выражений (построчный в обе стороны)
2. добавлено авто-определение кодовой страницы на Shift-F8
3. в авто-детекте можно задать запрещенные codepage-s (параметр)
4. авто-фокус на строке поиска в диалоге (по параметру)
5. повторный поиск всегда зациклен до места начала поиска или смены направления
6. добавлена возможность изменения максимальной длины строки (параметр)

max rusov

2011-05-12 20:34

reporter   bugnote:0006883

Вот бы еще курсор поддержать - цены бы не было...

2useven10

2011-05-12 21:18

developer   bugnote:0006884

А подробнее...
Для чего курсор? Только для показа текущей позиции?
Или что-то ещё (например выделение текста) ?
Поведение курсора в HexView...
Как он должен включаться/выключаться...

Ну или просто хороший пример вьювера с курсором.

max rusov

2011-05-12 22:29

reporter   bugnote:0006886

Ну, естественно, чтобы выделять и брать в clipboard. Все как в редакторе, но без возможности править. В Hex-View тоже курсор+выделение не помешает. Есть плагин Hex Editor но он не поддерживает блочные операции.

2useven10

2011-05-13 12:45

developer   bugnote:0006887

Last edited: 2011-05-13 12:46

Можно сделать... но не сейчас - через пару недель (если будет свободное время).

2useven10

2011-05-18 07:53

developer  

viewer-diff-8.7z (23,639 bytes)

2useven10

2011-05-18 07:55

developer   bugnote:0006890

выложил патч (изменения из #1755 здесь тоже есть)

vskirdin

2011-05-18 14:51

administrator   bugnote:0006893

Уф... вроде Инц про вьювер, а в патче замес по полной...
Надо бы разделить...

vskirdin

2011-05-18 16:07

administrator   bugnote:0006894

Так, лажа.
Куда делась супер-пупер фича (когда стоим в конце файла и, если его другой процесс "наполняет", то происходит автоскроллинг) из вьювера?

2useven10

2011-05-18 16:22

developer   bugnote:0006895

Last edited: 2011-05-18 16:31

ну вот и фидбэк ( надеюсь будет ещё :)
лажу посмотрю-поправлю. а насчет разделить - кроме NoAutoDetectCP (filestr.cpp)
все остальное связано с изменениями вьювера... можно только поделить на части.
Но тогда процесс затянется надолго (сужу по #1755).
К тому же, все что кроме - изменения в несколько строк.

vskirdin

2011-05-18 20:52

administrator   bugnote:0006896

> лажу посмотрю-поправлю.
Ок.

> Но тогда процесс затянется надолго (сужу по #1755).
необязательно.
иногда можно и напоминалку "сделать" :-)

2useven10

2011-05-18 21:27

developer   bugnote:0006897

Last edited: 2011-05-23 14:29

я бы взял сразу... но можно и частями. начнём:

http://bugs.farmanager.com/view.php?id=1755 [x]
http://bugs.farmanager.com/view.php?id=1772 [x]
http://bugs.farmanager.com/view.php?id=1773 [x]
http://bugs.farmanager.com/view.php?id=1774 [x]

автоскролл в своей версии я поправил, но ошибка есть и в транке.
если критично - могу сделать отдельный патч (хотя если честно - не хотелось бы)

vskirdin

2011-05-25 12:24

administrator   bugnote:0007033

так, вроде "предварительные ласки" закончились... че дальше?

2useven10

2011-05-25 13:43

developer   bugnote:0007038

Last edited: 2011-05-25 13:58

чуть доделаю поиск с регулярками - пока просто не знаю что делать с соответствием
^$ и найденным текстом нулевой длинны - пока нет курсора, как это показывать, а в hex...
думаю сегодня завтра выложу новый новый вьювер :)

2useven10

2011-05-26 11:10

developer  

NewViewer.diff (93,452 bytes)   
Index: viewer.hpp
===================================================================
--- viewer.hpp	(revision 6286)
+++ viewer.hpp	(working copy)
@@ -40,22 +40,13 @@
 #include "config.hpp"
 #include "cache.hpp"
 
-/* $ 10.07.2000 tran
-   ! modified MAXSCRY from 120 to 300
-   on win200, with Console height FAR work, but trap on viewer... */
-#define  MAXSCRY     300
+#define MAXSCRY       512                    // 300   // 120
 
-/* $ 12.07.2000 SVS
-  - ��-�� �������� ���� ������ �� 0x800 ������ FAR
-    �� Alt-F7. �������� MAX_VIEWLINE �� 1024 (0x400)
-*/
-#define MAX_VIEWLINE  0x800 // 0x400
-#define MAX_VIEWLINEB 0x80f // 0x40f
+#define MAX_VIEWLINE  (ViOpt.MaxLineSize+ 0) // 0x800 // 0x400
+#define MAX_VIEWLINEB (ViOpt.MaxLineSize+15) // 0x80f // 0x40f
 
 #define VIEWER_UNDO_COUNT   64
 
-enum {VIEW_UNWRAP=0,VIEW_WRAP=1, VIEW_WORDWRAP=2};
-
 class FileViewer;
 class KeyBar;
 
@@ -65,7 +56,7 @@
 	__int64 nFilePos;
 	__int64 nSelStart;
 	__int64 nSelEnd;
-	int  nbytes;
+	int  linesize;
 	bool bSelection;
 	bool have_eol;
 };
@@ -76,12 +67,6 @@
 	__int64 UndoLeft;
 };
 
-enum SEARCH_FLAGS
-{
-	SEARCH_MODE2   = 0x00000001,
-	REVERSE_SEARCH = 0x00000002
-};
-
 enum SHOW_MODES
 {
 	SHOW_RELOAD,
@@ -95,9 +80,6 @@
 		friend class FileViewer;
 
 	private:
-
-		BitFlags SearchFlags;
-
 		struct ViewerOptions ViOpt;
 
 		bool Signature;
@@ -105,7 +87,7 @@
 		NamesList ViewNamesList;
 		KeyBar *ViewKeyBar;
 
-		ViewerString *Strings[MAXSCRY+1];
+		ViewerString **Strings;
 
 		string strFileName;
 		string strFullFileName;
@@ -121,6 +103,8 @@
 
 		string strLastSearchStr;
 		int LastSearchCase,LastSearchWholeWords,LastSearchReverse,LastSearchHex,LastSearchRegexp;
+		int LastSearchDirection;
+		__int64 StartSearchPos;
 
 		struct ViewerMode VM;
 
@@ -128,13 +112,12 @@
 		__int64 SecondPos;
 		__int64 LastScrPos;
 		__int64 FileSize;
-		__int64 LastSelPos;
+		__int64 LastSelectPos, LastSelectSize;
 
 		__int64 LeftPos;
 		__int64 LastPage;
 		__int64 SelectPos,SelectSize;
 		DWORD SelectFlags;
-		__int64 SelectPosOffSet; // ���������� �� �������� ������ ������� � ������� �����
 		int ShowStatusLine,HideCursor;
 
 		string strTitle;
@@ -165,9 +148,20 @@
 		char *vread_buffer;
 		int vread_buffer_size;
 
-		wchar_t *Up_buffer;
-		int Up_buffer_size;
+		__int64  lcache_first;
+		__int64  lcache_last;
+		__int64 *lcache_lines;
+		int      lcache_size;
+		int      lcache_count;
+		int      lcache_base;
+	   bool     lcache_ready;
+		int      lcache_wrap;
+		int      lcache_wwrap;
+		int      lcache_width;
 
+		wchar_t *Search_buffer;
+		int Search_buffer_size;
+
 		ViewerString vString;
 
 		unsigned char vgetc_buffer[32];
@@ -181,8 +175,9 @@
 
 		void ShowPage(int nMode);
 
-		void Up();
-		int WrappedLength(wchar_t *str, int line_size);
+		void Up(int n);
+		void CacheLine(__int64 start, int length, bool have_eol);
+		int CacheFindUp(__int64 start);
 
 		void ShowHex();
 		void ShowStatus();
@@ -195,17 +190,31 @@
 		void AdjustWidth();
 		void AdjustFilePos();
 
-		void ReadString(ViewerString *pString, int MaxSize);
-		int CalcStrSize(const wchar_t *Str,int Length);
+		void ReadString(ViewerString *pString, int MaxSize, bool update_cache=true);
+		__int64 EndOfScreen( int line );
+		__int64 BegOfScreen();
+
 		void ChangeViewKeyBar();
+
 		void Search(int Next,int FirstChar);
-		void ConvertToHex(char *SearchStr,int &SearchLength);
+		//
+		struct search_data;
+		//
+		int search_hex_forward( search_data* sd );
+		int search_hex_backward( search_data* sd );
+		int search_text_forward( search_data* sd );
+		int search_text_backward( search_data* sd );
+		int search_regex_forward( search_data* sd );
+		int search_regex_backward( search_data* sd );
 
+		int read_line(wchar_t *buf, wchar_t *tbuf, INT64 cpos, int adjust, INT64 &lpos, int &lsize);
+
 		int vread(wchar_t *Buf, int Count, wchar_t *Buf2 = nullptr);
 		bool vseek(__int64 Offset, int Whence);
 		__int64 vtell();
 		bool vgetc(wchar_t *ch);
 		bool veof();
+		wchar_t vgetc_prev();
 
 		void SetFileSize();
 		int GetStrBytesNum(const wchar_t *Str, int Length);
@@ -283,5 +292,5 @@
 		int ProcessWrapMode(int newMode, bool isRedraw=TRUE);
 		int ProcessTypeWrapMode(int newMode, bool isRedraw=TRUE);
 
-		void SearchTextTransform(UnicodeString &to, const wchar_t *from, bool hex2text);
+		void SearchTextTransform(UnicodeString &to, const wchar_t *from, bool hex2text, int &pos);
 };
Index: viewer.cpp
===================================================================
--- viewer.cpp	(revision 6286)
+++ viewer.cpp	(working copy)
@@ -65,18 +65,20 @@
 #include "constitle.hpp"
 #include "console.hpp"
 #include "wakeful.hpp"
+#include "RegExp.hpp"
 
 static void PR_ViewerSearchMsg();
-static void ViewerSearchMsg(const wchar_t *Name,int Percent);
+static void ViewerSearchMsg(const wchar_t *name, int percent, int search_hex);
 
-static int InitHex=FALSE,SearchHex=FALSE;
+//static int InitHex=FALSE,SearchHex=FALSE;
 
 static int ViewerID=0;
 
 static int utf8_to_WideChar(const char *s, int nc, wchar_t *w1,wchar_t *w2, int wlen, int &tail);
 
-#define REPLACE_CHAR 0xFFFD  // Replacement
+#define REPLACE_CHAR  0xFFFD // Replacement
 #define CONTINUE_CHAR 0x203A // Single Right-Pointing Angle Quotation Mark
+#define BOM_CHAR      0xFEFF // Zero Length Space
 
 Viewer::Viewer(bool bQuickView, UINT aCodePage):
 	ViOpt(Opt.ViOpt),
@@ -85,10 +87,11 @@
 {
 	_OT(SysLog(L"[%p] Viewer::Viewer()", this));
 
-	for (int i=0; i<=MAXSCRY; i++)
+	Strings = new ViewerString*[MAXSCRY+1];
+	for (int i=0; i <= MAXSCRY; i++)
 	{
 		Strings[i] = new ViewerString();
-		Strings[i]->lpData = new wchar_t[MAX_VIEWLINEB];
+		memset(Strings[i], 0, sizeof(ViewerString));
 	}
 
 	strLastSearchStr = strGlobalSearchString;
@@ -97,19 +100,21 @@
 	LastSearchWholeWords=GlobalSearchWholeWords;
 	LastSearchReverse=GlobalSearchReverse;
 	LastSearchHex=GlobalSearchHex;
+	LastSearchDirection = GlobalSearchReverse ? -1 : +1;
+	StartSearchPos = 0;
 	VM.CodePage=DefCodePage=aCodePage;
 	// �������� ��� �����
 	VM.Wrap=Opt.ViOpt.ViewerIsWrap;
 	VM.WordWrap=Opt.ViOpt.ViewerWrap;
-	VM.Hex=InitHex;
+	VM.Hex = FALSE; //InitHex;
 	ViewKeyBar=nullptr;
 	FilePos=0;
 	LeftPos=0;
 	SecondPos=0;
 	FileSize=0;
 	LastPage=0;
-	SelectPos=SelectSize=0;
-	LastSelPos=0;
+	SelectPos = 0; SelectSize = -1;
+	LastSelectPos = 0; LastSelectSize = -1;
 	SetStatusMode(TRUE);
 	HideCursor=TRUE;
 	DeleteFolder=TRUE;
@@ -123,7 +128,6 @@
 	CtrlObject->Plugins.CurViewer=this;
 	OpenFailed=false;
 	HostFileViewer=nullptr;
-	SelectPosOffSet=0;
 	bVE_READ_Sent = false;
 	Signature = false;
 
@@ -132,8 +136,17 @@
 	vgetc_composite = L'\0';
 
 	vread_buffer = new char[vread_buffer_size = 8192];
-	Up_buffer = new wchar_t[Up_buffer_size = 2 * (3*MAX_VIEWLINE + 2)];
 
+	lcache_first = lcache_last = -1;
+	lcache_lines = new INT64[lcache_size = 8*1000];
+	lcache_count = 0;
+	lcache_base = 0;
+	lcache_ready = false;
+	lcache_wrap = lcache_wwrap = lcache_width = -1;
+	
+	Search_buffer_size = 3 * (MAX_VIEWLINEB < 8000 ? 8000 : MAX_VIEWLINEB);
+	Search_buffer = new wchar_t[Search_buffer_size];
+
 	memset(&vString, 0, sizeof(vString));
 	vString.lpData = new wchar_t[MAX_VIEWLINEB];
 }
@@ -169,7 +182,8 @@
 	}
 
 	delete[] vString.lpData;
-	delete[] Up_buffer;
+	delete[] Search_buffer;
+	delete[] lcache_lines;
 	delete[] vread_buffer;
 
 	_tran(SysLog(L"[%p] Viewer::~Viewer, TempViewName=[%s]",this,TempViewName));
@@ -192,11 +206,13 @@
 		}
 	}
 
-	for (int i=0; i<=MAXSCRY; i++)
+	for (int i=MAXSCRY; i >= 0; --i)
 	{
-		delete [] Strings[i]->lpData;
+		if (Strings[i]->lpData)
+			delete[] Strings[i]->lpData;
 		delete Strings[i];
 	}
+	delete[] Strings;
 
 	if (!OpenFailed && bVE_READ_Sent)
 	{
@@ -216,7 +232,7 @@
 	Opt.ViOpt.ViewerIsWrap=VM.Wrap;
 	Opt.ViOpt.ViewerWrap=VM.WordWrap;
 	Opt.ViOpt.SearchRegexp=LastSearchRegexp;
-	InitHex=VM.Hex;
+	//InitHex=VM.Hex;
 }
 
 
@@ -229,7 +245,7 @@
 	ViewFile.Close();
 	Reader.Clear();
 
-	SelectSize = 0; // ������� ��������
+	SelectSize = -1; // ������� ��������
 	strFileName = Name;
 
 	if (Opt.OnlyEditorViewerUsed && !StrCmp(strFileName, L"-"))
@@ -302,7 +318,7 @@
 		// �������� ������������� ��� ��� ���������� �� �� ������ �������
 		if (CachedCodePage && !IsCodePageSupported(CachedCodePage))
 			CachedCodePage = 0;
-		LastSelPos=FilePos=NewFilePos;
+		LastSelectPos=FilePos=NewFilePos;
 		LeftPos=NewLeftPos;
 	}
 	else
@@ -350,19 +366,13 @@
 			CodePageChangedByUser=TRUE;
 		}
 
-		if (!IsUnicodeOrUtfCodePage(VM.CodePage))
-		{
-			ViewFile.SetPointer(0, nullptr, FILE_BEGIN);
-		}
+		ViewFile.SetPointer(0, nullptr, FILE_BEGIN);
 	}
 	SetFileSize();
 
-	if (FilePos>FileSize)
+	if (FilePos > FileSize)
 		FilePos=0;
 
-//  if (ViOpt.AutoDetectTable && !TableChangedByUser)
-//  {
-//  }
 	ChangeViewKeyBar();
 	AdjustWidth();
 	CtrlObject->Plugins.CurViewer=this; // HostFileViewer;
@@ -379,12 +389,12 @@
 		DriveType = DRIVE_CDROM;
 	switch (DriveType) //??? make it configurable
 	{
-		case DRIVE_REMOVABLE: update_check_period = -1;   break; // flash drive or floppy: never
-		case DRIVE_FIXED:     update_check_period = +1;   break; // hard disk: 1msec
-		case DRIVE_REMOTE:    update_check_period = 1000; break; // network drive: 1sec
-		case DRIVE_CDROM:     update_check_period = -1;   break; // cd/dvd: never
-		case DRIVE_RAMDISK:   update_check_period = +1;   break; // ramdrive: 1msec
-		default:              update_check_period = -1;   break; // unknown: never
+		case DRIVE_REMOVABLE: update_check_period = -1;  break; // flash drive or floppy: never
+		case DRIVE_FIXED:     update_check_period = +1;  break; // hard disk: 1 msec
+		case DRIVE_REMOTE:    update_check_period = 500; break; // network drive: 0.5 sec
+		case DRIVE_CDROM:     update_check_period = -1;  break; // cd/dvd: never
+		case DRIVE_RAMDISK:   update_check_period = +1;  break; // ramdrive: 1 msec
+		default:              update_check_period = -1;  break; // unknown: never
 	}
 
 	return TRUE;
@@ -428,8 +438,9 @@
 		SetCursorType(0,10);
 
 	vseek(FilePos,SEEK_SET);
+	LastPage = 0;
 
-	if (!SelectSize)
+	if ( SelectSize < 0 )
 		SelectPos=FilePos;
 
 	switch (nMode)
@@ -471,7 +482,7 @@
 			}
 			FilePos = Strings[0]->nFilePos;
 			SecondPos = Strings[1]->nFilePos;
-			Strings[Y2-Y1]->nFilePos = Strings[Y2-Y1-1]->nFilePos + Strings[Y2-Y1-1]->nbytes;
+			Strings[Y2-Y1]->nFilePos = Strings[Y2-Y1-1]->nFilePos + Strings[Y2-Y1-1]->linesize;
 			vseek(Strings[Y2-Y1]->nFilePos, SEEK_SET);
 			ReadString(Strings[Y2-Y1],-1);
 			break;
@@ -487,21 +498,14 @@
 
 			if (StrLen > LeftPos)
 			{
-				if (IsUnicodeOrUtfCodePage(VM.CodePage) && Signature && !I && !Strings[I]->nFilePos)
-				{
-					FS<<fmt::LeftAlign()<<fmt::Width(Width)<<fmt::Precision(Width)<<&Strings[I]->lpData[static_cast<size_t>(LeftPos+1)];
-				}
-				else
-				{
-					FS<<fmt::LeftAlign()<<fmt::Width(Width)<<fmt::Precision(Width)<<&Strings[I]->lpData[static_cast<size_t>(LeftPos)];
-				}
+				FS<<fmt::LeftAlign()<<fmt::Width(Width)<<fmt::Precision(Width)<<&Strings[I]->lpData[static_cast<size_t>(LeftPos)];
 			}
 			else
 			{
 				FS<<fmt::Width(Width)<<L"";
 			}
 
-			if (SelectSize && Strings[I]->bSelection)
+			if ( SelectSize >= 0 && Strings[I]->bSelection)
 			{
 				__int64 SelX1;
 
@@ -532,7 +536,7 @@
 					if (LeftPos > Strings[I]->nSelEnd)
 						Length = 0;
 
-					FS<<fmt::Precision(static_cast<int>(Length))<<&Strings[I]->lpData[static_cast<size_t>(SelX1+LeftPos+SelectPosOffSet)];
+					FS<<fmt::Precision(static_cast<int>(Length))<<&Strings[I]->lpData[static_cast<size_t>(SelX1+LeftPos)];
 				}
 			}
 
@@ -571,6 +575,9 @@
 	bool bSelStartFound = false, bSelEndFound = false;
 	__int64 HexLeftPos=((LeftPos>80-ObjWidth) ? Max(80-ObjWidth,0):LeftPos);
 
+	const wchar_t BorderLine[] = {BoxSymbols[BS_V1],L' ',0};
+	int border_len = (int)wcslen(BorderLine);
+
 	for (EndFile=0,Y=Y1; Y<=Y2; Y++)
 	{
 		bSelStartFound = false;
@@ -588,12 +595,9 @@
 		if (Y==Y1+1 && !veof())
 			SecondPos=vtell();
 
-		INT64 Ptr=ViewFile.GetPointer();
-		_snwprintf(OutStr,ARRAYSIZE(OutStr),L"%010I64X: ", Ptr);
+		int out_len = _snwprintf(OutStr,ARRAYSIZE(OutStr),L"%010I64X: ", vtell());
+		SelEnd = SelStart = out_len;
 		TextPos=0;
-		int HexStrStart = (int)wcslen(OutStr);
-		SelStart = HexStrStart;
-		SelEnd = SelStart;
 		__int64 fpos = vtell();
 
 		if (fpos > SelectPos)
@@ -602,103 +606,108 @@
 		if (fpos < SelectPos+SelectSize-1)
 			bSelEndFound = true;
 
-		if (!SelectSize)
+		if ( SelectSize < 0 )
 			bSelStartFound = bSelEndFound = false;
 
-		const wchar_t BorderLine[]={BoxSymbols[BS_V1],L' ',0};
+		unsigned char line[16+3];
+		DWORD nr = 0;
+		DWORD nb = CP_UTF8 == VM.CodePage ? 16+3 : 16;
+		Reader.Read(line, nb, &nr);
+		if (nr > 16)
+			Reader.Unread(nr-16);
 
-		int out_len = (int)wcslen(OutStr);
-		int border_len = (int)wcslen(BorderLine);
+		LastPage = EndFile = (nr <= nb) && veof() ? 1 : 0;
 
-		if (IsUnicodeCodePage(VM.CodePage))
+		if (nr <= 0)
 		{
-			wchar_t line[8];
-			int nr = vread(line, 8);
-			LastPage = EndFile = veof() ? 1 : 0;
-			if (nr <= 0)
+			*OutStr = L'\0';
+		}
+		else
+		{
+			if (IsUnicodeCodePage(VM.CodePage))
 			{
-				*OutStr = L'\0';
-			}
-			else
-			{
-				for (X=0; X<8; X++)
+				int be = VM.CodePage == CP_REVERSEBOM ? 1 : 0;
+				for (X=0; X<16; X += 2)
 				{
-					if (SelectSize>0 && (SelectPos == fpos))
+					if (SelectSize >= 0 && (SelectPos == fpos || SelectPos == fpos+1))
 					{
+						bool half = SelectPos != fpos;
 						bSelStartFound = true;
-						SelStart = (int)wcslen(OutStr);
-						SelSize=SelectSize;
+						SelStart = out_len + (half ? 1+be : 0);
+						if ( 0 == (SelSize=SelectSize) )
+							SelStart += (half ? be : 0) - 1;
 					}
-					if (SelectSize>0 && (fpos == (SelectPos+SelectSize-1)))
+					if (SelectSize >= 0 && (fpos == SelectPos+SelectSize-1 || fpos+1 == SelectPos+SelectSize-1))
 					{
+						bool half = fpos == SelectPos+SelectSize-1;
 						bSelEndFound = true;
-						SelEnd = (int)wcslen(OutStr)+3;
-						SelSize=SelectSize;
+						SelEnd = out_len+3 - (half ? 1+be : 0);
+						if ( 0 == (SelSize=SelectSize) )
+							SelEnd = SelStart;
 					}
+					else if ( SelectSize == 0 && (SelectPos == fpos || SelectPos == fpos+1) )
+					{
+						bSelEndFound = true;
+						SelSize = 0;
+						SelEnd = SelStart;
+					}
 
-					if (X < nr)
+					if ((DWORD)X < nr-1) // full character
 					{
-						unsigned char b1 = HIBYTE(line[X]), b2 = LOBYTE(line[X]);
-#if 0																 // !!! ��������� �������� !!!
-						if (VM.CodePage != CP_REVERSEBOM) // ��� ������ � ���� ����
-						{                                 // ������� �������� ������������� ���
-							unsigned char t = b1; b1 = b2; b2 = t;
-						}
-#endif
-						_snwprintf(OutStr+out_len, ARRAYSIZE(OutStr)-out_len, L"%02X%02X ", b1, b2);
-						TextStr[TextPos++] = line[X] ? line[X] : L' ';
+						unsigned ch = line[X+be] + (line[X+1-be] << 8);
+						_snwprintf(OutStr+out_len, ARRAYSIZE(OutStr)-out_len, L"%04X ", ch);
+						TextStr[TextPos++] = ch ? (wchar_t)ch : L' ';
 					}
-					else
+					else if ((DWORD)X == nr-1) // half character only
 					{
+						int o1 = 2 * (1 - be);
+						_snwprintf(OutStr+out_len+o1, ARRAYSIZE(OutStr)-out_len-o1, L"%02X", line[X]);
+						OutStr[out_len+2-o1] = OutStr[out_len+2-o1+1] = L'x';
+						OutStr[out_len+4] = L' ';
+						TextStr[TextPos++] = REPLACE_CHAR;
+					}
+					else // no character
+					{
 						wcscpy(OutStr+out_len, L"     ");
 						TextStr[TextPos++] = L' ';
 					}
 					out_len += 5;
 
-					if (X == 3)
+					if (X == 3*2)
 					{
 						wcscpy(OutStr+out_len, BorderLine);
 						out_len += border_len;
 					}
-					++fpos;
+					fpos += 2;
 				}
 			}
-		}
-		else
-		{
-			unsigned char line[16+3];
-			DWORD nr = 0;
-			DWORD nb = CP_UTF8 == VM.CodePage ? 16+3 : 16;
-			Reader.Read(line, nb, &nr);
-			if (nr > 16)
-				Reader.Unread(nr-16);
-
-			LastPage = EndFile = (nr <= nb) && veof() ? 1 : 0;
-
-			if (!nr)
-			{
-				*OutStr = L'\0';
-			}
 			else
 			{
-				if (SelectSize)
+				if ( SelectSize >= 0 )
 				{
 					if (SelectPos >= fpos && SelectPos < fpos+16)
 					{
 						int off = (int)(SelectPos - fpos);
 						bSelStartFound = true;
 						SelStart = out_len + 3*off + (off < 8 ? 0 : border_len);
-						SelSize = SelectSize;
+						if ( 0 == (SelSize=SelectSize) )
+							--SelStart;
 					}
 					__int64 selectEnd = SelectPos + SelectSize - 1;
 					if (selectEnd >= fpos && selectEnd < fpos+16)
 					{
 						int off = (int)(selectEnd - fpos);
 						bSelEndFound = true;
-						SelEnd = out_len + 3*off + (off < 8 ? 0 : border_len) + 1;
-						SelSize = SelectSize;
+						SelEnd = (0 == (SelSize=SelectSize) ? SelStart : out_len + 3*off + (off < 8 ? 0 : border_len) + 1);
 					}
+					else if ( SelectSize == 0 && SelectPos == fpos )
+					{
+						bSelEndFound = true;
+						SelSize = 0;
+						SelEnd = SelStart;
+					}
 				}
+
 				for (X=0; X<16; X++)
 				{
 					int off = out_len + 3*X + (X < 8 ? 0 : border_len);
@@ -712,6 +721,7 @@
 					if (CP_UTF8 != VM.CodePage && X < (int)nr && line[X])
 						MultiByteToWideChar(VM.CodePage, 0, (LPCSTR)line+X, 1, TextStr+X, 1);
 				}
+				out_len += 3*16 + border_len;
 
 				if (CP_UTF8 == VM.CodePage)
 				{
@@ -730,7 +740,7 @@
 							first = false;
 							TextStr[ib++] = w1[iw] ? w1[iw] : L' ';
 						}
-						int clen = WideCharToMultiByte(CP_UTF8, 0, w2+iw, 1, nullptr, 0, nullptr, nullptr);
+						int clen = WideCharToMultiByte(CP_UTF8, 0, w2+iw, 1, NULL,0, NULL,NULL);
 						while (--clen > 0 && ib < 16)
 							TextStr[ib++] = CONTINUE_CHAR;
 						++iw;
@@ -740,14 +750,14 @@
 			}
 		}
 
-		TextStr[TextPos]=0;
-		wcscat(TextStr,L" ");
+		TextStr[TextPos] = L' ';
+		TextStr[TextPos+1] = L'\0';
 
-		if ((SelEnd <= SelStart) && bSelStartFound)
-			SelEnd = (int)wcslen(OutStr)-2;
+		if ((SelEnd <= SelStart) && bSelStartFound && bSelEndFound && SelectSize > 0 )
+			SelEnd = out_len-2;
 
-		wcscat(OutStr,L" ");
-		wcscat(OutStr,TextStr);
+		OutStr[out_len] = L' ';
+		wcscpy(OutStr+out_len+1, TextStr);
 
 		if (StrLength(OutStr)>HexLeftPos)
 		{
@@ -789,7 +799,7 @@
 		{
 			UINT64 Total=FileSize/16+((FileSize%16)?1:0);
 			UINT64 Top=FilePos/16+((FilePos%16)?1:0);
-ScrollBarEx(X2+(m_bQuickView?1:0),Y1,Y2-Y1+1,LastPage?Top?Total:0:Top,Total);
+			ScrollBarEx(X2+(m_bQuickView?1:0),Y1,Y2-Y1+1,LastPage?Top?Total:0:Top,Total);
 		}
 	}
 }
@@ -837,6 +847,15 @@
 	return L'\0' == ch || IsSpace(ch);
 }
 
+static bool is_word_div ( const wchar_t ch )
+{
+	static const wchar_t spaces[] = { L' ', L'\t', L'\n', L'\r', BOM_CHAR, REPLACE_CHAR, L'\0' };
+	return ( !ch
+		|| nullptr != wcschr(spaces, ch)
+		|| nullptr != wcschr(Opt.strWordDiv, ch)
+	);
+}
+
 static inline int wrapped_char( const wchar_t ch )
 {
 	static const wchar_t wrapped_chars[] = L",;>)"; // word-wrap enabled after it
@@ -849,18 +868,31 @@
 		return -1;
 }
 
-void Viewer::ReadString(ViewerString *pString, int MaxSize)
+static inline int getCharSize( UINT cp )
 {
+	if ( CP_UTF8 == cp )
+		return -1;
+	else if ( CP_UNICODE == cp || CP_REVERSEBOM == cp )
+		return +2;
+	else
+		return +1;
+}
+
+void Viewer::ReadString( ViewerString *pString, int MaxSize, bool update_cache )
+{
 	AdjustWidth();
 
 	int OutPtr = 0, nTab = 0, wrap_out = -1;
 	wchar_t ch, eol_char = L'\0';
-	__int64 fpos, sel_end, wrap_pos = -1;
+	INT64 fpos=0, fpos1, sel_end, wrap_pos = -1;
 	bool skip_space = false;
 
+	if ( !pString->lpData )
+		pString->lpData = new wchar_t[MAX_VIEWLINEB];
+
 	if (VM.Hex)
 	{
-		vseek(IsUnicodeCodePage(VM.CodePage) ? 8 : 16, FILE_CURRENT);
+		vseek(16, FILE_CURRENT);
 		pString->lpData[OutPtr] = L'\0';
 		LastPage = veof();
 		return;
@@ -869,10 +901,12 @@
 	bool bSelStartFound = false, bSelEndFound = false;
 	pString->bSelection = false;
 	sel_end = SelectPos + SelectSize;
-	fpos = vtell();
 
+	fpos1 = vtell();
 	for (;;)
 	{
+		fpos = fpos1;
+
 		if (OutPtr >= MAX_VIEWLINE)
 			break;
 
@@ -880,27 +914,31 @@
 			ch = L' ';
 		else
 		{
-			fpos = vtell();
 			if (!MaxSize-- || !vgetc(&ch))
 				break;
 		}
+		fpos1 = vtell();
 
-		if (SelectSize > 0)
+		if (SelectSize >= 0)
 		{
-			if (fpos == SelectPos)
+			if (fpos == SelectPos || (fpos < SelectPos && fpos1 > SelectPos))
 			{
 				pString->nSelStart = OutPtr;
 				bSelStartFound = true;
 			}
-			if (fpos == sel_end)
+			if (fpos == sel_end || (fpos < sel_end && fpos1 > sel_end))
 			{
-				pString->nSelEnd = OutPtr;
+				pString->nSelEnd = OutPtr + (fpos < sel_end ? 1 : 0);
 				bSelEndFound = true;
 			}
 		}
 
-		if (L'\t' == ch)
+		if (!fpos && BOM_CHAR == ch)
 		{
+			continue; // skip BOM
+		}
+		else if (L'\t' == ch)
+		{
 			nTab = ViOpt.TabSize - (OutPtr % ViOpt.TabSize);
 			continue;
 		}
@@ -913,7 +951,7 @@
 		pString->lpData[OutPtr++] = ch ? ch : L' ';
 		if (!VM.Wrap)
 			continue;
-
+	
 		if (OutPtr >= Width)
 		{
 			if (VM.WordWrap && wrap_out > 0)
@@ -965,9 +1003,12 @@
 
 	pString->have_eol = (eol_char != L'\0');
 	pString->lpData[(int)OutPtr]=0;
-	pString->nbytes = (int)(vtell() - pString->nFilePos);
+	pString->linesize = (int)(vtell() - pString->nFilePos);
 
-	if (SelectSize > 0 && OutPtr > 0)
+	if ( update_cache )
+		CacheLine(pString->nFilePos, pString->linesize, pString->have_eol);
+
+	if (SelectSize >= 0 && OutPtr > 0)
 	{
 		if (!bSelStartFound && pString->nFilePos >= SelectPos && pString->nFilePos <= sel_end)
 		{
@@ -987,8 +1028,86 @@
 
 	if (!eol_char && veof())
 		LastPage = 1;
+}	
+
+
+__int64 Viewer::EndOfScreen( int line )
+{
+	__int64 pos;
+
+	if (!VM.Hex)
+	{
+		pos = Strings[Y2-Y1+line]->nFilePos + Strings[Y2-Y1+line]->linesize;
+		if ( !line && !VM.Wrap && Strings[Y2-Y1]->linesize > 0 )
+		{
+			vseek(Strings[Y1-Y2]->nFilePos, SEEK_SET);
+			int col = 0, rmargin = (int)LeftPos + Width;
+			wchar_t ch;
+			for (;;)
+			{
+				if ( !vgetc(&ch) )
+					break;
+				if ( ch == L'\n' || ch == L'\r' )
+					break;
+				if ( ch == L'\t' )
+					col += ViOpt.TabSize - (col % ViOpt.TabSize);
+				else
+					++col;
+				if ( col >= rmargin )
+				{
+					pos = vtell();
+					break;
+				}
+			}
+		}
+	}
+	else
+		pos =	FilePos + 16 * (Y2-Y1+1+line);
+		
+   if (pos < 0)
+		pos = 0;
+	else if (pos > FileSize)
+		pos = FileSize;
+
+	return pos;
 }
 
+
+__int64 Viewer::BegOfScreen()
+{
+	__int64 pos = FilePos, prev_pos;
+
+	if (!VM.Hex && !VM.Wrap && LeftPos > 0)
+	{
+		vseek(FilePos, SEEK_SET);
+		int col = 0;
+		wchar_t ch;
+		pos = -1;
+		for (;;)
+		{
+			prev_pos = vtell();
+			if ( !vgetc(&ch) )
+				break;
+			if ( ch == L'\n' || ch == L'\r' )
+			{
+				pos = Strings[1]->nFilePos;
+				break;
+			}
+			if ( ch == L'\t' )
+				col += ViOpt.TabSize - (col % ViOpt.TabSize);
+			else
+				++col;
+			if ( col > LeftPos )	//!! ������ �������� ����� ������
+				break;				//!! ��� LeftPos=1 �� ���� 2 �������
+		}
+		if ( pos < 0 )
+			pos = (col > LeftPos ? prev_pos : vtell());
+	}
+
+	return pos;
+}
+
+
 __int64 Viewer::VMProcess(int OpCode,void *vParam,__int64 iParam)
 {
 	switch (OpCode)
@@ -996,7 +1115,7 @@
 		case MCODE_C_EMPTY:
 			return (__int64)!FileSize;
 		case MCODE_C_SELECTED:
-			return (__int64)(SelectSize?TRUE:FALSE);
+			return (__int64)(SelectSize >= 0 ?TRUE:FALSE);
 		case MCODE_C_EOF:
 			return (__int64)(LastPage || !ViewFile.Opened());
 		case MCODE_C_BOF:
@@ -1030,7 +1149,7 @@
 	*/
 	if (!ViOpt.PersistentBlocks &&
 	        Key!=KEY_IDLE && Key!=KEY_NONE && !(Key==KEY_CTRLINS||Key==KEY_CTRLNUMPAD0) && Key!=KEY_CTRLC)
-		SelectSize=0;
+		SelectSize = -1;
 
 	if (!InternalKey && !LastKeyUndo && (FilePos!=UndoData[0].UndoAddr || LeftPos!=UndoData[0].UndoLeft))
 	{
@@ -1055,7 +1174,6 @@
 		{
 			FilePos=BMSavePos.FilePos[Pos];
 			LeftPos=BMSavePos.LeftPos[Pos];
-//      LastSelPos=FilePos;
 			Show();
 		}
 
@@ -1082,25 +1200,20 @@
 		}
 		case KEY_CTRLU:
 		{
-//      if (SelectSize)
-			{
-				SelectSize = 0;
-				Show();
-			}
+			SelectSize = -1;
+			Show();
 			return TRUE;
 		}
 		case KEY_CTRLC:
 		case KEY_CTRLINS:  case KEY_CTRLNUMPAD0:
 		{
-			if (SelectSize && ViewFile.Opened())
+			if (SelectSize >= 0 && ViewFile.Opened())
 			{
-				wchar_t *SelData;
-				size_t DataSize = (size_t)SelectSize+(IsUnicodeCodePage(VM.CodePage)?sizeof(wchar_t):1);
-				__int64 CurFilePos=vtell();
-
-				if ((SelData=(wchar_t*)xf_malloc(DataSize*sizeof(wchar_t))) )
+				wchar_t *SelData = (wchar_t*)xf_malloc((size_t)SelectSize+1);
+				if ( SelData )
 				{
-					wmemset(SelData, 0, DataSize);
+					__int64 CurFilePos=vtell();
+					wmemset(SelData, 0, (size_t)SelectSize+1);
 					vseek(SelectPos,SEEK_SET);
 					vread(SelData, (int)SelectSize);
 					CopyToClipboard(SelData);
@@ -1108,7 +1221,6 @@
 					vseek(CurFilePos,SEEK_SET);
 				}
 			}
-
 			return TRUE;
 		}
 		//   �����/����� ��������
@@ -1145,6 +1257,8 @@
 
 					Reader.Clear(); // ���� ���� �� ��� ����?
 					ViewFile.FlushBuffers();
+					vseek(0, SEEK_CUR); // reset vgetc state
+					lcache_ready = false; // reset start-lines cache
 
 					if (FilePos>FileSize)
 					{
@@ -1251,19 +1365,16 @@
 		}
 		case KEY_SHIFTF2:
 		{
-			LastPage = 0;
 			ProcessTypeWrapMode(!VM.WordWrap);
 			return TRUE;
 		}
 		case KEY_F2:
 		{
-			LastPage = 0;
 			ProcessWrapMode(!VM.Wrap);
 			return TRUE;
 		}
 		case KEY_F4:
 		{
-			LastPage = 0;
 			ProcessHexMode(!VM.Hex);
 			return TRUE;
 		}
@@ -1280,23 +1391,13 @@
 		}
 		case KEY_ALTF7:
 		{
-			SearchFlags.Set(REVERSE_SEARCH);
-			Search(1,0);
-			SearchFlags.Clear(REVERSE_SEARCH);
+			Search(-1,0);
 			return TRUE;
 		}
 		case KEY_F8:
 		{
-			LastPage = 0;
-			if (IsUnicodeCodePage(VM.CodePage))
-			{
-				FilePos*=2;
-				SetFileSize();
-				SelectPos = 0;
-				SelectSize = 0;
-			}
-
 			VM.CodePage = VM.CodePage==GetOEMCP() ? GetACP() : GetOEMCP();
+			lcache_ready = false;
 			ChangeViewKeyBar();
 			Show();
 			CodePageChangedByUser=TRUE;
@@ -1304,12 +1405,12 @@
 		}
 		case KEY_SHIFTF8:
 		{
-			LastPage = 0;
 			UINT nCodePage = SelectCodePage(VM.CodePage, true, true, false, true);
+			lcache_ready = false;
 
-			if (nCodePage!=(UINT)-1)
+			if (nCodePage != static_cast<UINT>(-1))
 			{
-				if (nCodePage == (WORD)(CP_AUTODETECT & 0xffff))
+				if (nCodePage == (CP_AUTODETECT & 0xffff))
 				{
 					__int64 fpos = vtell();
 					bool detect = GetFileFormat(ViewFile,nCodePage,&Signature,true) && IsCodePageSupported(nCodePage);
@@ -1318,20 +1419,6 @@
 						nCodePage = Opt.ViOpt.AnsiCodePageAsDefault ? GetACP() : GetOEMCP();
 				}
 				CodePageChangedByUser=TRUE;
-
-				if (IsUnicodeCodePage(VM.CodePage) && !IsUnicodeCodePage(nCodePage))
-				{
-					FilePos*=2;
-					SelectPos = 0;
-					SelectSize = 0;
-				}
-				else if (!IsUnicodeCodePage(VM.CodePage) && IsUnicodeCodePage(nCodePage))
-				{
-					FilePos=(FilePos+(FilePos&1))/2; //????
-					SelectPos = 0;
-					SelectSize = 0;
-				}
-
 				VM.CodePage=nCodePage;
 				SetFileSize();
 				ChangeViewKeyBar();
@@ -1402,11 +1489,10 @@
 		{
 			if (FilePos>0 && ViewFile.Opened())
 			{
-				Up();	// LastPage = 0
+				Up(1);	// LastPage = 0
 
 				if (VM.Hex)
 				{
-					//FilePos&=~(IsUnicodeCodePage(VM.CodePage) ? 0x7:0xf);
 					Show();
 				}
 				else
@@ -1414,7 +1500,7 @@
 					ShowPage(SHOW_UP);
 					ViewerString *end = Strings[Y2-Y1];
 					LastPage = end->nFilePos >= FileSize ||
-					   (!end->have_eol && end->nFilePos + end->nbytes >= FileSize);
+						(!end->have_eol && end->nFilePos + end->linesize >= FileSize);
 				}
 			}
 
@@ -1439,9 +1525,7 @@
 		{
 			if (ViewFile.Opened())
 			{
-				for (int i=Y1; i<Y2; i++)
-					Up();
-
+				Up(Y2-Y1);
 				Show();
 			}
 
@@ -1452,25 +1536,25 @@
 			if (LastPage || !ViewFile.Opened())
 				return TRUE;
 
-			if (VM.Hex)
-				FilePos += (IsUnicodeCodePage(VM.CodePage) ? 8 : 16) * (Y2-Y1);
-			else
-				FilePos = Strings[Y2-Y1]->nFilePos;
+			FilePos = EndOfScreen(-1); // start of last screen line
 
 			if (Key == KEY_CTRLDOWN)
 			{
-				vseek(FilePos, SEEK_SET);
+				vseek(vString.nFilePos = FilePos, SEEK_SET);
 				for (int i=Y1; i<=Y2; i++)
-				ReadString(&vString,-1);
+				{
+					ReadString(&vString,-1);
+					vString.nFilePos += vString.linesize;
+				}
 
 				if (LastPage)
-			{
-				InternalKey++;
-				ProcessKey(KEY_CTRLPGDN);
-				InternalKey--;
-				return TRUE;
+				{
+					InternalKey++;
+					ProcessKey(KEY_CTRLPGDN);
+					InternalKey--;
+					return TRUE;
+				}
 			}
-			}
 
 			Show();
 			return TRUE;
@@ -1504,17 +1588,11 @@
 			{
 				if (VM.Hex)
 				{
-					FilePos--;
-
-					if (FilePos<0)
-						FilePos=0;
+					FilePos = FilePos > 0 ? FilePos-1 : 0;
 				}
 				else
 				{
-					LeftPos-=20;
-
-					if (LeftPos<0)
-						LeftPos=0;
+					LeftPos = LeftPos > 20 ? LeftPos-20 : 0;
 				}
 
 				Show();
@@ -1528,10 +1606,7 @@
 			{
 				if (VM.Hex)
 				{
-					FilePos++;
-
-					if (FilePos >= FileSize)
-						FilePos=FileSize-1; //??
+					FilePos = FilePos < FileSize-1 ? FilePos+1 : FileSize-1;
 				}
 				else if (!VM.Wrap)
 				{
@@ -1592,7 +1667,6 @@
 			if (ViewFile.Opened())
 			{
 				FilePos=0;
-				LastPage = 0;
 				Show();
 			}
 
@@ -1614,32 +1688,33 @@
 				   ��������� End (� ������) �� ����� ������ �������� �� ���������
 				   Down.
 				*/
-				unsigned int max_counter=Y2-Y1;
+				int max_counter = Y2 - Y1;
 
 				if (VM.Hex)
 				{
 					vseek(0,SEEK_END);
 					FilePos = vtell();
-					int line_chars = IsUnicodeCodePage(VM.CodePage) ? 8 : 16;
-					if ((FilePos % line_chars) == 0)
-						FilePos -= line_chars * (Y2 - Y1 + 1);
+					if ((FilePos % 16) == 0)
+						FilePos -= 16 * (Y2 - Y1 + 1);
 					else
-						FilePos -= (FilePos % line_chars) + line_chars * (Y2 - Y1);
+						FilePos -= (FilePos % 16) + 16 * (Y2 - Y1);
 					if (FilePos < 0)
 						FilePos = 0;
 				}
 				else
 				{
-					vseek(-1,SEEK_END);
-					WCHAR LastSym=0;
+					int ch_size = getCharSize(VM.CodePage);
+					wchar_t LastSym = L'\0';
 
-					if (vgetc(&LastSym) && LastSym!=L'\n' && LastSym!=L'\r')
+					vseek(0, SEEK_END);
+					FilePos = vtell() - 1;
+					FilePos -= FilePos % ch_size;
+					vseek(FilePos, SEEK_SET);
+					if (vgetc(&LastSym) && LastSym != L'\n' && LastSym != L'\r')
 						++max_counter;
 
 					FilePos=vtell();
-
-					for (int i=0; static_cast<unsigned int>(i)<max_counter; i++)
-						Up();
+					Up(max_counter);
 				}
 
 				Show();
@@ -1688,7 +1763,7 @@
 		{
 			while (IsMouseButtonPressed())
 			{
-//        _SVS(SysLog(L"Viewer/ KEY_DOWN= %i, %i",FilePos,FileSize));
+				//_SVS(SysLog(L"Viewer/ KEY_DOWN= %i, %i",FilePos,FileSize));
 				ProcessKey(KEY_DOWN);
 			}
 		}
@@ -1719,7 +1794,7 @@
 				else
 					Perc=ToPercent64(FilePos,FileSize);
 
-//_SVS(SysLog(L"Viewer/ ToPercent()=%i, %I64d, %I64d, Mouse=[%d:%d]",Perc,FilePos,FileSize,MsX,MsY));
+				//_SVS(SysLog(L"Viewer/ ToPercent()=%i, %I64d, %I64d, Mouse=[%d:%d]",Perc,FilePos,FileSize,MsX,MsY));
 				if (Perc == 100)
 					ProcessKey(KEY_CTRLEND);
 				else if (!Perc)
@@ -1796,140 +1871,270 @@
 	return TRUE;
 }
 
-void Viewer::Up()
+
+void Viewer::CacheLine( __int64 start, int length, bool have_eol )
 {
-	if (!ViewFile.Opened())
+	assert(start >= 0 && length >= 0);
+	if (!length) // empty lines beyond EOF
 		return;
 
-	LastPage = 0;
+	if ( lcache_ready
+	 && (lcache_wrap != VM.Wrap || lcache_wwrap != VM.WordWrap || lcache_width != Width)
+	){
+		lcache_ready = false;
+	}
 
-	if (FilePos <= 0)
-		return;
-
-	if (VM.Hex)
+	if (!lcache_ready || start > lcache_last || start+length < lcache_first)
 	{
-		FilePos -= IsUnicodeCodePage(VM.CodePage) ? 8 : 16;
-		if (FilePos < 0)
-			FilePos = 0;
-		return;
-	}
+		lcache_first = start;
+		lcache_last = start + length;
 
-	int Skipped = 0, I = -1, BufSize = MAX_VIEWLINE + 2;
-	wchar_t *Buf = Up_buffer, *tBuf = Up_buffer, *tBuf2 = nullptr;
+		lcache_count = 2;
+		lcache_base = 0;
+		lcache_lines[0] = (have_eol ? -start : +start);
+		lcache_lines[1] = start + length;
 
-	if (!VM.Hex && VM.CodePage == CP_UTF8)
-	{
-		BufSize = Up_buffer_size / 2; // = 3*MAXLINE + 2;
-		tBuf = tBuf2 = Up_buffer + BufSize;
+		lcache_wrap = VM.Wrap; lcache_wwrap = VM.WordWrap; lcache_width = Width;
+		lcache_ready = true;
 	}
-
-	if (FilePos < (__int64)BufSize)
-		BufSize = (int)FilePos;
-
-	if (BufSize >= 512) // first try to find newline in first 256 bytes
+	else if (start == lcache_last)
 	{
-		vseek(FilePos-256, SEEK_SET);
-		int nr = vread(Buf, 256, tBuf2);
-
-		if (nr > 0 && Buf[nr-1] == L'\n')
+		int i = (lcache_base + lcache_count - 1) % lcache_size;
+		lcache_lines[i] = (have_eol ? -start : +start);
+		i = (i + 1) % lcache_size;
+		lcache_lines[i]	= lcache_last = start + length;
+		if (lcache_count < lcache_size)
+			++lcache_count;
+		else
 		{
-			--nr; ++Skipped;
+			lcache_base = (lcache_base + 1) % lcache_size; // ++start
+			lcache_first = _abs64(lcache_lines[lcache_base]);
 		}
-		if (nr > 0 && Buf[nr-1] == L'\r')
+	}
+	else if (start+length == lcache_first)
+	{
+	   lcache_base = (lcache_base + lcache_size - 1) % lcache_size; // --start
+      lcache_lines[lcache_base] = (have_eol ? -start : +start);
+		lcache_first = start;
+		if (lcache_count < lcache_size)
+			++lcache_count;
+		else
 		{
-			--nr; ++Skipped;
+			int i = (lcache_base + lcache_size - 1) % lcache_size; // i = start - 1
+			lcache_last = _abs64(lcache_lines[i]);
 		}
+	}
+	else
+	{
+#if defined(_DEBUG) && 1 // it is legal case if file changed...
+		assert(start >= lcache_first && start+length <= lcache_last);
+		int i = CacheFindUp(start+length);
+		assert(i >= 0 && _abs64(lcache_lines[i]) == start);
+#endif
+		lcache_first = start;
+		lcache_last = start + length;
 
-		for (I = nr-1; I >= 0; I--)
-		{
-			if (Buf[I] == L'\n' || Buf[I] == L'\r')
-			{
-				BufSize = nr;
-				break;
-			}
-		}
+		lcache_count = 2;
+		lcache_base = 0;
+		lcache_lines[0] = (have_eol ? -start : +start);
+		lcache_lines[1] = start + length;
 	}
+}
 
-	if (I < 0) // full portion read
+int Viewer::CacheFindUp( __int64 start )
+{
+	if ( lcache_ready
+		&& (lcache_wrap != VM.Wrap || lcache_wwrap != VM.WordWrap || lcache_width != Width)
+	){
+		lcache_ready = false;
+	}
+	if ( !lcache_ready || start <= lcache_first || start > lcache_last )
+		return -1;
+
+	int i, j, i1 = 0, i2 = lcache_count - 1;
+   for (;;)
 	{
-		vseek(FilePos - BufSize, SEEK_SET);
-		BufSize = vread(Buf, BufSize, tBuf2);
-		Skipped=0;
+		if ( i1+1 >= i2 )
+			return (lcache_base + i1) % lcache_size;
 
-		if (BufSize > 0 && Buf[BufSize-1]==L'\n')
-		{
-			--BufSize; ++Skipped;
-		}
-		if (BufSize > 0 && Buf[BufSize-1] == L'\r')
-		{
-			--BufSize; ++Skipped;
-		}
-
-		int I0 = BufSize > MAX_VIEWLINE ? BufSize - MAX_VIEWLINE : 0;
-		for (I = BufSize-1; I >= I0; I--)
-		{
-			if (Buf[I] == L'\n' || Buf[I] == L'\r')
-				break;
-		}
+		i = (i1 + i2) / 2;
+		j = (lcache_base + i) % lcache_size;
+		if (_abs64(lcache_lines[j]) < start)
+			i1 = i;
+		else
+			i2 = i;
 	}
+}
 
-	if (!VM.Wrap)
+void Viewer::Up( int nlines )
+{
+	assert( nlines > 0 );
+
+	if (!ViewFile.Opened())
+		return;
+
+   LastPage = 0;
+
+	if (FilePos <= 0)
+		return;
+
+	if (VM.Hex)
 	{
-		FilePos -= GetStrBytesNum(tBuf + (I+1), BufSize-(I+1)) + Skipped;
+		FilePos = FilePos > 16*nlines ? FilePos-16*nlines : 0;
+		return;
 	}
-	else
+
+	__int64 fpos, fpos1;
+
+	int i = CacheFindUp(fpos = FilePos);
+	if ( i >= 0 )
 	{
-		wchar_t *line = tBuf + I+1;
-		int len, line_size = BufSize - (I+1);
 		for (;;)
 		{
-			len = WrappedLength(line, line_size);
-			if ( len >= line_size )
+			fpos = _abs64(lcache_lines[i]);
+			if (--nlines == 0)
+			{
+				FilePos = fpos;
+				return;
+			}
+			if (i == lcache_base)
 				break;
-			line += len;
-			line_size -= len;
+			i = (i + lcache_size - 1) % lcache_size;
 		}
-		FilePos -= GetStrBytesNum(line, line_size + Skipped);
 	}
-	return;
-}
 
-int Viewer::WrappedLength(wchar_t *str, int line_size)
-{
-	int width = 0, off = 0, wrap_pos = -1, wrap_ch, tab_size = ViOpt.TabSize;
-	bool wwrap = VM.Wrap && VM.WordWrap;
+	static const int portion_size = 256;
+	static const int max_backward = 32*1024;
 
-	for (;;)
+	union {
+		char c1[portion_size];
+		wchar_t c2[portion_size/(int)sizeof(wchar_t)];
+	} buff;
+
+	int j, buff_size, nr, ch_size = getCharSize(VM.CodePage);
+	int len[max_backward/40]; // suppose minimum line with is 80 chars...
+
+	while ( nlines > 0 )
 	{
-		if (off >= line_size)
-			return off;
+		if ( fpos <= 0 )
+		{
+			FilePos = 0;
+			return;
+		}
 
-		wchar_t ch = str[off++];
-		width += L'\t' != ch ? 1 : tab_size - width % tab_size;
+		fpos1 = fpos;
 
-		if (width >= Width)
+		// backward CR-LF search
+		//
+		for ( j = 0; j < max_backward/portion_size; ++j )
 		{
-			if (!wwrap || wrap_pos <= 0)
-				return off;
-			while (wrap_pos < line_size && is_space_or_nul(str[wrap_pos]))
-				++wrap_pos;
-			return wrap_pos;
+			buff_size = (fpos > (__int64)portion_size ? portion_size : (int)fpos);
+			fpos -= buff_size;
+			vseek(fpos, SEEK_SET);
+
+			if ( ch_size <= 1 )
+			{
+				DWORD nread = 0;
+				Reader.Read(buff.c1, static_cast<DWORD>(buff_size), &nread);
+				if ((nr = (int)nread) != buff_size)
+				{
+					return; //??? error handling
+				}
+				if ( 0 == j )
+				{
+					if ( nr > 0 && '\n' == buff.c1[nr-1] )
+					{
+						--nr;
+					}
+					if ( nr > 0 && '\r' == buff.c1[nr-1] )
+					{
+						--nr;
+					}
+				}
+				for ( i = nr-1; i >= 0; --i )
+				{
+					if ( '\n' == buff.c1[i] || '\r' == buff.c1[i] )
+						break;
+				}
+				if ( i >= 0 )
+				{
+					fpos += i + 1;
+					break;
+				}
+			}
+			else
+			{
+				nr = vread(buff.c2, buff_size);
+				if ( nr != buff_size / ch_size )
+				{
+					return; //??? error handling
+				}
+				if ( 0 == j )
+				{
+					if ( nr > 0 && L'\n' == buff.c2[nr-1] )
+					{
+						--nr;
+					}
+					if ( nr > 0 && L'\r' == buff.c2[nr-1] )
+					{
+						--nr;
+					}
+				}
+				for ( i = nr-1; i >= 0; --i )
+				{
+					if ( L'\n' == buff.c2[i] || L'\r' == buff.c2[i] )
+						break;
+				}
+				if ( i >= 0 )
+				{
+					fpos += ch_size * (i + 1);
+					break;
+				}
+			}
 		}
 
-		if (!wwrap)
-			continue;
+		// split read portion 
+		//
+		vseek(vString.nFilePos = fpos, SEEK_SET);
+		for (i = 0; i < (int)ARRAYSIZE(len); ++i)
+		{
+			ReadString(&vString, -1, false);
+			len[i] = (vString.have_eol ? -1 : +1) * vString.linesize;
+			if ((vString.nFilePos += vString.linesize) >= fpos1)
+			{
+				fpos1 = vString.nFilePos;
+				break;
+			}
+		}
+		assert(i < (int)ARRAYSIZE(len));
+		if (i >= (int)ARRAYSIZE(len))
+			--i;
 
-		if ((wrap_ch = wrapped_char(ch)) >= 0)
-			wrap_pos = off - 1 + wrap_ch;
+		while ( i >= 0 )
+		{
+			int l = len[i--];
+			bool eol = false;
+			if (l < 0)
+			{
+				eol = true;
+				l = -l;
+			}
+			fpos1 -= l;
+			CacheLine(fpos1, l, eol);
+			if (--nlines == 0)
+				FilePos = fpos1;
+		}
 	}
 }
 
-int Viewer::GetStrBytesNum(const wchar_t *Str, int Length)
+
+int Viewer::GetStrBytesNum( const wchar_t *Str, int Length )
 {
-	if (IsUnicodeCodePage(VM.CodePage))
-		return Length;
+	int ch_size = getCharSize(VM.CodePage);
+	if (ch_size > 0)
+		return Length * ch_size;
 	else
-		return WideCharToMultiByte(VM.CodePage, 0, Str, Length, nullptr, 0, nullptr, nullptr);
+		return WideCharToMultiByte(VM.CodePage,0, Str,Length, nullptr,0, nullptr,nullptr);
 }
 
 void Viewer::SetViewKeyBar(KeyBar *ViewKeyBar)
@@ -1938,7 +2143,6 @@
 	ChangeViewKeyBar();
 }
 
-
 void Viewer::ChangeViewKeyBar()
 {
 	if (ViewKeyBar)
@@ -1971,6 +2175,7 @@
 	CtrlObject->Plugins.CurViewer=this; //HostFileViewer;
 }
 
+
 enum SEARCHDLG
 {
 	SD_DOUBLEBOX,
@@ -1991,9 +2196,18 @@
 
 enum
 {
- DM_SDSETVISIBILITY = DM_USER+1,
+	DM_SDSETVISIBILITY = DM_USER + 1,
+	DM_SDREXVISIBILITY = DM_USER + 2
 };
 
+struct MyDialogData
+{
+   Viewer      *viewer;
+	bool edit_autofocus;
+   bool       hex_mode;
+	bool      recursive;
+};
+
 INT_PTR WINAPI ViewerSearchDlgProc(HANDLE hDlg,int Msg,int Param1,void* Param2)
 {
 	switch (Msg)
@@ -2010,42 +2224,133 @@
 			SendDlgMessage(hDlg,DM_SHOWITEM,SD_EDIT_TEXT,ToPtr(!Param1));
 			SendDlgMessage(hDlg,DM_SHOWITEM,SD_EDIT_HEX,ToPtr(Param1));
 			SendDlgMessage(hDlg,DM_ENABLE,SD_CHECKBOX_CASE,ToPtr(!Param1));
-			SendDlgMessage(hDlg,DM_ENABLE,SD_CHECKBOX_WORDS,ToPtr(!Param1));
-			//SendDlgMessage(hDlg,DM_ENABLE,SD_CHECKBOX_REGEXP,!Param1);
+			int re = SendDlgMessage(hDlg, DM_GETCHECK,SD_CHECKBOX_REGEXP,0) == BSTATE_CHECKED;
+			int ww = !Param1 && !re;
+			SendDlgMessage(hDlg,DM_ENABLE,SD_CHECKBOX_WORDS,ToPtr(ww));
+			SendDlgMessage(hDlg,DM_ENABLE,SD_CHECKBOX_REGEXP,ToPtr(!Param1));
+			SendDlgMessage(hDlg, DM_SDREXVISIBILITY, re && !Param1, 0);
 			return TRUE;
 		}
+		case DM_SDREXVISIBILITY:
+		{
+		   int show = 1;
+			if ( Param1 )
+			{
+				int tlen = (int)SendDlgMessage(hDlg, DM_GETTEXTPTR, SD_EDIT_TEXT, 0);
+				show = 0;
+				if ( tlen > 0 )
+				{
+					RegExp re;
+					wchar_t t[128], *tmp = t;
+					if (tlen > (int)ARRAYSIZE(t))
+						tmp = new wchar_t[tlen];
+					SendDlgMessage(hDlg, DM_GETTEXTPTR, SD_EDIT_TEXT, tmp);
+					string sre = tmp;
+					InsertRegexpQuote(sre);
+					show = re.Compile(sre.CPtr(), OP_PERLSTYLE);
+					if (tmp != t)
+						delete[] tmp;;
+				}
+			}
+			SendDlgMessage(hDlg, DM_ENABLE, SD_TEXT_SEARCH, ToPtr(show));
+			SendDlgMessage(hDlg, DM_ENABLE, SD_BUTTON_OK,   ToPtr(show));
+			return TRUE;
+		}
+		case DN_KILLFOCUS:
+		{
+			if ( SD_EDIT_TEXT == Param1 || SD_EDIT_HEX == Param1 )
+			{
+				MyDialogData *my = (MyDialogData *)SendDlgMessage(hDlg, DM_GETITEMDATA, SD_EDIT_TEXT, 0);
+				my->hex_mode = (SD_EDIT_HEX == Param1);
+			}
+			break;
+		}
 		case DN_BTNCLICK:
 		{
+			bool need_focus = false;
+			MyDialogData *my = (MyDialogData *)SendDlgMessage(hDlg, DM_GETITEMDATA, SD_EDIT_TEXT, 0);
+			int cradio = (my->hex_mode ? SD_RADIO_HEX : SD_RADIO_TEXT);
+
 			if ((Param1 == SD_RADIO_TEXT || Param1 == SD_RADIO_HEX) && Param2)
 			{
-				SendDlgMessage(hDlg, DM_ENABLEREDRAW, FALSE, 0);
-				Viewer *viewer = (Viewer *)SendDlgMessage(hDlg, DM_GETITEMDATA, SD_EDIT_TEXT, 0);
+				need_focus = true;
+				if ( Param1 != cradio)
+				{
+					bool new_hex = (Param1 == SD_RADIO_HEX);
 
-				int sd_dst = Param1 == SD_RADIO_HEX  ? SD_EDIT_HEX : SD_EDIT_TEXT;
-				int sd_src = Param1 == SD_RADIO_TEXT ? SD_EDIT_HEX : SD_EDIT_TEXT;
+					SendDlgMessage(hDlg, DM_ENABLEREDRAW, FALSE, 0);
 
-				const wchar_t *ps = (const wchar_t *)SendDlgMessage(hDlg, DM_GETCONSTTEXTPTR, sd_src, 0);
-				string strTo;
-				viewer->SearchTextTransform(strTo, ps, Param1 == SD_RADIO_TEXT);
+					int sd_dst = new_hex ? SD_EDIT_HEX : SD_EDIT_TEXT;
+					int sd_src = new_hex ? SD_EDIT_TEXT : SD_EDIT_HEX;
 
-				SendDlgMessage(hDlg, DM_SETTEXTPTR, sd_dst, const_cast<wchar_t*>(strTo.CPtr()));
-				SendDlgMessage(hDlg, DM_SDSETVISIBILITY, Param1 == SD_RADIO_HEX, 0);
+					EditorSetPosition esp;
+					esp.CurPos = -1;
+					SendDlgMessage(hDlg, DM_GETEDITPOSITION, sd_src, &esp);
+					const wchar_t *ps = (const wchar_t *)SendDlgMessage(hDlg, DM_GETCONSTTEXTPTR, sd_src, 0);
+					string strTo;
+					my->viewer->SearchTextTransform(strTo, ps, !new_hex, esp.CurPos);
 
-				if (!strTo.IsEmpty())
-				{
-					int changed = (int)SendDlgMessage(hDlg, DM_EDITUNCHANGEDFLAG, sd_src, ToPtr(-1));
-					SendDlgMessage(hDlg, DM_EDITUNCHANGEDFLAG, sd_dst, ToPtr(changed));
+					SendDlgMessage(hDlg, DM_SETTEXTPTR, sd_dst, ToPtr((INT_PTR)strTo.CPtr()));
+					SendDlgMessage(hDlg, DM_SDSETVISIBILITY, new_hex, 0);
+					if (esp.CurPos >= 0)
+					{
+						int p = esp.CurPos;
+						if (SendDlgMessage(hDlg, DM_GETEDITPOSITION, sd_dst, &esp))
+						{
+							esp.CurPos = esp.CurTabPos = p;
+							esp.LeftPos = 0;
+							SendDlgMessage(hDlg, DM_SETEDITPOSITION, sd_dst, &esp);
+						}
+					}
+
+					if (!strTo.IsEmpty())
+					{
+						int changed = (int)SendDlgMessage(hDlg, DM_EDITUNCHANGEDFLAG, sd_src, ToPtr(-1));
+						SendDlgMessage(hDlg, DM_EDITUNCHANGEDFLAG, sd_dst, ToPtr(changed));
+					}
+
+					SendDlgMessage(hDlg,DM_ENABLEREDRAW,TRUE,0);
+					my->hex_mode = new_hex;
+					if ( !my->edit_autofocus )
+						return TRUE;
 				}
+			}
+			else if ( Param1 == SD_CHECKBOX_REGEXP )
+			{
+				SendDlgMessage(hDlg, DM_SDSETVISIBILITY, my->hex_mode, 0);
+			}
 
-				SendDlgMessage(hDlg,DM_ENABLEREDRAW,TRUE,0);
+			if ( my->edit_autofocus && !my->recursive )
+			{
+				if ( need_focus
+				  || Param1 == SD_CHECKBOX_CASE
+				  || Param1 == SD_CHECKBOX_WORDS
+				  || Param1 == SD_CHECKBOX_REVERSE
+				  || Param1 == SD_CHECKBOX_REGEXP
+				){
+					my->recursive = true;
+					SendDlgMessage(hDlg, DM_SETFOCUS, my->hex_mode ? SD_EDIT_HEX : SD_EDIT_TEXT, 0);
+					my->recursive = false;
+				}
+			}
+
+			if (need_focus)
 				return TRUE;
-			}
+			else
+				break;
 		}
+		case DN_EDITCHANGE:
+		{
+			if ( Param1 == SD_EDIT_TEXT && SendDlgMessage(hDlg,DM_GETCHECK,SD_CHECKBOX_REGEXP,0) == BSTATE_CHECKED )
+				SendDlgMessage(hDlg, DM_SDREXVISIBILITY, 1, 0);
+			break;
+		}
 		case DN_HOTKEY:
 		{
 			if (Param1==SD_TEXT_SEARCH)
 			{
-				SendDlgMessage(hDlg,DM_SETFOCUS,(SendDlgMessage(hDlg,DM_GETCHECK,SD_RADIO_HEX,0) == BSTATE_CHECKED)?SD_EDIT_HEX:SD_EDIT_TEXT,0);
+				MyDialogData *my = (MyDialogData *)SendDlgMessage(hDlg, DM_GETITEMDATA, SD_EDIT_TEXT, 0);
+				SendDlgMessage(hDlg, DM_SETFOCUS, (my->hex_mode ? SD_EDIT_HEX : SD_EDIT_TEXT), 0);
 				return FALSE;
 			}
 		}
@@ -2059,10 +2364,13 @@
 static void PR_ViewerSearchMsg()
 {
 	PreRedrawItem preRedrawItem=PreRedraw.Peek();
-	ViewerSearchMsg((const wchar_t*)preRedrawItem.Param.Param1,(int)(INT_PTR)preRedrawItem.Param.Param2);
+	const wchar_t *name = (const wchar_t*)preRedrawItem.Param.Param1;
+	int percent = (int)(INT_PTR)preRedrawItem.Param.Param2;
+	int search_hex = (int)(INT_PTR)preRedrawItem.Param.Param3;
+	ViewerSearchMsg(name, percent, search_hex);
 }
 
-void ViewerSearchMsg(const wchar_t *MsgStr,int Percent)
+void ViewerSearchMsg(const wchar_t *MsgStr, int Percent, int SearchHex)
 {
 	string strProgress;
 
@@ -2093,6 +2401,7 @@
 	PreRedrawItem preRedrawItem=PreRedraw.Peek();
 	preRedrawItem.Param.Param1=(void*)MsgStr;
 	preRedrawItem.Param.Param2=(LPVOID)(INT_PTR)Percent;
+	preRedrawItem.Param.Param3=(LPVOID)(INT_PTR)SearchHex;
 	PreRedraw.SetParam(preRedrawItem.Param);
 }
 
@@ -2109,15 +2418,17 @@
 	}
 }
 
-static int hex2ss(const wchar_t *from, char *c1, int mb)
+static int hex2ss(const wchar_t *from, char *c1, int mb, int *pos = 0)
 {
-	int nb, i, v, sub = 0;
+	int nb, i, v, sub = 0, ps = 0, p0 = (pos ? *pos : -1), p1 = -1;
 	wchar_t ch;
 
 	nb = i = v = 0;
 	for (;;)
 	{
 		ch = *from++;
+		++ps;
+
 		if      (ch >= L'0' && ch <= L'9') sub = L'0';
 		else if (ch >= L'A' && ch <= L'F') sub = L'A' - 10;
 		else if (ch >= L'a' && ch <= L'f') sub = L'a' - 10;
@@ -2125,6 +2436,8 @@
 		{
 			if (i > 0)
 			{
+				if ( p0 >= 0 && ps > p0 && p1 < 0 )
+					p1 = nb;
 				c1[nb++] = (char)v;
 				i = 0;
 			}
@@ -2136,6 +2449,8 @@
 		v = v*16 + ((int)ch - sub);
 		if (++i >= 2)
 		{
+			if ( p0 >= 0 && ps > p0 && p1 < 0 )
+				p1 = nb;
 			c1[nb++] = (char)v;
 			i = 0;
 			if (nb >= mb)
@@ -2145,10 +2460,12 @@
 #ifdef _DEBUG
 	if (nb < mb) c1[nb] = '\0';
 #endif
+	if ( p1 >= 0 && pos )
+		*pos = p1;
 	return nb;
 }
 
-void Viewer::SearchTextTransform( UnicodeString &to, const wchar_t *from, bool hex2text )
+void Viewer::SearchTextTransform( UnicodeString &to, const wchar_t *from, bool hex2text, int &pos )
 {
 	int nb, i, v;
 	char c1[128];
@@ -2156,7 +2473,7 @@
 
 	if (hex2text)
 	{
-		nb = hex2ss(from, c1, ARRAYSIZE(c1));
+		nb = hex2ss(from, c1, ARRAYSIZE(c1), &pos);
 
 		if (IsUnicodeCodePage(VM.CodePage))
 		{
@@ -2171,10 +2488,16 @@
 					ch = 0xffff; // encode L'\0'
 				to += ch;
 			}
+			if ( pos >= 0 )
+				pos /= 2;
 		}
 		else
 		{
 			int nw = MultiByteToWideChar(VM.CodePage,0, c1,nb, ss,ARRAYSIZE(ss));
+			if ( pos >= 0 )
+			{
+				pos = MultiByteToWideChar(VM.CodePage,0, c1,pos, NULL,0);
+			}
 			for (i=0; i < nw; ++i)
 				if (!ss[i])
 					ss[i] = 0xffff;
@@ -2184,8 +2507,12 @@
 	}
 	else // text2hex
 	{
+		int ps = 0, pd = 0, p0 = pos, p1 = -1;
 		while (*from)
 		{
+			if ( ps == p0 )
+				p1 = pd;
+			++ps;
 			ch = *from++;
 			if (0xffff == ch)	// 0xffff - invalid unicode char
 				ch = 0x0000;   // it used to transfer '\0' in zero terminated string
@@ -2199,398 +2526,737 @@
 					nb = 2; c1[0] = (char)HIBYTE(ch); c1[1] = (char)LOBYTE(ch);
 				break;
 				default:
-					nb = WideCharToMultiByte(VM.CodePage,0, &ch, 1, c1, 4, nullptr, nullptr);
+					nb = WideCharToMultiByte(VM.CodePage,0, &ch,1, c1,4, NULL,NULL);
 				break;
 			}
 
 			ss2hex(to, c1, nb, L'\0');
+			pd += nb * 3;
 		}
+		pos = p1;
 	}
 }
 
-/* $ 27.01.2003 VVM
-   + �������� Next ����� �������� ������:
-   0 - ���� �����
-   1 - ��������� ����� �� ������� ������
-   2 - ��������� ����� � ����� �����
-*/
+struct Viewer::search_data
+{
+	INT64 CurPos;
+	INT64 MatchPos;
+	const char *search_bytes;
+	const wchar_t *search_text;
+	int search_len;
+	int  ch_size;
+	bool is_utf8;
+   bool first_Rex;
+	RegExp *pRex;
 
-void Viewer::Search(int Next,int FirstChar)
-{
-	const wchar_t *TextHistoryName=L"SearchText";
-	const wchar_t *HexMask=L"HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH ";
-	FarDialogItem SearchDlgData[]=
+	search_data()
 	{
-		{DI_DOUBLEBOX,3,1,72,11,0,nullptr,nullptr,0,MSG(MViewSearchTitle)},
-		{DI_TEXT,5,2,0,2,0,nullptr,nullptr,0,MSG(MViewSearchFor)},
-		{DI_EDIT,5,3,70,3,0,TextHistoryName,nullptr,DIF_FOCUS|DIF_HISTORY|DIF_USELASTHISTORY,L""},
-		{DI_FIXEDIT,5,3,70,3,0,nullptr,HexMask,DIF_MASKEDIT,L""},
-		{DI_TEXT,3,4,0,4,0,nullptr,nullptr,DIF_SEPARATOR,L""},
-		{DI_RADIOBUTTON,5,5,0,5,1,nullptr,nullptr,DIF_GROUP,MSG(MViewSearchForText)},
-		{DI_RADIOBUTTON,5,6,0,6,0,nullptr,nullptr,0,MSG(MViewSearchForHex)},
-		{DI_CHECKBOX,40,5,0,5,0,nullptr,nullptr,0,MSG(MViewSearchCase)},
-		{DI_CHECKBOX,40,6,0,6,0,nullptr,nullptr,0,MSG(MViewSearchWholeWords)},
-		{DI_CHECKBOX,40,7,0,7,0,nullptr,nullptr,0,MSG(MViewSearchReverse)},
-		{DI_CHECKBOX,40,8,0,8,0,nullptr,nullptr,DIF_DISABLE,MSG(MViewSearchRegexp)},
-		{DI_TEXT,3,9,0,9,0,nullptr,nullptr,DIF_SEPARATOR,L""},
-		{DI_BUTTON,0,10,0,10,0,nullptr,nullptr,DIF_DEFAULTBUTTON|DIF_CENTERGROUP,MSG(MViewSearchSearch)},
-		{DI_BUTTON,0,10,0,10,0,nullptr,nullptr,DIF_CENTERGROUP,MSG(MViewSearchCancel)},
-	};
-	MakeDialogItemsEx(SearchDlgData,SearchDlg);
-	string strSearchStr;
-	char search_bytes[128];
-	string strMsgStr;
-	__int64 MatchPos=0;
-	int SearchLength,Case,WholeWords,ReverseSearch,Match,SearchRegexp;
+		CurPos = MatchPos = -1;
+		search_bytes = nullptr;
+		search_text  = nullptr;
+		search_len = ch_size = 0;
+		is_utf8 = false;
+		first_Rex = true;
+		pRex = nullptr;
+	}
 
-	if (!ViewFile.Opened() || (Next && strLastSearchStr.IsEmpty()))
-		return;
+	~search_data()
+	{
+		 delete pRex;
+	}
+};
 
-	if (!strLastSearchStr.IsEmpty())
-		strSearchStr = strLastSearchStr;
+int Viewer::search_hex_forward( search_data* sd )
+{
+	char *buff = (char *)Search_buffer;
+	const char *search_str = sd->search_bytes;
+	int bsize = Search_buffer_size * static_cast<int>(sizeof(wchar_t)), slen = sd->search_len;
+	INT64 to, cpos = sd->CurPos;
+
+	bool up_half = cpos >= StartSearchPos;
+	if ( up_half )
+	{
+		if ( (to = FileSize) - cpos <= bsize )
+			SetFileSize();
+		to = FileSize;
+	}
 	else
-		strSearchStr.Clear();
+	{
+		to = StartSearchPos + slen - 1;
+		if ( to > FileSize )
+			to = FileSize;
+	}
 
-	SearchDlg[SD_RADIO_TEXT].Selected=!LastSearchHex;
-	SearchDlg[SD_RADIO_HEX].Selected=LastSearchHex;
-	SearchDlg[SD_CHECKBOX_CASE].Selected=LastSearchCase;
-	SearchDlg[SD_CHECKBOX_WORDS].Selected=LastSearchWholeWords;
-	SearchDlg[SD_CHECKBOX_REVERSE].Selected=LastSearchReverse;
-	SearchDlg[SD_CHECKBOX_REGEXP].Selected=LastSearchRegexp;
+	int nb = (to - cpos < bsize ? static_cast<int>(to - cpos) : bsize);
+	vseek(cpos, SEEK_SET);
+	DWORD nr = 0;
+	Reader.Read(buff, static_cast<DWORD>(nb), &nr);
+	int n1 = static_cast<int>(nr);
+	if ( n1 != nb )
+		SetFileSize();
 
-	if (SearchFlags.Check(REVERSE_SEARCH))
-		SearchDlg[SD_CHECKBOX_REVERSE].Selected=!SearchDlg[SD_CHECKBOX_REVERSE].Selected;
+	if ( n1 < slen )
+	{
+		if ( !up_half || StartSearchPos <= 0 )
+			return -1;
+		sd->CurPos = 0;
+		return 0;
+	}
 
-	if (SearchDlg[SD_RADIO_HEX].Selected)
+	char *ps = buff;
+	while ( nullptr != (ps = static_cast<char *>(memchr(ps, search_str[0], n1-slen+1))) )
 	{
-		int nb = hex2ss(strSearchStr.CPtr(), search_bytes, ARRAYSIZE(search_bytes));
-		strSearchStr.Clear();
-		ss2hex(strSearchStr, search_bytes, nb, L'\0');
-		SearchDlg[SD_EDIT_HEX].strData = strSearchStr;
+		if ( slen <= 1 || 0 == memcmp(ps+1, search_str+1, slen-1) )
+		{
+			sd->MatchPos = cpos + static_cast<INT64>(ps - buff);
+			return +1; // found
+		}
+		++ps;
+		n1 = static_cast<int>(nr - (ps - buff)); 
 	}
+
+	sd->CurPos = cpos + nr - slen + 1;
+	return (up_half || sd->CurPos < StartSearchPos ? 0 : -1);
+}
+
+int Viewer::search_hex_backward( search_data* sd )
+{
+	char *buff = (char *)Search_buffer;
+	const char *search_str = sd->search_bytes;
+	int bsize = Search_buffer_size * static_cast<int>(sizeof(wchar_t)), slen = sd->search_len;
+	INT64 to, cpos = sd->CurPos;
+
+	bool lo_half = cpos <= StartSearchPos;
+	if ( lo_half )
+	{
+		to = 0;
+	}
 	else
-		SearchDlg[SD_EDIT_TEXT].strData = strSearchStr;
+	{
+		to = StartSearchPos - slen + 1;
+		if ( to < 0 )
+			to = 0;
+	}
 
-	SearchDlg[SD_EDIT_TEXT].UserData = this;
+	int nb = (cpos - to < bsize ? static_cast<int>(cpos - to) : bsize);
+	vseek(cpos-nb, SEEK_SET);
+	DWORD nr = 0;
+	Reader.Read(buff, static_cast<DWORD>(nb), &nr);
+	int n1 = static_cast<int>(nr);
+	if ( n1 != nb )
+	{
+		SetFileSize();
+		cpos = vtell();
+	}
 
-	if (!Next)
+	if ( n1 < slen )
 	{
-		SearchFlags.Flags = 0;
-		Dialog Dlg(SearchDlg,ARRAYSIZE(SearchDlg),ViewerSearchDlgProc);
-		Dlg.SetPosition(-1,-1,76,13);
-		Dlg.SetHelp(L"ViewerSearch");
+		if ( !lo_half )
+			return -1;
+		SetFileSize();
+		if ( StartSearchPos >= FileSize )
+			return -1;
+		sd->CurPos = FileSize;
+		return 0;
+	}
 
-		if (FirstChar)
+	for ( char *ps = buff + n1 - 1; ps >= buff + slen - 1; --ps )
+	{
+		if ( *ps == search_str[slen-1] )
 		{
-			Dlg.InitDialog();
-			Dlg.Show();
-			Dlg.ProcessKey(FirstChar);
+			if ( slen <= 1 || 0 == memcmp(ps-slen+1, search_str, slen-1) )
+			{
+				sd->MatchPos = static_cast<INT64>(cpos-n1+(ps-slen+1-buff));
+				return +1;
+			}
 		}
+	}
 
-		Dlg.Process();
+	sd->CurPos = cpos - nr + slen - 1;
+	return (lo_half || sd->CurPos > StartSearchPos ? 0 : -1);
+}
 
-		if (Dlg.GetExitCode()!=SD_BUTTON_OK)
-			return;
+int Viewer::search_text_forward( search_data* sd )
+{
+	int bsize = 8192, slen = sd->search_len, ww = (LastSearchWholeWords ? 1 : 0);
+	wchar_t prev_char = L'\0', *buff = Search_buffer, *t_buff = (sd->is_utf8 ? buff + bsize : nullptr);
+	const wchar_t *search_str = sd->search_text;
+	INT64 to1, to, cpos = sd->CurPos;
+
+	vseek(cpos, SEEK_SET);
+	if ( ww )
+		prev_char = vgetc_prev();
+
+	bool up_half = cpos >= StartSearchPos;
+	if ( up_half )
+	{
+		if ( (to = FileSize) - cpos <= bsize )
+			SetFileSize();
+		to = FileSize;
 	}
+	else
+	{
+		to = StartSearchPos;
+		if ( to > FileSize )
+			to = FileSize;
+	}
 
-	SearchHex=SearchDlg[SD_RADIO_HEX].Selected;
-	Case=SearchDlg[SD_CHECKBOX_CASE].Selected;
-	WholeWords=SearchDlg[SD_CHECKBOX_WORDS].Selected;
-	ReverseSearch=SearchDlg[SD_CHECKBOX_REVERSE].Selected;
-	SearchRegexp=SearchDlg[SD_CHECKBOX_REGEXP].Selected;
+   int nb = (to - cpos > bsize ? bsize : static_cast<int>(to - cpos));
+	int nw = vread(buff, nb, t_buff);
+	to1 = vtell();
+	if ( !up_half && nb + 3*(slen+ww) < bsize && !veof() )
+	{
+		int nw1 = vread(buff+nw, 3*(slen+ww), t_buff ? t_buff+nw : nullptr);
+		nw1 = (nw1 > slen+ww-1 ? slen+ww-1 : nw1);
+		nw += nw1;
+		to1 = to + (t_buff ? GetStrBytesNum(t_buff, nw1) : sd->ch_size * nw1);
+	}
 
-	bool t_utf8 = !SearchHex && VM.CodePage == CP_UTF8;
+	int is_eof = (to1 >= FileSize ? 1 : 0), iLast = nw - slen - ww + is_eof;
+	if ( !LastSearchCase )
+		CharUpperBuff(buff, nw);
 
-	if (SearchHex)
+	for ( int i = 0; i <= iLast; ++i )
 	{
-		strSearchStr = SearchDlg[SD_EDIT_HEX].strData;
-		SearchLength = hex2ss(strSearchStr.CPtr(), search_bytes, ARRAYSIZE(search_bytes));
-		strSearchStr.Clear();
-		ss2hex(strSearchStr, search_bytes, SearchLength, L' ');
+		if ( ww )
+		{
+			if ( !is_word_div(i > 0 ? buff[i-1] : prev_char))
+				continue;
+			if ( !(i == iLast && is_eof) && !is_word_div(buff[i+slen]) )
+				continue;
+		}
+		if ( buff[i] != search_str[0]
+		 || (slen > 1 && buff[i+1] != search_str[1])
+		 || (slen > 2 && 0 != wmemcmp(buff+i+2, search_str+2, slen-2))
+		) continue;
+
+		sd->MatchPos = cpos + GetStrBytesNum(t_buff, i);
+		sd->search_len = GetStrBytesNum(t_buff+i, slen);
+		return +1;
 	}
+
+	if ( up_half && is_eof )
+	{
+		sd->CurPos = 0;
+		return (StartSearchPos > 0 ? 0 : -1);
+	}
 	else
 	{
-		strSearchStr = SearchDlg[SD_EDIT_TEXT].strData;
-		SearchLength = (int)strSearchStr.GetLength();
-		size_t pos = 0;
-		while (strSearchStr.Pos(pos, 0xffff))
-			strSearchStr.Replace(pos, 1, L'\0');
+		if ( iLast < 0 || (!up_half && to1 > StartSearchPos) )
+			return -1;
+		sd->CurPos = to1;
+		return 0;
 	}
+}
 
-	strLastSearchStr = strSearchStr;
-	LastSearchHex=SearchHex;
-	LastSearchCase=Case;
-	LastSearchWholeWords=WholeWords;
+int Viewer::search_text_backward( search_data* sd )
+{
+	int bsize = 8192, slen = sd->search_len, ww = (LastSearchWholeWords ? 1 : 0);
+	wchar_t *buff = Search_buffer, *t_buff = (sd->is_utf8 ? buff + bsize : nullptr);
+	const wchar_t *search_str = sd->search_text;
+	INT64 to1, to, cpos = sd->CurPos;
 
-	if (!SearchFlags.Check(REVERSE_SEARCH))
-		LastSearchReverse=ReverseSearch;
+	bool up_half = cpos > StartSearchPos;
+	to = (up_half ? StartSearchPos : 0);
 
-	LastSearchRegexp=SearchRegexp;
+	int nb = (cpos - to > bsize ? bsize : static_cast<int>(cpos- to));
+	if ( up_half && nb + 3*(slen+ww) < bsize && cpos > nb )
+	{
+		if ( sd->ch_size > 0 )
+		{
+			nb += sd->ch_size * (slen + ww - 1);
+			if (cpos < nb)
+				nb = static_cast<int>(cpos);
+		}
+		else
+		{
+			to1 = cpos - nb - 3*(slen + ww - 1);
+			if (to1 < 0)
+				to1 = 0;
+			int nb1 = static_cast<int>(cpos - nb - to1);
+			vseek(to1, SEEK_SET);
+			int nw1 = vread(buff, nb1, t_buff);
+			if ( nw1 > slen + ww - 1 )
+				nb1 = GetStrBytesNum(t_buff + nw1 - (slen + ww - 1), slen + ww - 1);
+			nb += nb1;
+		}
+	}
 
-	if (!SearchLength)
-		return;
+	cpos -= nb;
+	vseek(cpos, SEEK_SET);
+	int nw = vread(buff, nb, t_buff);
+	if ( !LastSearchCase )
+		CharUpperBuff(buff, nw);
 
+	int is_eof = (veof() ? 1 : 0), iFirst = ww * (cpos > 0 ? 1 : 0), iLast = nw - slen - ww + ww*is_eof;
+	for ( int i = iLast; i >= iFirst; --i )
 	{
-		TPreRedrawFuncGuard preRedrawFuncGuard(PR_ViewerSearchMsg);
-		//SaveScreen SaveScr;
-		SetCursorType(FALSE,0);
-		strMsgStr = strSearchStr;
+		if ( ww )
+		{
+			if ( i > 0 && !is_word_div(buff[i-1]) )
+				continue;
+			if ( !(i == iLast && is_eof) && !is_word_div(buff[i+slen]) )
+				continue;
+		}
+		if ( buff[i] != search_str[0]
+		|| (slen > 1 && buff[i+1] != search_str[1])
+		|| (slen > 2 && 0 != wmemcmp(buff+i+2, search_str+2, slen-2))
+		) continue;
 
-		if (strMsgStr.GetLength()+18 > static_cast<DWORD>(ObjWidth))
-			TruncStrFromEnd(strMsgStr, ObjWidth-18);
+		sd->MatchPos = cpos + GetStrBytesNum(t_buff, i);
+		sd->search_len = GetStrBytesNum(t_buff+i, slen);
+		return +1;
+	}
 
-		if (!SearchHex)
-			InsertQuote(strMsgStr);
+	int ret = 0;
+	if ( up_half )
+		ret = (cpos <= StartSearchPos ? -1 : 0);
+	else
+	{
+		if ( cpos <= 0 )
+		{
+			SetFileSize();
+			cpos = FileSize;
+			ret = (StartSearchPos >= FileSize ? -1 : 0);
+		}
+	}
+	sd->CurPos = cpos;
+   return ret;
+}
 
-		if (SearchHex)
-			Case = 1 + (WholeWords = 0);
+int Viewer::read_line(wchar_t *buf, wchar_t *tbuf, INT64 cpos, int adjust, INT64 &lpos, int &lsize)
+{
+	int llen = 0;
 
-		if (!Case && !SearchHex)
-			strSearchStr.Upper();
+	INT64 save_FilePos = FilePos, save_LastPage = LastPage;
+	int save_Hex = VM.Hex, save_Wrap = VM.Wrap, save_WordWrap = VM.WordWrap;
+	VM.Hex = VM.Wrap = VM.WordWrap = 0;
 
-		SelectSize = 0;
+	FilePos = cpos;
+	if ( adjust )
+	{
+		if ( adjust > 0 )
+			AdjustFilePos();
+		else
+			Up(1);
+	}
 
-		if (Next)
+	vseek(lpos = vString.nFilePos = FilePos, SEEK_SET);
+	vString.linesize = 0;
+	ReadString(&vString, -1, false); // read unwrapped text line
+
+	vseek(FilePos, SEEK_SET);
+	llen = vread(buf, lsize = vString.linesize, tbuf);
+	if ( llen > 0 && vString.have_eol ) // remove eol-s
+	{
+		--llen;
+		if ( llen > 0 && (buf[llen-1] == L'\r') || buf[llen-1] == L'\n' )
+			--llen;
+	}
+	buf[llen >= 0 ? llen : 0] = L'\0';
+
+   VM.Hex = save_Hex; VM.Wrap = save_Wrap; VM.WordWrap = save_WordWrap;
+	FilePos = save_FilePos; LastPage = save_LastPage;
+	return llen;
+}
+
+int Viewer::search_regex_forward( search_data* sd )
+{
+	assert(sd->pRex);
+	assert(Search_buffer_size >= 2*MAX_VIEWLINEB);
+
+	wchar_t *line = Search_buffer, *t_line = sd->is_utf8 ? Search_buffer + MAX_VIEWLINEB : nullptr;
+	INT64 cpos = sd->CurPos, bpos = 0;
+
+	int first = (sd->first_Rex ? +1 : 0);
+	sd->first_Rex = false;
+	bool up_half = cpos >= StartSearchPos;
+
+	int lsize = 0, nw = read_line(line, t_line, cpos, first, bpos, lsize);
+   if ( lsize <= 0 )
+	{
+		sd->CurPos = 0;
+		return (up_half && StartSearchPos > 0 ? 0 : -1);
+	}
+
+	int off = 0;
+	for (;;)
+	{
+		if ( off > nw )
+			break;
+
+		SMatch m[1];
+		int n = static_cast<int>(ARRAYSIZE(m));
+		if ( !sd->pRex->SearchEx(line, line+off, line+nw, m, n) )  // doesn't match
+			break;
+
+		INT64 fpos = bpos + GetStrBytesNum(t_line, m[0].start);
+		if ( fpos < cpos )
 		{
-			if (Next == 2)
-			{
-				SearchFlags.Set(SEARCH_MODE2);
-				LastSelPos = ReverseSearch?FileSize:0;
-			}
-			else
-			{
-				LastSelPos = SelectPos + (ReverseSearch?-1:1);
-			}
+			off = m[0].start + 1; // skip
+			continue;
 		}
-		else
+		else if ( !up_half && fpos >= StartSearchPos )
 		{
-			LastSelPos = FilePos;
+			break; // done - not found
+		}
+		else // found
+		{
+			sd->MatchPos = fpos;
+			sd->search_len = GetStrBytesNum(t_line+off, m[0].end - m[0].start);
+			return +1;
+		}
+	}
 
-			if (!LastSelPos || LastSelPos == FileSize)
-				SearchFlags.Set(SEARCH_MODE2);
+	sd->CurPos = vtell();
+	if ( up_half )
+	{
+		if ( veof() )
+		{
+			sd->CurPos = 0;
+			return (StartSearchPos > 0 ? 0 : -1);
 		}
+		return 0;
+	}
+	else
+	{
+		return (sd->CurPos >= StartSearchPos ? -1 : 0);
+	}
+}
 
-		vseek(LastSelPos,SEEK_SET);
-		Match = 0;
+int Viewer::search_regex_backward( search_data* sd )
+{
+	assert(sd->pRex);
+	assert(Search_buffer_size >= 2*MAX_VIEWLINEB);
 
-		if (SearchLength > 0 && (!ReverseSearch || LastSelPos > 0))
+	wchar_t *line = Search_buffer, *t_line = sd->is_utf8 ? Search_buffer + MAX_VIEWLINEB : nullptr;
+	INT64 cpos = sd->CurPos, bpos = 0, prev_pos = -1;
+
+	bool up_half = cpos > StartSearchPos;
+
+	int off=0, lsize=0, nw, prev_len = -1, flen;
+	nw = read_line(line, t_line, cpos, -1, bpos, lsize);
+	for (;;)
+	{
+		if ( lsize <= 0 || off > nw )
+			break;
+
+		SMatch m[1];
+		int n = static_cast<int>(ARRAYSIZE(m));
+		if ( !sd->pRex->SearchEx(line, line+off, line+nw, m, n) )
+			break;
+
+		INT64 fpos = bpos + GetStrBytesNum(t_line, m[0].start);
+		flen = GetStrBytesNum(t_line + m[0].start, m[0].end - m[0].start);
+		if ( fpos+flen > cpos )
+			break;
+
+		if ( !(up_half && fpos+flen <= StartSearchPos) )
 		{
-			wchar_t *Buf = new wchar_t[8192 + 8192], *t_Buf = t_utf8 ? Buf + 8192 : nullptr;
-			int buf_size = 8192, seek_size = 1;
-			char *byte_buff = nullptr;
-			if (SearchHex)
-			{
-				byte_buff = (char *)Buf;
-				buf_size *= 2*(int)sizeof(wchar_t);
-				seek_size = IsUnicodeCodePage(VM.CodePage) ? 2 : 1;
-			}
+			prev_pos = fpos;
+			prev_len = flen;
+		}
 
-			__int64 CurPos=LastSelPos;
-			int BufSize = buf_size;
+		off = m[0].start + 1; // skip
+		continue;
+	}
 
-			if (ReverseSearch)
-			{
-				/* $ 01.08.2000 KM
-				   ������� �������� CurPos � ���� Whole words
-				*/
-				if (WholeWords)
-					CurPos -= BufSize/seek_size - SearchLength/seek_size + 1;
-				else
-					CurPos -= BufSize/seek_size - SearchLength/seek_size;
+	if ( prev_len >= 0 )
+	{
+		sd->MatchPos = prev_pos;
+		sd->search_len = prev_len;
+		return +1;
+	}
+	
+	if ( (sd->CurPos = bpos) <= 0 )
+	{
+		SetFileSize();
+		sd->CurPos = FileSize;
+		return (StartSearchPos >= FileSize ? -1 : 0);
+	}
+   return (up_half && bpos <= StartSearchPos ? -1 : 0);
+}
 
-				if (CurPos<0)
-					BufSize += (int)CurPos * seek_size;
-			}
 
-			int ReadSize;
-			TaskBar TB;
-			wakeful W;
-			INT64 StartPos = vtell();
-			DWORD StartTime = GetTickCount();
+/*
+ + �������� Next ����� �������� ������:
+ 0 - ���� �����
+ 1 - ��������� ����� �� ������� ������
+-1 - ��������� ����� �� ������� ������ � ��������������� �����������
+ 2 - ��������� ����� � �����/���� �����
+*/
+void Viewer::Search(int Next,int FirstChar)
+{
+	if (!ViewFile.Opened() || (Next && strLastSearchStr.IsEmpty()))
+		return;
 
-			while (!Match)
-			{
-				/* $ 01.08.2000 KM
-				   �������� ������ if (ReverseSearch && CurPos<0) �� if (CurPos<0),
-				   ��� ��� ��� ����� ����� � LastSelPos=0xFFFFFFFF, �����
-				   ���������� ��� � �� ������.
-				*/
-				if (CurPos < 0)
-					CurPos = 0;
+	string strSearchStr, strMsgStr;
+	char search_bytes[128];
+	int Case,WholeWords,ReverseSearch,SearchRegexp,SearchHex;
 
-				vseek(CurPos, SEEK_SET);
+	SearchHex = LastSearchHex;
+	Case = LastSearchCase;
+	WholeWords = LastSearchWholeWords;
+	ReverseSearch = LastSearchReverse;
+	SearchRegexp = LastSearchRegexp;
+	strSearchStr.Clear();
+	if (!strLastSearchStr.IsEmpty())
+		strSearchStr = strLastSearchStr;
 
-				if (SearchHex)
-				{
-					DWORD nr = 0;
-					Reader.Read(byte_buff, (DWORD)BufSize, &nr);
-					ReadSize = (int)nr;
-				}
-				else
-				{
-					ReadSize = vread(Buf, BufSize, t_Buf);
-				}
-				if (ReadSize <= 0)
-					break;
+	search_data sd;
+	int (Viewer::* searcher)( Viewer::search_data *p_sd ) = nullptr;
 
-				DWORD CurTime=GetTickCount();
+	if ( !Next )
+	{
+		const wchar_t *TextHistoryName=L"SearchText";
+		const wchar_t *HexMask = L"HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH " ;
+		FarDialogItem SearchDlgData[]=
+		{
+			{DI_DOUBLEBOX,3,1,72,11,0,nullptr,nullptr,0,MSG(MViewSearchTitle)},
+			{DI_TEXT,5,2,0,2,0,nullptr,nullptr,0,MSG(MViewSearchFor)},
+			{DI_EDIT,5,3,70,3,0,TextHistoryName,nullptr,DIF_FOCUS|DIF_HISTORY|DIF_USELASTHISTORY,L""},
+			{DI_FIXEDIT,5,3,70,3,0,nullptr,HexMask,DIF_MASKEDIT,L""},
+			{DI_TEXT,3,4,0,4,0,nullptr,nullptr,DIF_SEPARATOR,L""},
+			{DI_RADIOBUTTON,5,5,0,5,1,nullptr,nullptr,DIF_GROUP,MSG(MViewSearchForText)},
+			{DI_RADIOBUTTON,5,6,0,6,0,nullptr,nullptr,0,MSG(MViewSearchForHex)},
+			{DI_CHECKBOX,40,5,0,5,0,nullptr,nullptr,0,MSG(MViewSearchCase)},
+			{DI_CHECKBOX,40,6,0,6,0,nullptr,nullptr,0,MSG(MViewSearchWholeWords)},
+			{DI_CHECKBOX,40,7,0,7,0,nullptr,nullptr,0,MSG(MViewSearchReverse)},
+			{DI_CHECKBOX,40,8,0,8,0,nullptr,nullptr,DIF_DISABLE,MSG(MViewSearchRegexp)},
+			{DI_TEXT,3,9,0,9,0,nullptr,nullptr,DIF_SEPARATOR,L""},
+			{DI_BUTTON,0,10,0,10,0,nullptr,nullptr,DIF_DEFAULTBUTTON|DIF_CENTERGROUP,MSG(MViewSearchSearch)},
+			{DI_BUTTON,0,10,0,10,0,nullptr,nullptr,DIF_CENTERGROUP,MSG(MViewSearchCancel)},
+		};
+		MakeDialogItemsEx(SearchDlgData,SearchDlg);
 
-				if (CurTime-StartTime>RedrawTimeout)
-				{
-					StartTime=CurTime;
+		SearchDlg[SD_RADIO_TEXT].Selected=!LastSearchHex;
+		SearchDlg[SD_RADIO_HEX].Selected=LastSearchHex;
+		SearchDlg[SD_CHECKBOX_CASE].Selected=LastSearchCase;
+		SearchDlg[SD_CHECKBOX_WORDS].Selected=LastSearchWholeWords;
+		SearchDlg[SD_CHECKBOX_REVERSE].Selected=LastSearchReverse;
+		SearchDlg[SD_CHECKBOX_REGEXP].Selected=LastSearchRegexp;
 
-					if (CheckForEscSilent())
-					{
-						if (ConfirmAbortOp())
-						{
-							Redraw();
-							return;
-						}
-					}
+		if (SearchDlg[SD_RADIO_HEX].Selected)
+		{
+			int nb = hex2ss(strSearchStr.CPtr(), search_bytes, ARRAYSIZE(search_bytes));
+			strSearchStr.Clear();
+			ss2hex(strSearchStr, search_bytes, nb, L'\0');
+			SearchDlg[SD_EDIT_HEX].strData = strSearchStr;
+		}
+		else
+			SearchDlg[SD_EDIT_TEXT].strData = strSearchStr;
 
-					INT64 Total=ReverseSearch?StartPos:FileSize-StartPos;
-					INT64 Current=_abs64(CurPos-StartPos);
-					int Percent=Total>0?static_cast<int>(Current*100/Total):-1;
-					// � ����� ���� ���� ����������� �������, �� ���������
-					// �������� ����� �� ���� 100. ����������� ��� ������.
-					if (Percent>100)
-					{
-						SetFileSize();
-						Total=FileSize-StartPos;
-						Percent=Total>0?static_cast<int>(Current*100/Total):-1;
-						if (Percent>100)
-						{
-							Percent=100;
-						}
-					}
-					ViewerSearchMsg(strMsgStr,Percent);
-				}
+		MyDialogData my;
+		//
+		my.viewer = this;
+		my.edit_autofocus = (ViOpt.SearchEditFocus != 0);
+		my.hex_mode = (LastSearchHex != 0);
+		my.recursive = false;
+		//
+		SearchDlg[SD_EDIT_TEXT].UserData = &my;
 
-				/* $ 01.08.2000 KM
-				   ������� ����� �������� �� Case sensitive � Hex
-				   � ���� ���, ����� Buf ��������� � ������� ��������
-				*/
-				if (!Case && !SearchHex)
-					CharUpperBuff(Buf, ReadSize);
+		Dialog Dlg(SearchDlg,ARRAYSIZE(SearchDlg),ViewerSearchDlgProc);
+		Dlg.SetPosition(-1,-1,76,13);
+		Dlg.SetHelp(L"ViewerSearch");
 
-				/* $ 01.08.2000 KM
-				   ����� ����� ������ ����� ��������� ��������� ������
-				   � Buf � ������� ��������, ���� ����� �� ����������������
-				   ��� �� ���� Hex-������ � � ���� � ���� ����������� ��� ������
-				*/
-				int MaxSize=ReadSize-SearchLength+1;
-				int Increment=ReverseSearch ? -seek_size : +seek_size;
+		if (FirstChar)
+		{
+			Dlg.InitDialog();
+			Dlg.Show();
+			Dlg.ProcessKey(FirstChar);
+		}
 
-				for (int I=ReverseSearch ? MaxSize-1:0; I<MaxSize && I>=0; I+=Increment)
-				{
-					/* $ 01.08.2000 KM
-					   ��������� ������ "Whole words"
-					*/
-					int locResultLeft=FALSE;
-					int locResultRight=FALSE;
+		Dlg.Process();
 
-					if (WholeWords)
-					{
-						if (I)
-						{
-							if (IsSpace(Buf[I-1]) || IsEol(Buf[I-1]) ||
-							        (wcschr(Opt.strWordDiv,Buf[I-1])))
-								locResultLeft=TRUE;
-						}
-						else
-						{
-							locResultLeft=TRUE;
-						}
+		if (Dlg.GetExitCode()!=SD_BUTTON_OK)
+			return;
 
-						if (ReadSize!=BufSize && I+SearchLength>=ReadSize)
-							locResultRight=TRUE;
-						else if (I+SearchLength<ReadSize &&
-						         (IsSpace(Buf[I+SearchLength]) || IsEol(Buf[I+SearchLength]) ||
-						          (wcschr(Opt.strWordDiv,Buf[I+SearchLength]))))
-							locResultRight=TRUE;
-					}
-					else
-					{
-						locResultLeft=TRUE;
-						locResultRight=TRUE;
-					}
+		SearchHex=SearchDlg[SD_RADIO_HEX].Selected;
+		Case=SearchDlg[SD_CHECKBOX_CASE].Selected;
+		WholeWords=SearchDlg[SD_CHECKBOX_WORDS].Selected;
+		ReverseSearch=SearchDlg[SD_CHECKBOX_REVERSE].Selected;
+		SearchRegexp=SearchDlg[SD_CHECKBOX_REGEXP].Selected;
 
-					if (!SearchHex)
-					{
-						Match = locResultLeft && locResultRight && strSearchStr.At(0) == Buf[I] &&
-							( SearchLength==1 ||
-								( strSearchStr.At(1) == Buf[I+1] &&
-									(SearchLength==2 || !memcmp(strSearchStr.CPtr()+2,&Buf[I+2],(SearchLength-2)*sizeof(wchar_t)))
-								)
-							)
-						;
-					}
-					else
-					{
-						if ((Match = search_bytes[0] == byte_buff[I+0]) && SearchLength > 1)
-							if ((Match = search_bytes[1] == byte_buff[I+1]) && SearchLength > 2)
-								if ((Match = search_bytes[2] == byte_buff[I+2]) && SearchLength > 3)
-									if ((Match = search_bytes[3] == byte_buff[I+3]) && SearchLength > 4)
-										Match = !memcmp(search_bytes+4, byte_buff+I+4, SearchLength-4);
-					}
+		if (SearchHex)
+		{
+			strSearchStr = SearchDlg[SD_EDIT_HEX].strData;
+			int len = hex2ss(strSearchStr.CPtr(), search_bytes, ARRAYSIZE(search_bytes));
+			strSearchStr.Clear();
+			ss2hex(strSearchStr, search_bytes, len, L' ');
+		}
+		else
+		{
+			strSearchStr = SearchDlg[SD_EDIT_TEXT].strData;
+			size_t pos = 0;
+			while (strSearchStr.Pos(pos, 0xffff))
+				strSearchStr.Replace(pos, 1, L'\0');
+		}
+	}
 
-					if (Match)
+	LastSearchCase = Case;
+	LastSearchWholeWords = WholeWords;
+	LastSearchReverse = ReverseSearch;
+	LastSearchRegexp = SearchRegexp;
+
+	if (Next == -1)
+		ReverseSearch = !ReverseSearch;
+
+	strMsgStr = strLastSearchStr = strSearchStr;
+	if (strMsgStr.GetLength()+18 > static_cast<DWORD>(ObjWidth))
+		TruncStrFromEnd(strMsgStr, ObjWidth-18);
+
+	sd.search_len = (int)strSearchStr.GetLength();
+	if (0 != (LastSearchHex = SearchHex))
+	{
+		sd.search_len = hex2ss(strSearchStr.CPtr(), search_bytes, ARRAYSIZE(search_bytes));
+		sd.search_bytes = search_bytes;
+		sd.ch_size = 1;
+		Case = 1 + (WholeWords = SearchRegexp = 0);
+		searcher = (ReverseSearch ? &Viewer::search_hex_backward : &Viewer::search_hex_forward);
+	}
+	else
+	{
+		sd.is_utf8 = VM.CodePage == CP_UTF8;
+		sd.ch_size = getCharSize(VM.CodePage);
+		sd.search_text = strSearchStr.CPtr();
+
+		if ( SearchRegexp )
+		{
+			WholeWords = 0;
+			searcher = (ReverseSearch ? &Viewer::search_regex_backward : &Viewer::search_regex_forward);
+			InsertRegexpQuote(strMsgStr);
+			sd.pRex = new RegExp;
+			string strSlash = strSearchStr;
+			InsertRegexpQuote(strSlash);
+			if ( !sd.pRex->Compile(strSlash, OP_PERLSTYLE | OP_OPTIMIZE | (Case ? 0 : OP_IGNORECASE)) )
+				return; // wrong regular expression...
+		}
+		else
+		{
+			searcher = (ReverseSearch ? &Viewer::search_text_backward : &Viewer::search_text_forward);
+			InsertQuote(strMsgStr);
+		}
+	}
+		
+	if (!Case && !SearchRegexp)
+	{
+		strSearchStr.Upper();
+		sd.search_text = strSearchStr.CPtr();
+	}
+
+	int found = 0;
+
+	int search_direction = ReverseSearch ? -1 : +1;
+	switch (Next)
+	{
+		case 2:
+			StartSearchPos = LastSelectPos = (ReverseSearch ? FileSize : 0);
+		break;
+		case +1: case -1:
+			if ( SelectPos >= 0 && LastSelectSize >= 0 )
+			{
+				if (sd.ch_size >= 1)
+					LastSelectPos = SelectPos + (ReverseSearch ? LastSelectSize-sd.ch_size : sd.ch_size);
+				else
+				{
+					INT64 prev_pos = SelectPos;
+					vseek(SelectPos, SEEK_SET);
+					for (;;)
 					{
-						MatchPos = CurPos + I / seek_size;
-						if (t_utf8)
+						wchar_t ch;
+						bool ok_getc = vgetc(&ch);
+						LastSelectPos = vtell();
+						if (!ReverseSearch || !ok_getc)
+							break;
+						if ( LastSelectPos >= SelectPos + LastSelectSize )
 						{
-							MatchPos = CurPos + GetStrBytesNum(t_Buf, I);
-							SearchLength = GetStrBytesNum(t_Buf+I, SearchLength);
+							LastSelectPos = prev_pos;
+							break;
 						}
-						break;
+						prev_pos = LastSelectPos;
 					}
 				}
+				if (search_direction != LastSearchDirection)
+					StartSearchPos = LastSelectPos;
+				else if ( LastSelectPos == StartSearchPos ) // ������ �
+					found = -1;									  // ������������
 
-				if ((ReverseSearch && CurPos <= 0) || (!ReverseSearch && veof()))
-					break;
+				break;
+			} // else pass to case 0 (below)
+		case 0:
+			LastSelectSize = SelectSize = -1;
+			StartSearchPos = LastSelectPos = (ReverseSearch ? EndOfScreen(0) : BegOfScreen());
+		break;
+	}
+	LastSearchDirection = search_direction;
 
-				int asz = buf_size;
-				if (t_utf8)
-					asz = GetStrBytesNum(t_Buf, ReadSize);
+	if (!sd.search_len || (__int64)sd.search_len > FileSize)
+		return;
 
-				if (ReverseSearch)
+	sd.CurPos = LastSelectPos;
+	if ( !found )
+	{
+		TPreRedrawFuncGuard preRedrawFuncGuard(PR_ViewerSearchMsg);
+		SetCursorType(FALSE,0);
+
+		DWORD start_time = GetTickCount();
+		for (;;)
+		{
+			found = (this->*searcher)(&sd);
+			if ( found != 0 )
+				break;
+
+			DWORD cur_time = GetTickCount();
+			if ( cur_time - start_time > RedrawTimeout )
+			{
+				start_time = cur_time;
+
+				if (CheckForEscSilent())
 				{
-					/* $ 01.08.2000 KM
-					   ������� �������� CurPos � ���� Whole words
-					*/
-					if (WholeWords)
-						CurPos -= asz/seek_size - SearchLength/seek_size + 1;
-					else
-						CurPos -= asz/seek_size - SearchLength/seek_size;
+					if (ConfirmAbortOp())
+					{
+						Redraw();
+						return;
+					}
 				}
-				else
+
+				int percent = -1;
+				INT64 done, total = FileSize;
+				if ( total > 0 )
 				{
-					if (WholeWords)
-						CurPos += asz/seek_size - SearchLength/seek_size + 1;
+					if ( !ReverseSearch )
+					{
+						if ( sd.CurPos >= StartSearchPos )
+							done = sd.CurPos - StartSearchPos;
+						else
+							done = (FileSize - StartSearchPos) + sd.CurPos;
+					}
 					else
-						CurPos += asz/seek_size - SearchLength/seek_size;
+					{
+						if ( sd.CurPos <= StartSearchPos )
+							done = StartSearchPos - sd.CurPos;
+						else
+							done = StartSearchPos + (FileSize - sd.CurPos);
+					}
+					percent = static_cast<int>(done*100/total);
 				}
+				ViewerSearchMsg(strMsgStr, percent, SearchHex);
 			}
-
-			delete[] Buf;
 		}
 	}
 
-	if (Match)
+	if ( sd.MatchPos >= 0 )
 	{
-		/* $ 24.01.2003 KM
-		   ! �� �������� ������ �������� �� ���� ������ ��
-		     ���� ������������ ����.
-		*/
-		SelectText(MatchPos,SearchLength,ReverseSearch?0x2:0);
-		// ������� ��������� �� ��������� ����� ������ �� ����.
+		SelectText(sd.MatchPos, sd.search_len, ReverseSearch?0x2:0);
+		LastSelectSize = SelectSize;
+
+		// ������� ��������� �� ��������� �������� ������ �� ����.
 		int FromTop=(ScrY-(Opt.ViOpt.ShowKeyBar?2:1))/4;
 
 		if (FromTop<0 || FromTop>ScrY)
 			FromTop=0;
 
-		for (int i=0; i<FromTop; i++)
-			Up();
+		Up(FromTop);
 
 		AdjustSelPosition = TRUE;
 		Show();
@@ -2598,24 +3264,13 @@
 	}
 	else
 	{
-		//Show();
-		/* $ 27.01.2003 VVM
-		   + ����� ������� ������ ������� � ������� ������ � �����/���� */
-		if (SearchFlags.Check(SEARCH_MODE2))
-		{
-			Message(MSG_WARNING,1,MSG(MViewSearchTitle),
-			        (SearchHex?MSG(MViewSearchCannotFindHex):MSG(MViewSearchCannotFind)),strMsgStr,MSG(MOk));
-		}
-		else
-		{
-			if (!Message(MSG_WARNING,2,MSG(MViewSearchTitle),
-			            (SearchHex?MSG(MViewSearchCannotFindHex):MSG(MViewSearchCannotFind)),strMsgStr,
-			            (ReverseSearch?MSG(MViewSearchFromEnd):MSG(MViewSearchFromBegin)),
-			            MSG(MHYes),MSG(MHNo)))
-			{
-				Search(2,0);
-			}
-		}
+		Message(
+			MSG_WARNING, 1,
+			MSG(MViewSearchTitle),
+			(SearchHex ? MSG(MViewSearchCannotFindHex) : MSG(MViewSearchCannotFind)),
+			strMsgStr,
+			MSG(MOk)
+		);
 	}
 }
 
@@ -2625,37 +3280,31 @@
 	return(VM.Wrap);
 }
 
-
 void Viewer::SetWrapMode(int Wrap)
 {
 	Viewer::VM.Wrap=Wrap;
 }
 
-
 void Viewer::EnableHideCursor(int HideCursor)
 {
 	Viewer::HideCursor=HideCursor;
 }
 
-
 int Viewer::GetWrapType()
 {
 	return(VM.WordWrap);
 }
 
-
 void Viewer::SetWrapType(int TypeWrap)
 {
 	Viewer::VM.WordWrap=TypeWrap;
 }
 
-
 void Viewer::GetFileName(string &strName)
 {
 	strName = strFullFileName;
 }
 
-
 void Viewer::ShowConsoleTitle()
 {
 	string strTitle;
@@ -2663,7 +3312,6 @@
 	ConsoleTitle::SetFarTitle(strTitle);
 }
 
-
 void Viewer::SetTempViewName(const wchar_t *Name, BOOL DeleteFolder)
 {
 	if (Name && *Name)
@@ -2677,7 +3325,6 @@
 	Viewer::DeleteFolder=DeleteFolder;
 }
 
-
 void Viewer::SetTitle(const wchar_t *Title)
 {
 	if (!Title)
@@ -2686,19 +3333,16 @@
 		strTitle = Title;
 }
 
-
 void Viewer::SetFilePos(__int64 Pos)
 {
 	FilePos=Pos;
 };
 
-
 void Viewer::SetPluginData(const wchar_t *PluginData)
 {
 	Viewer::strPluginData = NullToEmpty(PluginData);
 }
 
-
 void Viewer::SetNamesList(NamesList *List)
 {
 	if (List)
@@ -2825,10 +3469,15 @@
 
 	if (IsUnicodeCodePage(VM.CodePage))
 	{
-		Reader.Read(Buf, Count*2, &ReadSize);
+		int rev = (CP_REVERSEBOM == VM.CodePage ? 1 : 0);
+
+		Reader.Read(Buf, Count, &ReadSize);
 		if (0 != (ReadSize & 1))
-			((char *)Buf)[ReadSize++] = '\0';
-
+		{
+			((char *)Buf)[ReadSize-1+rev] = (char)(REPLACE_CHAR & 0xff);
+			((char *)Buf)[ReadSize-0-rev] = (char)(REPLACE_CHAR >> 8);
+			++ReadSize;
+		}
 		if (CP_REVERSEBOM == VM.CodePage)
 		{
 			for (DWORD i=0; i<ReadSize; i+=2)
@@ -2838,7 +3487,6 @@
 				((char *)Buf)[i+1] = t;
 			}
 		}
-
 		ReadSize /= 2;
 	}
 	else
@@ -2871,15 +3519,11 @@
 
 bool Viewer::vseek(__int64 Offset, int Whence)
 {
-	int seek_size = IsUnicodeCodePage(VM.CodePage) ? 2 : 1;
-	Offset *= seek_size;
-
 	if (FILE_CURRENT == Whence)
 	{
 		if (vgetc_ready)
 		{
 			int tail = vgetc_cb - vgetc_ib;
-			tail += (seek_size - 1) * (tail & 1);
 			Offset += tail;
 			vgetc_ready = false;
 		}
@@ -2898,9 +3542,6 @@
 	if (vgetc_ready)
 		Ptr -= (vgetc_cb - vgetc_ib);
 
-	if (IsUnicodeCodePage(VM.CodePage))
-		Ptr=(Ptr+(Ptr&1))/2;
-
 	return Ptr;
 }
 
@@ -2927,9 +3568,6 @@
 		DWORD nr = 0;
 		Reader.Read(vgetc_buffer + vgetc_cb, (DWORD)(ARRAYSIZE(vgetc_buffer)-vgetc_cb), &nr);
 		vgetc_cb += (int)nr;
-
-		if (0 != (vgetc_cb & 1) && IsUnicodeCodePage(VM.CodePage))
-			vgetc_buffer[vgetc_cb++] = 0;
 	}
 
 	vgetc_ready = true;
@@ -2951,11 +3589,17 @@
 	switch (VM.CodePage)
 	{
 		case CP_REVERSEBOM:
-			*pCh = (wchar_t)((vgetc_buffer[vgetc_ib] << 8) | vgetc_buffer[vgetc_ib+1]);
+			if (vgetc_ib == vgetc_cb-1)
+				*pCh = REPLACE_CHAR;
+			else
+				*pCh = (wchar_t)((vgetc_buffer[vgetc_ib] << 8) | vgetc_buffer[vgetc_ib+1]);
 			vgetc_ib += 2;
 		break;
 		case CP_UNICODE:
-			*pCh = (wchar_t)((vgetc_buffer[vgetc_ib+1] << 8) | vgetc_buffer[vgetc_ib]);
+			if (vgetc_ib == vgetc_cb-1)
+				*pCh = REPLACE_CHAR;
+			else
+				*pCh = (wchar_t)((vgetc_buffer[vgetc_ib+1] << 8) | vgetc_buffer[vgetc_ib]);
 			vgetc_ib += 2;
 		break;
 		case CP_UTF8:
@@ -2978,6 +3622,54 @@
 	return true;
 }
 
+wchar_t Viewer::vgetc_prev()
+{
+	INT64 pos = vtell();
+	if ( pos <= 0 )
+		return L'\0';
+
+	int ch_size = getCharSize(VM.CodePage);
+	if ( pos < ch_size )
+		return REPLACE_CHAR;
+
+	int nb = (ch_size >= 1 ? ch_size : (pos > 4 ? 4 : static_cast<int>(pos)));
+	DWORD nr = 0;
+
+	char ss[4];
+	if ( vseek(-nb, SEEK_CUR) )
+		 Reader.Read(ss, static_cast<DWORD>(nb), &nr);
+
+	vseek(pos, SEEK_SET);
+	
+	wchar_t ch = REPLACE_CHAR;
+	if ( static_cast<int>(nr) == nb )
+	{
+		switch ( VM.CodePage )
+		{
+			case CP_REVERSEBOM:
+				ch = MAKEWORD(ss[1], ss[0]);
+			break;
+			case CP_UNICODE:
+				ch = MAKEWORD(ss[0], ss[1]);
+			break;
+			case CP_UTF8:
+			{
+				int tail = 0;
+				wchar_t w[4];
+				int nw = utf8_to_WideChar(ss, nb, w,nullptr, 4,tail);
+				if ( !tail && nw > 0 )
+					ch = w[nw-1];
+				break;
+			}
+			default:
+				MultiByteToWideChar(VM.CodePage, 0, (LPCSTR)ss,1, &ch,1);
+			break;
+		}
+	}
+	return ch;
+}
+
+
 #define RB_PRC 3
 #define RB_HEX 4
 #define RB_DEC 5
@@ -3060,9 +3752,6 @@
 				//  Percent=0;
 				Offset=FileSize/100*Percent;
 
-				if (IsUnicodeCodePage(VM.CodePage))
-					Offset*=2;
-
 				while (ToPercent64(Offset,FileSize)<Percent)
 					Offset++;
 			}
@@ -3094,31 +3783,16 @@
 				//  Percent=0;
 				Offset=FileSize/100*Percent;
 
-				if (IsUnicodeCodePage(VM.CodePage))
-					Offset*=2;
-
 				while (ToPercent64(Offset,FileSize)<Percent)
 					Offset++;
 			}
 		}
 
-		if (Relative)
-		{
-			if (Relative==-1 && Offset>FilePos)   // ���� ���, if (FilePos<0) �� ������� - FilePos � ��� unsigned long
-				FilePos=0;
-			else
-				FilePos=IsUnicodeCodePage(VM.CodePage)? FilePos+Offset*Relative/2 : FilePos+Offset*Relative;
-		}
-		else
-			FilePos=IsUnicodeCodePage(VM.CodePage) ? Offset/2:Offset;
-
-		if (FilePos>FileSize || FilePos<0)     // � ���� ��� �����?
-			FilePos=FileSize;     // ��� ��� ����� ����� ����
+		FilePos = (Relative ? FilePos + Offset*Relative : Offset);
+		FilePos = (FilePos < 0 ? 0 : (FilePos > FileSize ? FileSize : FilePos));
 	}
-	// �������
 	AdjustFilePos();
 
-//  LastSelPos=FilePos;
 	if (!(Flags&VSP_NOREDRAW))
 		Show();
 }
@@ -3129,6 +3803,9 @@
 
 	if (!VM.Hex)
 	{
+		int ch_size = getCharSize(VM.CodePage);
+		FilePos -= FilePos % ch_size;
+
 		vseek(FilePos, SEEK_SET);
 		if (VM.CodePage != CP_UTF8)
 		{
@@ -3140,7 +3817,7 @@
 			if (vgetc_ib < vgetc_cb)
 			{
 				if (0x80 == (vgetc_buffer[vgetc_ib] & 0xC0))
-		{
+				{
 					if (++vgetc_ib < vgetc_cb)
 					{
 						if (0x80 == (vgetc_buffer[vgetc_ib] & 0xC0))
@@ -3152,18 +3829,18 @@
 							}
 						}
 					}
-		}
+				}
 				else
-		{
+				{
 					vgetc(&ch);
 				}
 			}
 		}
 
-		   FilePos = vtell();
-			Up();
-		}
+		FilePos = vtell();
+		Up(1);
 	}
+}
 
 void Viewer::SetFileSize()
 {
@@ -3173,13 +3850,6 @@
 	UINT64 uFileSize=0; // BUGBUG, sign
 	ViewFile.GetSize(uFileSize);
 	FileSize=uFileSize;
-
-	/* $ 20.02.2003 IS
-	   ����� ���������� FilePos � FileSize, FilePos �� ������� ������
-	   �������� � ��� ����, ������ FileSize ���� ���� ������
-	*/
-	if (IsUnicodeCodePage(VM.CodePage))
-		FileSize=(FileSize+(FileSize&1))/2;
 }
 
 
@@ -3190,85 +3860,50 @@
 	Flags=SelectFlags;
 }
 
-/* $ 19.01.2001 SVS
-   �������� - � ������� ������������� ������.
-   Flags=0x01 - �������� (����� Show())
-         0x02 - "������� �����" ?
-*/
-void Viewer::SelectText(const __int64 &MatchPos,const __int64 &SearchLength, const DWORD Flags)
+// Flags=0x01 - �������� [����� Show()]
+//       0x02 - "������� �����" ?
+//
+void Viewer::SelectText(const __int64 &match_pos,const __int64 &search_len, const DWORD flags)
 {
 	if (!ViewFile.Opened())
 		return;
 
-	int buff_size = 1024;
-	wchar_t *Buf = Up_buffer, *tBuf = (!VM.Hex && VM.CodePage == CP_UTF8) ? Buf + buff_size : nullptr;
-	__int64 StartLinePos = -1, SearchLinePos = MatchPos - buff_size;
-	if (SearchLinePos < 0)
-	{
-		buff_size += (int)SearchLinePos;
-		SearchLinePos = 0;
-	}
+	SelectPos = match_pos;
+	SelectSize = search_len;
+	SelectFlags = flags;
+	if ( SelectSize < 0 )
+		return;
 
-	vseek(SearchLinePos, SEEK_SET);
-	int ReadSize = vread(Buf, buff_size, tBuf);
-
-	for (int I=ReadSize-1; I >= 0; I--)
-	if (Buf[I]==L'\n' || Buf[I]==L'\r')
+	if ( VM.Hex )
 	{
-		if (tBuf)
-			StartLinePos = MatchPos - GetStrBytesNum(tBuf+I, ReadSize-I);
-		else
-			StartLinePos = SearchLinePos+I;
-		break;
+		 FilePos = (FilePos & 0xf) | (SelectPos & ~0xf);
+		 FilePos = (FilePos < SelectPos ? FilePos : (FilePos > 16 ? FilePos-16 : 0));
 	}
-
-	vseek(MatchPos+1, SEEK_SET);
-	SelectPos = FilePos=MatchPos;
-	SelectSize = SearchLength;
-	SelectFlags = Flags;
-
-//  LastSelPos=SelectPos+((Flags&0x2) ? -1:1);
-	if (VM.Hex)
-	{
-		FilePos&=~(IsUnicodeCodePage(VM.CodePage) ? 0x7:0xf);
-	}
 	else
 	{
-		if (SelectPos != StartLinePos)
+		FilePos = SelectPos;
+		Up(1);
+		LeftPos = 0;
+
+		if ( !VM.Wrap )
 		{
-			Up();
-			Show();  //update OutStr
-		}
+			vseek(vString.nFilePos = FilePos, SEEK_SET);
+			vString.lpData[0] = L'\0';
+			ReadString(&vString, (int)(SelectPos-FilePos));
 
-		/* $ 13.03.2001 IS
-		   ���� ��������� ����������� � ����� ������ ������ ���������� ����� � ����
-		   ����� � ����� fffe ��� feff, �� �� ����� ���������� �������, ���
-		   ����� ����� ������ �� ������ (��-�� ����, ��� ������ ������ ��
-		   ����������)
-		*/
-		SelectPosOffSet=(IsUnicodeOrUtfCodePage(VM.CodePage) && Signature
-		                 && (MatchPos+SelectSize<=ObjWidth && MatchPos<(__int64)StrLength(Strings[0]->lpData)))?1:0;
-		SelectPos-=SelectPosOffSet;
-		__int64 Length=SelectPos-StartLinePos-1;
+			wchar_t first_found_char = L'\0';
+			vgetc(&first_found_char);
 
-		if (VM.Wrap)
-			Length%=Width+1; //??
-
-		if (Length<=Width)
-			LeftPos=0;
-
-		if (Length-LeftPos>Width || Length<LeftPos)
-		{
-			LeftPos=Length;
-
-			if (LeftPos>(MAX_VIEWLINE-1) || LeftPos<0)
-				LeftPos=0;
-			else if (LeftPos>10)
-				LeftPos-=10;
+			if ( L'\r' != first_found_char && L'\n' != first_found_char )
+			{
+				int found_offset = (int)wcslen(vString.lpData);
+				if ( found_offset > Width-10 )
+					LeftPos = (Width <= 10 ? found_offset : found_offset + 10 - Width);
+			}
 		}
 	}
 
-	if (Flags&1)
+	if (0 != (flags & 1))
 	{
 		AdjustSelPosition = TRUE;
 		Show();
@@ -3276,6 +3911,7 @@
 	}
 }
 
+
 int Viewer::ViewerControl(int Command,void *Param)
 {
 	switch (Command)
@@ -3365,7 +4001,7 @@
 			}
 			else
 			{
-				SelectSize = 0;
+				SelectSize = -1;
 				Show();
 			}
 
@@ -3476,7 +4112,6 @@
 		Show();
 	}
 
-// LastSelPos=FilePos;
 	return oldHex;
 }
 
@@ -3488,7 +4123,7 @@
 	if (VM.Wrap)
 		LeftPos = 0;
 
-	if (!VM.Wrap)
+	if (!VM.Hex)
 		AdjustFilePos();
 
 	if (isRedraw)
@@ -3498,7 +4133,6 @@
 	}
 
 	Opt.ViOpt.ViewerIsWrap=VM.Wrap;
-//  LastSelPos=FilePos;
 	return oldWrap;
 }
 
@@ -3513,6 +4147,9 @@
 		LeftPos = 0;
 	}
 
+	if (!VM.Hex)
+		AdjustFilePos();
+
 	if (isRedraw)
 	{
 		ChangeViewKeyBar();
@@ -3520,6 +4157,5 @@
 	}
 
 	Opt.ViOpt.ViewerWrap=VM.WordWrap;
-//LastSelPos=FilePos;
 	return oldTypeWrap;
 }
Index: config.cpp
===================================================================
--- config.cpp	(revision 6286)
+++ config.cpp	(working copy)
@@ -595,6 +595,9 @@
 	{1, GeneralConfig::TYPE_INTEGER, NKeyViewer,L"PersistentBlocks",&Opt.ViOpt.PersistentBlocks,0, 0},
 	{1, GeneralConfig::TYPE_INTEGER, NKeyViewer,L"AnsiCodePageAsDefault",&Opt.ViOpt.AnsiCodePageAsDefault,1, 0},
 
+	{1, GeneralConfig::TYPE_INTEGER, NKeyViewer,L"MaxLineSize",&Opt.ViOpt.MaxLineSize,2048, 0},
+	{1, GeneralConfig::TYPE_INTEGER, NKeyViewer,L"SearchEditFocus",&Opt.ViOpt.SearchEditFocus,0, 0},
+
 	{1, GeneralConfig::TYPE_INTEGER, NKeyDialog, L"EditHistory",&Opt.Dialogs.EditHistory,1, 0},
 	{1, GeneralConfig::TYPE_INTEGER, NKeyDialog, L"EditBlock",&Opt.Dialogs.EditBlock,0, 0},
 	{1, GeneralConfig::TYPE_INTEGER, NKeyDialog, L"AutoComplete",&Opt.Dialogs.AutoComplete,1, 0},
@@ -920,8 +923,15 @@
 		}
 	}
 
-	Opt.ViOpt.ViewerIsWrap&=1;
-	Opt.ViOpt.ViewerWrap&=1;
+	Opt.ViOpt.ViewerIsWrap &= 1;
+	Opt.ViOpt.ViewerWrap &= 1;
+	if (!Opt.ViOpt.MaxLineSize)
+		Opt.ViOpt.MaxLineSize = 2048;
+	else if (Opt.ViOpt.MaxLineSize < 80)
+		Opt.ViOpt.MaxLineSize = 80;
+	else if (Opt.ViOpt.MaxLineSize > 16*1024)
+		Opt.ViOpt.MaxLineSize = 16*1024;
+	Opt.ViOpt.SearchEditFocus &= 1;
 
 	// ������� �������� �������� ������������ ;-)
 	if (Opt.strWordDiv.IsEmpty())
Index: config.hpp
===================================================================
--- config.hpp	(revision 6286)
+++ config.hpp	(working copy)
@@ -314,6 +314,8 @@
 	int AnsiCodePageAsDefault;
 	int ShowTitleBar;
 	int SearchRegexp;
+	int MaxLineSize; // 80..16384, default=2048
+	int SearchEditFocus; // auto-focus on edit text/hex window
 };
 
 // "�����"
NewViewer.diff (93,452 bytes)   

2useven10

2011-05-26 11:10

developer   bugnote:0007063

новая версия выложена

vskirdin

2011-05-26 11:59

administrator   bugnote:0007065

тестим - http://skirda-file.googlecode.com/files/Viewer-new.1.7z

2useven10

2011-05-26 14:04

developer  

NewViewer2.diff (93,375 bytes)   
Index: viewer.hpp
===================================================================
--- viewer.hpp	(revision 6286)
+++ viewer.hpp	(working copy)
@@ -40,22 +40,13 @@
 #include "config.hpp"
 #include "cache.hpp"
 
-/* $ 10.07.2000 tran
-   ! modified MAXSCRY from 120 to 300
-   on win200, with Console height FAR work, but trap on viewer... */
-#define  MAXSCRY     300
+#define MAXSCRY       512                    // 300   // 120
 
-/* $ 12.07.2000 SVS
-  - ��-�� �������� ���� ������ �� 0x800 ������ FAR
-    �� Alt-F7. �������� MAX_VIEWLINE �� 1024 (0x400)
-*/
-#define MAX_VIEWLINE  0x800 // 0x400
-#define MAX_VIEWLINEB 0x80f // 0x40f
+#define MAX_VIEWLINE  (ViOpt.MaxLineSize+ 0) // 0x800 // 0x400
+#define MAX_VIEWLINEB (ViOpt.MaxLineSize+15) // 0x80f // 0x40f
 
 #define VIEWER_UNDO_COUNT   64
 
-enum {VIEW_UNWRAP=0,VIEW_WRAP=1, VIEW_WORDWRAP=2};
-
 class FileViewer;
 class KeyBar;
 
@@ -65,7 +56,7 @@
 	__int64 nFilePos;
 	__int64 nSelStart;
 	__int64 nSelEnd;
-	int  nbytes;
+	int  linesize;
 	bool bSelection;
 	bool have_eol;
 };
@@ -76,12 +67,6 @@
 	__int64 UndoLeft;
 };
 
-enum SEARCH_FLAGS
-{
-	SEARCH_MODE2   = 0x00000001,
-	REVERSE_SEARCH = 0x00000002
-};
-
 enum SHOW_MODES
 {
 	SHOW_RELOAD,
@@ -95,9 +80,6 @@
 		friend class FileViewer;
 
 	private:
-
-		BitFlags SearchFlags;
-
 		struct ViewerOptions ViOpt;
 
 		bool Signature;
@@ -105,7 +87,7 @@
 		NamesList ViewNamesList;
 		KeyBar *ViewKeyBar;
 
-		ViewerString *Strings[MAXSCRY+1];
+		ViewerString **Strings;
 
 		string strFileName;
 		string strFullFileName;
@@ -121,6 +103,8 @@
 
 		string strLastSearchStr;
 		int LastSearchCase,LastSearchWholeWords,LastSearchReverse,LastSearchHex,LastSearchRegexp;
+		int LastSearchDirection;
+		__int64 StartSearchPos;
 
 		struct ViewerMode VM;
 
@@ -128,13 +112,12 @@
 		__int64 SecondPos;
 		__int64 LastScrPos;
 		__int64 FileSize;
-		__int64 LastSelPos;
+		__int64 LastSelectPos, LastSelectSize;
 
 		__int64 LeftPos;
 		__int64 LastPage;
 		__int64 SelectPos,SelectSize;
 		DWORD SelectFlags;
-		__int64 SelectPosOffSet; // ���������� �� �������� ������ ������� � ������� �����
 		int ShowStatusLine,HideCursor;
 
 		string strTitle;
@@ -165,9 +148,20 @@
 		char *vread_buffer;
 		int vread_buffer_size;
 
-		wchar_t *Up_buffer;
-		int Up_buffer_size;
+		__int64  lcache_first;
+		__int64  lcache_last;
+		__int64 *lcache_lines;
+		int      lcache_size;
+		int      lcache_count;
+		int      lcache_base;
+	   bool     lcache_ready;
+		int      lcache_wrap;
+		int      lcache_wwrap;
+		int      lcache_width;
 
+		wchar_t *Search_buffer;
+		int Search_buffer_size;
+
 		ViewerString vString;
 
 		unsigned char vgetc_buffer[32];
@@ -181,8 +175,9 @@
 
 		void ShowPage(int nMode);
 
-		void Up();
-		int WrappedLength(wchar_t *str, int line_size);
+		void Up(int n);
+		void CacheLine(__int64 start, int length, bool have_eol);
+		int CacheFindUp(__int64 start);
 
 		void ShowHex();
 		void ShowStatus();
@@ -195,17 +190,31 @@
 		void AdjustWidth();
 		void AdjustFilePos();
 
-		void ReadString(ViewerString *pString, int MaxSize);
-		int CalcStrSize(const wchar_t *Str,int Length);
+		void ReadString(ViewerString *pString, int MaxSize, bool update_cache=true);
+		__int64 EndOfScreen( int line );
+		__int64 BegOfScreen();
+
 		void ChangeViewKeyBar();
+
 		void Search(int Next,int FirstChar);
-		void ConvertToHex(char *SearchStr,int &SearchLength);
+		//
+		struct search_data;
+		//
+		int search_hex_forward( search_data* sd );
+		int search_hex_backward( search_data* sd );
+		int search_text_forward( search_data* sd );
+		int search_text_backward( search_data* sd );
+		int search_regex_forward( search_data* sd );
+		int search_regex_backward( search_data* sd );
 
+		int read_line(wchar_t *buf, wchar_t *tbuf, INT64 cpos, int adjust, INT64 &lpos, int &lsize);
+
 		int vread(wchar_t *Buf, int Count, wchar_t *Buf2 = nullptr);
 		bool vseek(__int64 Offset, int Whence);
 		__int64 vtell();
 		bool vgetc(wchar_t *ch);
 		bool veof();
+		wchar_t vgetc_prev();
 
 		void SetFileSize();
 		int GetStrBytesNum(const wchar_t *Str, int Length);
@@ -283,5 +292,5 @@
 		int ProcessWrapMode(int newMode, bool isRedraw=TRUE);
 		int ProcessTypeWrapMode(int newMode, bool isRedraw=TRUE);
 
-		void SearchTextTransform(UnicodeString &to, const wchar_t *from, bool hex2text);
+		void SearchTextTransform(UnicodeString &to, const wchar_t *from, bool hex2text, int &pos);
 };
Index: viewer.cpp
===================================================================
--- viewer.cpp	(revision 6286)
+++ viewer.cpp	(working copy)
@@ -67,16 +67,17 @@
 #include "wakeful.hpp"
 
 static void PR_ViewerSearchMsg();
-static void ViewerSearchMsg(const wchar_t *Name,int Percent);
+static void ViewerSearchMsg(const wchar_t *name, int percent, int search_hex);
 
-static int InitHex=FALSE,SearchHex=FALSE;
+//static int InitHex=FALSE,SearchHex=FALSE;
 
 static int ViewerID=0;
 
 static int utf8_to_WideChar(const char *s, int nc, wchar_t *w1,wchar_t *w2, int wlen, int &tail);
 
-#define REPLACE_CHAR 0xFFFD  // Replacement
+#define REPLACE_CHAR  0xFFFD // Replacement
 #define CONTINUE_CHAR 0x203A // Single Right-Pointing Angle Quotation Mark
+#define BOM_CHAR      0xFEFF // Zero Length Space
 
 Viewer::Viewer(bool bQuickView, UINT aCodePage):
 	ViOpt(Opt.ViOpt),
@@ -85,10 +86,11 @@
 {
 	_OT(SysLog(L"[%p] Viewer::Viewer()", this));
 
-	for (int i=0; i<=MAXSCRY; i++)
+	Strings = new ViewerString*[MAXSCRY+1];
+	for (int i=0; i <= MAXSCRY; i++)
 	{
 		Strings[i] = new ViewerString();
-		Strings[i]->lpData = new wchar_t[MAX_VIEWLINEB];
+		memset(Strings[i], 0, sizeof(ViewerString));
 	}
 
 	strLastSearchStr = strGlobalSearchString;
@@ -97,19 +99,21 @@
 	LastSearchWholeWords=GlobalSearchWholeWords;
 	LastSearchReverse=GlobalSearchReverse;
 	LastSearchHex=GlobalSearchHex;
+	LastSearchDirection = GlobalSearchReverse ? -1 : +1;
+	StartSearchPos = 0;
 	VM.CodePage=DefCodePage=aCodePage;
 	// �������� ��� �����
 	VM.Wrap=Opt.ViOpt.ViewerIsWrap;
 	VM.WordWrap=Opt.ViOpt.ViewerWrap;
-	VM.Hex=InitHex;
+	VM.Hex = FALSE; //InitHex;
 	ViewKeyBar=nullptr;
 	FilePos=0;
 	LeftPos=0;
 	SecondPos=0;
 	FileSize=0;
 	LastPage=0;
-	SelectPos=SelectSize=0;
-	LastSelPos=0;
+	SelectPos = 0; SelectSize = -1;
+	LastSelectPos = 0; LastSelectSize = -1;
 	SetStatusMode(TRUE);
 	HideCursor=TRUE;
 	DeleteFolder=TRUE;
@@ -123,7 +127,6 @@
 	CtrlObject->Plugins.CurViewer=this;
 	OpenFailed=false;
 	HostFileViewer=nullptr;
-	SelectPosOffSet=0;
 	bVE_READ_Sent = false;
 	Signature = false;
 
@@ -132,8 +135,17 @@
 	vgetc_composite = L'\0';
 
 	vread_buffer = new char[vread_buffer_size = 8192];
-	Up_buffer = new wchar_t[Up_buffer_size = 2 * (3*MAX_VIEWLINE + 2)];
 
+	lcache_first = lcache_last = -1;
+	lcache_lines = new INT64[lcache_size = 8*1000];
+	lcache_count = 0;
+	lcache_base = 0;
+	lcache_ready = false;
+	lcache_wrap = lcache_wwrap = lcache_width = -1;
+	
+	Search_buffer_size = 3 * (MAX_VIEWLINEB < 8000 ? 8000 : MAX_VIEWLINEB);
+	Search_buffer = new wchar_t[Search_buffer_size];
+
 	memset(&vString, 0, sizeof(vString));
 	vString.lpData = new wchar_t[MAX_VIEWLINEB];
 }
@@ -169,7 +181,8 @@
 	}
 
 	delete[] vString.lpData;
-	delete[] Up_buffer;
+	delete[] Search_buffer;
+	delete[] lcache_lines;
 	delete[] vread_buffer;
 
 	_tran(SysLog(L"[%p] Viewer::~Viewer, TempViewName=[%s]",this,TempViewName));
@@ -192,11 +205,13 @@
 		}
 	}
 
-	for (int i=0; i<=MAXSCRY; i++)
+	for (int i=MAXSCRY; i >= 0; --i)
 	{
-		delete [] Strings[i]->lpData;
+		if (Strings[i]->lpData)
+			delete[] Strings[i]->lpData;
 		delete Strings[i];
 	}
+	delete[] Strings;
 
 	if (!OpenFailed && bVE_READ_Sent)
 	{
@@ -216,7 +231,7 @@
 	Opt.ViOpt.ViewerIsWrap=VM.Wrap;
 	Opt.ViOpt.ViewerWrap=VM.WordWrap;
 	Opt.ViOpt.SearchRegexp=LastSearchRegexp;
-	InitHex=VM.Hex;
+	//InitHex=VM.Hex;
 }
 
 
@@ -229,7 +244,7 @@
 	ViewFile.Close();
 	Reader.Clear();
 
-	SelectSize = 0; // ������� ��������
+	SelectSize = -1; // ������� ��������
 	strFileName = Name;
 
 	if (Opt.OnlyEditorViewerUsed && !StrCmp(strFileName, L"-"))
@@ -302,7 +317,7 @@
 		// �������� ������������� ��� ��� ���������� �� �� ������ �������
 		if (CachedCodePage && !IsCodePageSupported(CachedCodePage))
 			CachedCodePage = 0;
-		LastSelPos=FilePos=NewFilePos;
+		LastSelectPos=FilePos=NewFilePos;
 		LeftPos=NewLeftPos;
 	}
 	else
@@ -350,19 +365,13 @@
 			CodePageChangedByUser=TRUE;
 		}
 
-		if (!IsUnicodeOrUtfCodePage(VM.CodePage))
-		{
-			ViewFile.SetPointer(0, nullptr, FILE_BEGIN);
-		}
+		ViewFile.SetPointer(0, nullptr, FILE_BEGIN);
 	}
 	SetFileSize();
 
-	if (FilePos>FileSize)
+	if (FilePos > FileSize)
 		FilePos=0;
 
-//  if (ViOpt.AutoDetectTable && !TableChangedByUser)
-//  {
-//  }
 	ChangeViewKeyBar();
 	AdjustWidth();
 	CtrlObject->Plugins.CurViewer=this; // HostFileViewer;
@@ -379,12 +388,12 @@
 		DriveType = DRIVE_CDROM;
 	switch (DriveType) //??? make it configurable
 	{
-		case DRIVE_REMOVABLE: update_check_period = -1;   break; // flash drive or floppy: never
-		case DRIVE_FIXED:     update_check_period = +1;   break; // hard disk: 1msec
-		case DRIVE_REMOTE:    update_check_period = 1000; break; // network drive: 1sec
-		case DRIVE_CDROM:     update_check_period = -1;   break; // cd/dvd: never
-		case DRIVE_RAMDISK:   update_check_period = +1;   break; // ramdrive: 1msec
-		default:              update_check_period = -1;   break; // unknown: never
+		case DRIVE_REMOVABLE: update_check_period = -1;  break; // flash drive or floppy: never
+		case DRIVE_FIXED:     update_check_period = +1;  break; // hard disk: 1 msec
+		case DRIVE_REMOTE:    update_check_period = 500; break; // network drive: 0.5 sec
+		case DRIVE_CDROM:     update_check_period = -1;  break; // cd/dvd: never
+		case DRIVE_RAMDISK:   update_check_period = +1;  break; // ramdrive: 1 msec
+		default:              update_check_period = -1;  break; // unknown: never
 	}
 
 	return TRUE;
@@ -428,8 +437,9 @@
 		SetCursorType(0,10);
 
 	vseek(FilePos,SEEK_SET);
+	LastPage = 0;
 
-	if (!SelectSize)
+	if ( SelectSize < 0 )
 		SelectPos=FilePos;
 
 	switch (nMode)
@@ -471,7 +481,7 @@
 			}
 			FilePos = Strings[0]->nFilePos;
 			SecondPos = Strings[1]->nFilePos;
-			Strings[Y2-Y1]->nFilePos = Strings[Y2-Y1-1]->nFilePos + Strings[Y2-Y1-1]->nbytes;
+			Strings[Y2-Y1]->nFilePos = Strings[Y2-Y1-1]->nFilePos + Strings[Y2-Y1-1]->linesize;
 			vseek(Strings[Y2-Y1]->nFilePos, SEEK_SET);
 			ReadString(Strings[Y2-Y1],-1);
 			break;
@@ -487,21 +497,14 @@
 
 			if (StrLen > LeftPos)
 			{
-				if (IsUnicodeOrUtfCodePage(VM.CodePage) && Signature && !I && !Strings[I]->nFilePos)
-				{
-					FS<<fmt::LeftAlign()<<fmt::Width(Width)<<fmt::Precision(Width)<<&Strings[I]->lpData[static_cast<size_t>(LeftPos+1)];
-				}
-				else
-				{
-					FS<<fmt::LeftAlign()<<fmt::Width(Width)<<fmt::Precision(Width)<<&Strings[I]->lpData[static_cast<size_t>(LeftPos)];
-				}
+				FS<<fmt::LeftAlign()<<fmt::Width(Width)<<fmt::Precision(Width)<<&Strings[I]->lpData[static_cast<size_t>(LeftPos)];
 			}
 			else
 			{
 				FS<<fmt::Width(Width)<<L"";
 			}
 
-			if (SelectSize && Strings[I]->bSelection)
+			if ( SelectSize >= 0 && Strings[I]->bSelection)
 			{
 				__int64 SelX1;
 
@@ -532,7 +535,7 @@
 					if (LeftPos > Strings[I]->nSelEnd)
 						Length = 0;
 
-					FS<<fmt::Precision(static_cast<int>(Length))<<&Strings[I]->lpData[static_cast<size_t>(SelX1+LeftPos+SelectPosOffSet)];
+					FS<<fmt::Precision(static_cast<int>(Length))<<&Strings[I]->lpData[static_cast<size_t>(SelX1+LeftPos)];
 				}
 			}
 
@@ -571,6 +574,9 @@
 	bool bSelStartFound = false, bSelEndFound = false;
 	__int64 HexLeftPos=((LeftPos>80-ObjWidth) ? Max(80-ObjWidth,0):LeftPos);
 
+	const wchar_t BorderLine[] = {BoxSymbols[BS_V1],L' ',0};
+	int border_len = (int)wcslen(BorderLine);
+
 	for (EndFile=0,Y=Y1; Y<=Y2; Y++)
 	{
 		bSelStartFound = false;
@@ -588,12 +594,9 @@
 		if (Y==Y1+1 && !veof())
 			SecondPos=vtell();
 
-		INT64 Ptr=ViewFile.GetPointer();
-		_snwprintf(OutStr,ARRAYSIZE(OutStr),L"%010I64X: ", Ptr);
+		int out_len = _snwprintf(OutStr,ARRAYSIZE(OutStr),L"%010I64X: ", vtell());
+		SelEnd = SelStart = out_len;
 		TextPos=0;
-		int HexStrStart = (int)wcslen(OutStr);
-		SelStart = HexStrStart;
-		SelEnd = SelStart;
 		__int64 fpos = vtell();
 
 		if (fpos > SelectPos)
@@ -602,103 +605,108 @@
 		if (fpos < SelectPos+SelectSize-1)
 			bSelEndFound = true;
 
-		if (!SelectSize)
+		if ( SelectSize < 0 )
 			bSelStartFound = bSelEndFound = false;
 
-		const wchar_t BorderLine[]={BoxSymbols[BS_V1],L' ',0};
+		unsigned char line[16+3];
+		DWORD nr = 0;
+		DWORD nb = CP_UTF8 == VM.CodePage ? 16+3 : 16;
+		Reader.Read(line, nb, &nr);
+		if (nr > 16)
+			Reader.Unread(nr-16);
 
-		int out_len = (int)wcslen(OutStr);
-		int border_len = (int)wcslen(BorderLine);
+		LastPage = EndFile = (nr <= nb) && veof() ? 1 : 0;
 
-		if (IsUnicodeCodePage(VM.CodePage))
+		if (nr <= 0)
 		{
-			wchar_t line[8];
-			int nr = vread(line, 8);
-			LastPage = EndFile = veof() ? 1 : 0;
-			if (nr <= 0)
+			*OutStr = L'\0';
+		}
+		else
+		{
+			if (IsUnicodeCodePage(VM.CodePage))
 			{
-				*OutStr = L'\0';
-			}
-			else
-			{
-				for (X=0; X<8; X++)
+				int be = VM.CodePage == CP_REVERSEBOM ? 1 : 0;
+				for (X=0; X<16; X += 2)
 				{
-					if (SelectSize>0 && (SelectPos == fpos))
+					if (SelectSize >= 0 && (SelectPos == fpos || SelectPos == fpos+1))
 					{
+						bool half = SelectPos != fpos;
 						bSelStartFound = true;
-						SelStart = (int)wcslen(OutStr);
-						SelSize=SelectSize;
+						SelStart = out_len + (half ? 1+be : 0);
+						if ( 0 == (SelSize=SelectSize) )
+							SelStart += (half ? be : 0) - 1;
 					}
-					if (SelectSize>0 && (fpos == (SelectPos+SelectSize-1)))
+					if (SelectSize >= 0 && (fpos == SelectPos+SelectSize-1 || fpos+1 == SelectPos+SelectSize-1))
 					{
+						bool half = fpos == SelectPos+SelectSize-1;
 						bSelEndFound = true;
-						SelEnd = (int)wcslen(OutStr)+3;
-						SelSize=SelectSize;
+						SelEnd = out_len+3 - (half ? 1+be : 0);
+						if ( 0 == (SelSize=SelectSize) )
+							SelEnd = SelStart;
 					}
+					else if ( SelectSize == 0 && (SelectPos == fpos || SelectPos == fpos+1) )
+					{
+						bSelEndFound = true;
+						SelSize = 0;
+						SelEnd = SelStart;
+					}
 
-					if (X < nr)
+					if ((DWORD)X < nr-1) // full character
 					{
-						unsigned char b1 = HIBYTE(line[X]), b2 = LOBYTE(line[X]);
-#if 0																 // !!! ��������� �������� !!!
-						if (VM.CodePage != CP_REVERSEBOM) // ��� ������ � ���� ����
-						{                                 // ������� �������� ������������� ���
-							unsigned char t = b1; b1 = b2; b2 = t;
-						}
-#endif
-						_snwprintf(OutStr+out_len, ARRAYSIZE(OutStr)-out_len, L"%02X%02X ", b1, b2);
-						TextStr[TextPos++] = line[X] ? line[X] : L' ';
+						unsigned ch = line[X+be] + (line[X+1-be] << 8);
+						_snwprintf(OutStr+out_len, ARRAYSIZE(OutStr)-out_len, L"%04X ", ch);
+						TextStr[TextPos++] = ch ? (wchar_t)ch : L' ';
 					}
-					else
+					else if ((DWORD)X == nr-1) // half character only
 					{
+						int o1 = 2 * (1 - be);
+						_snwprintf(OutStr+out_len+o1, ARRAYSIZE(OutStr)-out_len-o1, L"%02X", line[X]);
+						OutStr[out_len+2-o1] = OutStr[out_len+2-o1+1] = L'x';
+						OutStr[out_len+4] = L' ';
+						TextStr[TextPos++] = REPLACE_CHAR;
+					}
+					else // no character
+					{
 						wcscpy(OutStr+out_len, L"     ");
 						TextStr[TextPos++] = L' ';
 					}
 					out_len += 5;
 
-					if (X == 3)
+					if (X == 3*2)
 					{
 						wcscpy(OutStr+out_len, BorderLine);
 						out_len += border_len;
 					}
-					++fpos;
+					fpos += 2;
 				}
 			}
-		}
-		else
-		{
-			unsigned char line[16+3];
-			DWORD nr = 0;
-			DWORD nb = CP_UTF8 == VM.CodePage ? 16+3 : 16;
-			Reader.Read(line, nb, &nr);
-			if (nr > 16)
-				Reader.Unread(nr-16);
-
-			LastPage = EndFile = (nr <= nb) && veof() ? 1 : 0;
-
-			if (!nr)
-			{
-				*OutStr = L'\0';
-			}
 			else
 			{
-				if (SelectSize)
+				if ( SelectSize >= 0 )
 				{
 					if (SelectPos >= fpos && SelectPos < fpos+16)
 					{
 						int off = (int)(SelectPos - fpos);
 						bSelStartFound = true;
 						SelStart = out_len + 3*off + (off < 8 ? 0 : border_len);
-						SelSize = SelectSize;
+						if ( 0 == (SelSize=SelectSize) )
+							--SelStart;
 					}
 					__int64 selectEnd = SelectPos + SelectSize - 1;
 					if (selectEnd >= fpos && selectEnd < fpos+16)
 					{
 						int off = (int)(selectEnd - fpos);
 						bSelEndFound = true;
-						SelEnd = out_len + 3*off + (off < 8 ? 0 : border_len) + 1;
-						SelSize = SelectSize;
+						SelEnd = (0 == (SelSize=SelectSize) ? SelStart : out_len + 3*off + (off < 8 ? 0 : border_len) + 1);
 					}
+					else if ( SelectSize == 0 && SelectPos == fpos )
+					{
+						bSelEndFound = true;
+						SelSize = 0;
+						SelEnd = SelStart;
+					}
 				}
+
 				for (X=0; X<16; X++)
 				{
 					int off = out_len + 3*X + (X < 8 ? 0 : border_len);
@@ -712,6 +720,7 @@
 					if (CP_UTF8 != VM.CodePage && X < (int)nr && line[X])
 						MultiByteToWideChar(VM.CodePage, 0, (LPCSTR)line+X, 1, TextStr+X, 1);
 				}
+				out_len += 3*16 + border_len;
 
 				if (CP_UTF8 == VM.CodePage)
 				{
@@ -730,7 +739,7 @@
 							first = false;
 							TextStr[ib++] = w1[iw] ? w1[iw] : L' ';
 						}
-						int clen = WideCharToMultiByte(CP_UTF8, 0, w2+iw, 1, nullptr, 0, nullptr, nullptr);
+						int clen = WideCharToMultiByte(CP_UTF8, 0, w2+iw, 1, NULL,0, NULL,NULL);
 						while (--clen > 0 && ib < 16)
 							TextStr[ib++] = CONTINUE_CHAR;
 						++iw;
@@ -740,14 +749,14 @@
 			}
 		}
 
-		TextStr[TextPos]=0;
-		wcscat(TextStr,L" ");
+		TextStr[TextPos] = L' ';
+		TextStr[TextPos+1] = L'\0';
 
-		if ((SelEnd <= SelStart) && bSelStartFound)
-			SelEnd = (int)wcslen(OutStr)-2;
+		if ((SelEnd <= SelStart) && bSelStartFound && bSelEndFound && SelectSize > 0 )
+			SelEnd = out_len-2;
 
-		wcscat(OutStr,L" ");
-		wcscat(OutStr,TextStr);
+		OutStr[out_len] = L' ';
+		wcscpy(OutStr+out_len+1, TextStr);
 
 		if (StrLength(OutStr)>HexLeftPos)
 		{
@@ -789,7 +798,7 @@
 		{
 			UINT64 Total=FileSize/16+((FileSize%16)?1:0);
 			UINT64 Top=FilePos/16+((FilePos%16)?1:0);
-ScrollBarEx(X2+(m_bQuickView?1:0),Y1,Y2-Y1+1,LastPage?Top?Total:0:Top,Total);
+			ScrollBarEx(X2+(m_bQuickView?1:0),Y1,Y2-Y1+1,LastPage?Top?Total:0:Top,Total);
 		}
 	}
 }
@@ -837,6 +846,15 @@
 	return L'\0' == ch || IsSpace(ch);
 }
 
+static bool is_word_div ( const wchar_t ch )
+{
+	static const wchar_t spaces[] = { L' ', L'\t', L'\n', L'\r', BOM_CHAR, REPLACE_CHAR, L'\0' };
+	return ( !ch
+		|| nullptr != wcschr(spaces, ch)
+		|| nullptr != wcschr(Opt.strWordDiv, ch)
+	);
+}
+
 static inline int wrapped_char( const wchar_t ch )
 {
 	static const wchar_t wrapped_chars[] = L",;>)"; // word-wrap enabled after it
@@ -849,18 +867,31 @@
 		return -1;
 }
 
-void Viewer::ReadString(ViewerString *pString, int MaxSize)
+static inline int getCharSize( UINT cp )
 {
+	if ( CP_UTF8 == cp )
+		return -1;
+	else if ( CP_UNICODE == cp || CP_REVERSEBOM == cp )
+		return +2;
+	else
+		return +1;
+}
+
+void Viewer::ReadString( ViewerString *pString, int MaxSize, bool update_cache )
+{
 	AdjustWidth();
 
 	int OutPtr = 0, nTab = 0, wrap_out = -1;
 	wchar_t ch, eol_char = L'\0';
-	__int64 fpos, sel_end, wrap_pos = -1;
+	INT64 fpos=0, fpos1, sel_end, wrap_pos = -1;
 	bool skip_space = false;
 
+	if ( !pString->lpData )
+		pString->lpData = new wchar_t[MAX_VIEWLINEB];
+
 	if (VM.Hex)
 	{
-		vseek(IsUnicodeCodePage(VM.CodePage) ? 8 : 16, FILE_CURRENT);
+		vseek(16, FILE_CURRENT);
 		pString->lpData[OutPtr] = L'\0';
 		LastPage = veof();
 		return;
@@ -869,10 +900,12 @@
 	bool bSelStartFound = false, bSelEndFound = false;
 	pString->bSelection = false;
 	sel_end = SelectPos + SelectSize;
-	fpos = vtell();
 
+	fpos1 = vtell();
 	for (;;)
 	{
+		fpos = fpos1;
+
 		if (OutPtr >= MAX_VIEWLINE)
 			break;
 
@@ -880,27 +913,31 @@
 			ch = L' ';
 		else
 		{
-			fpos = vtell();
 			if (!MaxSize-- || !vgetc(&ch))
 				break;
 		}
+		fpos1 = vtell();
 
-		if (SelectSize > 0)
+		if (SelectSize >= 0)
 		{
-			if (fpos == SelectPos)
+			if (fpos == SelectPos || (fpos < SelectPos && fpos1 > SelectPos))
 			{
 				pString->nSelStart = OutPtr;
 				bSelStartFound = true;
 			}
-			if (fpos == sel_end)
+			if (fpos == sel_end || (fpos < sel_end && fpos1 > sel_end))
 			{
-				pString->nSelEnd = OutPtr;
+				pString->nSelEnd = OutPtr + (fpos < sel_end ? 1 : 0);
 				bSelEndFound = true;
 			}
 		}
 
-		if (L'\t' == ch)
+		if (!fpos && BOM_CHAR == ch)
 		{
+			continue; // skip BOM
+		}
+		else if (L'\t' == ch)
+		{
 			nTab = ViOpt.TabSize - (OutPtr % ViOpt.TabSize);
 			continue;
 		}
@@ -913,7 +950,7 @@
 		pString->lpData[OutPtr++] = ch ? ch : L' ';
 		if (!VM.Wrap)
 			continue;
-
+	
 		if (OutPtr >= Width)
 		{
 			if (VM.WordWrap && wrap_out > 0)
@@ -965,9 +1002,12 @@
 
 	pString->have_eol = (eol_char != L'\0');
 	pString->lpData[(int)OutPtr]=0;
-	pString->nbytes = (int)(vtell() - pString->nFilePos);
+	pString->linesize = (int)(vtell() - pString->nFilePos);
 
-	if (SelectSize > 0 && OutPtr > 0)
+	if ( update_cache )
+		CacheLine(pString->nFilePos, pString->linesize, pString->have_eol);
+
+	if (SelectSize >= 0 && OutPtr > 0)
 	{
 		if (!bSelStartFound && pString->nFilePos >= SelectPos && pString->nFilePos <= sel_end)
 		{
@@ -987,8 +1027,86 @@
 
 	if (!eol_char && veof())
 		LastPage = 1;
+}	
+
+
+__int64 Viewer::EndOfScreen( int line )
+{
+	__int64 pos;
+
+	if (!VM.Hex)
+	{
+		pos = Strings[Y2-Y1+line]->nFilePos + Strings[Y2-Y1+line]->linesize;
+		if ( !line && !VM.Wrap && Strings[Y2-Y1]->linesize > 0 )
+		{
+			vseek(Strings[Y1-Y2]->nFilePos, SEEK_SET);
+			int col = 0, rmargin = (int)LeftPos + Width;
+			wchar_t ch;
+			for (;;)
+			{
+				if ( !vgetc(&ch) )
+					break;
+				if ( ch == L'\n' || ch == L'\r' )
+					break;
+				if ( ch == L'\t' )
+					col += ViOpt.TabSize - (col % ViOpt.TabSize);
+				else
+					++col;
+				if ( col >= rmargin )
+				{
+					pos = vtell();
+					break;
+				}
+			}
+		}
+	}
+	else
+		pos =	FilePos + 16 * (Y2-Y1+1+line);
+		
+   if (pos < 0)
+		pos = 0;
+	else if (pos > FileSize)
+		pos = FileSize;
+
+	return pos;
 }
 
+
+__int64 Viewer::BegOfScreen()
+{
+	__int64 pos = FilePos, prev_pos;
+
+	if (!VM.Hex && !VM.Wrap && LeftPos > 0)
+	{
+		vseek(FilePos, SEEK_SET);
+		int col = 0;
+		wchar_t ch;
+		pos = -1;
+		for (;;)
+		{
+			prev_pos = vtell();
+			if ( !vgetc(&ch) )
+				break;
+			if ( ch == L'\n' || ch == L'\r' )
+			{
+				pos = Strings[1]->nFilePos;
+				break;
+			}
+			if ( ch == L'\t' )
+				col += ViOpt.TabSize - (col % ViOpt.TabSize);
+			else
+				++col;
+			if ( col > LeftPos )	//!! ������ �������� ����� ������
+				break;				//!! ��� LeftPos=1 �� ���� 2 �������
+		}
+		if ( pos < 0 )
+			pos = (col > LeftPos ? prev_pos : vtell());
+	}
+
+	return pos;
+}
+
+
 __int64 Viewer::VMProcess(int OpCode,void *vParam,__int64 iParam)
 {
 	switch (OpCode)
@@ -996,7 +1114,7 @@
 		case MCODE_C_EMPTY:
 			return (__int64)!FileSize;
 		case MCODE_C_SELECTED:
-			return (__int64)(SelectSize?TRUE:FALSE);
+			return (__int64)(SelectSize >= 0 ?TRUE:FALSE);
 		case MCODE_C_EOF:
 			return (__int64)(LastPage || !ViewFile.Opened());
 		case MCODE_C_BOF:
@@ -1030,7 +1148,7 @@
 	*/
 	if (!ViOpt.PersistentBlocks &&
 	        Key!=KEY_IDLE && Key!=KEY_NONE && !(Key==KEY_CTRLINS||Key==KEY_CTRLNUMPAD0) && Key!=KEY_CTRLC)
-		SelectSize=0;
+		SelectSize = -1;
 
 	if (!InternalKey && !LastKeyUndo && (FilePos!=UndoData[0].UndoAddr || LeftPos!=UndoData[0].UndoLeft))
 	{
@@ -1055,7 +1173,6 @@
 		{
 			FilePos=BMSavePos.FilePos[Pos];
 			LeftPos=BMSavePos.LeftPos[Pos];
-//      LastSelPos=FilePos;
 			Show();
 		}
 
@@ -1082,25 +1199,20 @@
 		}
 		case KEY_CTRLU:
 		{
-//      if (SelectSize)
-			{
-				SelectSize = 0;
-				Show();
-			}
+			SelectSize = -1;
+			Show();
 			return TRUE;
 		}
 		case KEY_CTRLC:
 		case KEY_CTRLINS:  case KEY_CTRLNUMPAD0:
 		{
-			if (SelectSize && ViewFile.Opened())
+			if (SelectSize >= 0 && ViewFile.Opened())
 			{
-				wchar_t *SelData;
-				size_t DataSize = (size_t)SelectSize+(IsUnicodeCodePage(VM.CodePage)?sizeof(wchar_t):1);
-				__int64 CurFilePos=vtell();
-
-				if ((SelData=(wchar_t*)xf_malloc(DataSize*sizeof(wchar_t))) )
+				wchar_t *SelData = (wchar_t*)xf_malloc((size_t)SelectSize+1);
+				if ( SelData )
 				{
-					wmemset(SelData, 0, DataSize);
+					__int64 CurFilePos=vtell();
+					wmemset(SelData, 0, (size_t)SelectSize+1);
 					vseek(SelectPos,SEEK_SET);
 					vread(SelData, (int)SelectSize);
 					CopyToClipboard(SelData);
@@ -1108,7 +1220,6 @@
 					vseek(CurFilePos,SEEK_SET);
 				}
 			}
-
 			return TRUE;
 		}
 		//   �����/����� ��������
@@ -1145,6 +1256,8 @@
 
 					Reader.Clear(); // ���� ���� �� ��� ����?
 					ViewFile.FlushBuffers();
+					vseek(0, SEEK_CUR); // reset vgetc state
+					lcache_ready = false; // reset start-lines cache
 
 					if (FilePos>FileSize)
 					{
@@ -1251,19 +1364,16 @@
 		}
 		case KEY_SHIFTF2:
 		{
-			LastPage = 0;
 			ProcessTypeWrapMode(!VM.WordWrap);
 			return TRUE;
 		}
 		case KEY_F2:
 		{
-			LastPage = 0;
 			ProcessWrapMode(!VM.Wrap);
 			return TRUE;
 		}
 		case KEY_F4:
 		{
-			LastPage = 0;
 			ProcessHexMode(!VM.Hex);
 			return TRUE;
 		}
@@ -1280,23 +1390,13 @@
 		}
 		case KEY_ALTF7:
 		{
-			SearchFlags.Set(REVERSE_SEARCH);
-			Search(1,0);
-			SearchFlags.Clear(REVERSE_SEARCH);
+			Search(-1,0);
 			return TRUE;
 		}
 		case KEY_F8:
 		{
-			LastPage = 0;
-			if (IsUnicodeCodePage(VM.CodePage))
-			{
-				FilePos*=2;
-				SetFileSize();
-				SelectPos = 0;
-				SelectSize = 0;
-			}
-
 			VM.CodePage = VM.CodePage==GetOEMCP() ? GetACP() : GetOEMCP();
+			lcache_ready = false;
 			ChangeViewKeyBar();
 			Show();
 			CodePageChangedByUser=TRUE;
@@ -1304,12 +1404,12 @@
 		}
 		case KEY_SHIFTF8:
 		{
-			LastPage = 0;
 			UINT nCodePage = SelectCodePage(VM.CodePage, true, true, false, true);
+			lcache_ready = false;
 
-			if (nCodePage!=(UINT)-1)
+			if (nCodePage != static_cast<UINT>(-1))
 			{
-				if (nCodePage == (WORD)(CP_AUTODETECT & 0xffff))
+				if (nCodePage == (CP_AUTODETECT & 0xffff))
 				{
 					__int64 fpos = vtell();
 					bool detect = GetFileFormat(ViewFile,nCodePage,&Signature,true) && IsCodePageSupported(nCodePage);
@@ -1318,20 +1418,6 @@
 						nCodePage = Opt.ViOpt.AnsiCodePageAsDefault ? GetACP() : GetOEMCP();
 				}
 				CodePageChangedByUser=TRUE;
-
-				if (IsUnicodeCodePage(VM.CodePage) && !IsUnicodeCodePage(nCodePage))
-				{
-					FilePos*=2;
-					SelectPos = 0;
-					SelectSize = 0;
-				}
-				else if (!IsUnicodeCodePage(VM.CodePage) && IsUnicodeCodePage(nCodePage))
-				{
-					FilePos=(FilePos+(FilePos&1))/2; //????
-					SelectPos = 0;
-					SelectSize = 0;
-				}
-
 				VM.CodePage=nCodePage;
 				SetFileSize();
 				ChangeViewKeyBar();
@@ -1402,11 +1488,10 @@
 		{
 			if (FilePos>0 && ViewFile.Opened())
 			{
-				Up();	// LastPage = 0
+				Up(1);	// LastPage = 0
 
 				if (VM.Hex)
 				{
-					//FilePos&=~(IsUnicodeCodePage(VM.CodePage) ? 0x7:0xf);
 					Show();
 				}
 				else
@@ -1414,7 +1499,7 @@
 					ShowPage(SHOW_UP);
 					ViewerString *end = Strings[Y2-Y1];
 					LastPage = end->nFilePos >= FileSize ||
-					   (!end->have_eol && end->nFilePos + end->nbytes >= FileSize);
+						(!end->have_eol && end->nFilePos + end->linesize >= FileSize);
 				}
 			}
 
@@ -1439,9 +1524,7 @@
 		{
 			if (ViewFile.Opened())
 			{
-				for (int i=Y1; i<Y2; i++)
-					Up();
-
+				Up(Y2-Y1);
 				Show();
 			}
 
@@ -1452,25 +1535,25 @@
 			if (LastPage || !ViewFile.Opened())
 				return TRUE;
 
-			if (VM.Hex)
-				FilePos += (IsUnicodeCodePage(VM.CodePage) ? 8 : 16) * (Y2-Y1);
-			else
-				FilePos = Strings[Y2-Y1]->nFilePos;
+			FilePos = EndOfScreen(-1); // start of last screen line
 
 			if (Key == KEY_CTRLDOWN)
 			{
-				vseek(FilePos, SEEK_SET);
+				vseek(vString.nFilePos = FilePos, SEEK_SET);
 				for (int i=Y1; i<=Y2; i++)
-				ReadString(&vString,-1);
+				{
+					ReadString(&vString,-1);
+					vString.nFilePos += vString.linesize;
+				}
 
 				if (LastPage)
-			{
-				InternalKey++;
-				ProcessKey(KEY_CTRLPGDN);
-				InternalKey--;
-				return TRUE;
+				{
+					InternalKey++;
+					ProcessKey(KEY_CTRLPGDN);
+					InternalKey--;
+					return TRUE;
+				}
 			}
-			}
 
 			Show();
 			return TRUE;
@@ -1504,17 +1587,11 @@
 			{
 				if (VM.Hex)
 				{
-					FilePos--;
-
-					if (FilePos<0)
-						FilePos=0;
+					FilePos = FilePos > 0 ? FilePos-1 : 0;
 				}
 				else
 				{
-					LeftPos-=20;
-
-					if (LeftPos<0)
-						LeftPos=0;
+					LeftPos = LeftPos > 20 ? LeftPos-20 : 0;
 				}
 
 				Show();
@@ -1528,10 +1605,7 @@
 			{
 				if (VM.Hex)
 				{
-					FilePos++;
-
-					if (FilePos >= FileSize)
-						FilePos=FileSize-1; //??
+					FilePos = FilePos < FileSize-1 ? FilePos+1 : FileSize-1;
 				}
 				else if (!VM.Wrap)
 				{
@@ -1592,7 +1666,6 @@
 			if (ViewFile.Opened())
 			{
 				FilePos=0;
-				LastPage = 0;
 				Show();
 			}
 
@@ -1614,32 +1687,33 @@
 				   ��������� End (� ������) �� ����� ������ �������� �� ���������
 				   Down.
 				*/
-				unsigned int max_counter=Y2-Y1;
+				int max_counter = Y2 - Y1;
 
 				if (VM.Hex)
 				{
 					vseek(0,SEEK_END);
 					FilePos = vtell();
-					int line_chars = IsUnicodeCodePage(VM.CodePage) ? 8 : 16;
-					if ((FilePos % line_chars) == 0)
-						FilePos -= line_chars * (Y2 - Y1 + 1);
+					if ((FilePos % 16) == 0)
+						FilePos -= 16 * (Y2 - Y1 + 1);
 					else
-						FilePos -= (FilePos % line_chars) + line_chars * (Y2 - Y1);
+						FilePos -= (FilePos % 16) + 16 * (Y2 - Y1);
 					if (FilePos < 0)
 						FilePos = 0;
 				}
 				else
 				{
-					vseek(-1,SEEK_END);
-					WCHAR LastSym=0;
+					int ch_size = getCharSize(VM.CodePage);
+					wchar_t LastSym = L'\0';
 
-					if (vgetc(&LastSym) && LastSym!=L'\n' && LastSym!=L'\r')
+					vseek(0, SEEK_END);
+					FilePos = vtell() - 1;
+					FilePos -= FilePos % ch_size;
+					vseek(FilePos, SEEK_SET);
+					if (vgetc(&LastSym) && LastSym != L'\n' && LastSym != L'\r')
 						++max_counter;
 
 					FilePos=vtell();
-
-					for (int i=0; static_cast<unsigned int>(i)<max_counter; i++)
-						Up();
+					Up(max_counter);
 				}
 
 				Show();
@@ -1688,7 +1762,7 @@
 		{
 			while (IsMouseButtonPressed())
 			{
-//        _SVS(SysLog(L"Viewer/ KEY_DOWN= %i, %i",FilePos,FileSize));
+				//_SVS(SysLog(L"Viewer/ KEY_DOWN= %i, %i",FilePos,FileSize));
 				ProcessKey(KEY_DOWN);
 			}
 		}
@@ -1719,7 +1793,7 @@
 				else
 					Perc=ToPercent64(FilePos,FileSize);
 
-//_SVS(SysLog(L"Viewer/ ToPercent()=%i, %I64d, %I64d, Mouse=[%d:%d]",Perc,FilePos,FileSize,MsX,MsY));
+				//_SVS(SysLog(L"Viewer/ ToPercent()=%i, %I64d, %I64d, Mouse=[%d:%d]",Perc,FilePos,FileSize,MsX,MsY));
 				if (Perc == 100)
 					ProcessKey(KEY_CTRLEND);
 				else if (!Perc)
@@ -1796,140 +1870,270 @@
 	return TRUE;
 }
 
-void Viewer::Up()
+
+void Viewer::CacheLine( __int64 start, int length, bool have_eol )
 {
-	if (!ViewFile.Opened())
+	assert(start >= 0 && length >= 0);
+	if (!length) // empty lines beyond EOF
 		return;
 
-	LastPage = 0;
+	if ( lcache_ready
+	 && (lcache_wrap != VM.Wrap || lcache_wwrap != VM.WordWrap || lcache_width != Width)
+	){
+		lcache_ready = false;
+	}
 
-	if (FilePos <= 0)
-		return;
-
-	if (VM.Hex)
+	if (!lcache_ready || start > lcache_last || start+length < lcache_first)
 	{
-		FilePos -= IsUnicodeCodePage(VM.CodePage) ? 8 : 16;
-		if (FilePos < 0)
-			FilePos = 0;
-		return;
-	}
+		lcache_first = start;
+		lcache_last = start + length;
 
-	int Skipped = 0, I = -1, BufSize = MAX_VIEWLINE + 2;
-	wchar_t *Buf = Up_buffer, *tBuf = Up_buffer, *tBuf2 = nullptr;
+		lcache_count = 2;
+		lcache_base = 0;
+		lcache_lines[0] = (have_eol ? -start : +start);
+		lcache_lines[1] = start + length;
 
-	if (!VM.Hex && VM.CodePage == CP_UTF8)
-	{
-		BufSize = Up_buffer_size / 2; // = 3*MAXLINE + 2;
-		tBuf = tBuf2 = Up_buffer + BufSize;
+		lcache_wrap = VM.Wrap; lcache_wwrap = VM.WordWrap; lcache_width = Width;
+		lcache_ready = true;
 	}
-
-	if (FilePos < (__int64)BufSize)
-		BufSize = (int)FilePos;
-
-	if (BufSize >= 512) // first try to find newline in first 256 bytes
+	else if (start == lcache_last)
 	{
-		vseek(FilePos-256, SEEK_SET);
-		int nr = vread(Buf, 256, tBuf2);
-
-		if (nr > 0 && Buf[nr-1] == L'\n')
+		int i = (lcache_base + lcache_count - 1) % lcache_size;
+		lcache_lines[i] = (have_eol ? -start : +start);
+		i = (i + 1) % lcache_size;
+		lcache_lines[i]	= lcache_last = start + length;
+		if (lcache_count < lcache_size)
+			++lcache_count;
+		else
 		{
-			--nr; ++Skipped;
+			lcache_base = (lcache_base + 1) % lcache_size; // ++start
+			lcache_first = _abs64(lcache_lines[lcache_base]);
 		}
-		if (nr > 0 && Buf[nr-1] == L'\r')
+	}
+	else if (start+length == lcache_first)
+	{
+	   lcache_base = (lcache_base + lcache_size - 1) % lcache_size; // --start
+      lcache_lines[lcache_base] = (have_eol ? -start : +start);
+		lcache_first = start;
+		if (lcache_count < lcache_size)
+			++lcache_count;
+		else
 		{
-			--nr; ++Skipped;
+			int i = (lcache_base + lcache_size - 1) % lcache_size; // i = start - 1
+			lcache_last = _abs64(lcache_lines[i]);
 		}
+	}
+	else
+	{
+#if defined(_DEBUG) && 1 // it is legal case if file changed...
+		assert(start >= lcache_first && start+length <= lcache_last);
+		int i = CacheFindUp(start+length);
+		assert(i >= 0 && _abs64(lcache_lines[i]) == start);
+#endif
+		lcache_first = start;
+		lcache_last = start + length;
 
-		for (I = nr-1; I >= 0; I--)
-		{
-			if (Buf[I] == L'\n' || Buf[I] == L'\r')
-			{
-				BufSize = nr;
-				break;
-			}
-		}
+		lcache_count = 2;
+		lcache_base = 0;
+		lcache_lines[0] = (have_eol ? -start : +start);
+		lcache_lines[1] = start + length;
 	}
+}
 
-	if (I < 0) // full portion read
+int Viewer::CacheFindUp( __int64 start )
+{
+	if ( lcache_ready
+		&& (lcache_wrap != VM.Wrap || lcache_wwrap != VM.WordWrap || lcache_width != Width)
+	){
+		lcache_ready = false;
+	}
+	if ( !lcache_ready || start <= lcache_first || start > lcache_last )
+		return -1;
+
+	int i, j, i1 = 0, i2 = lcache_count - 1;
+   for (;;)
 	{
-		vseek(FilePos - BufSize, SEEK_SET);
-		BufSize = vread(Buf, BufSize, tBuf2);
-		Skipped=0;
+		if ( i1+1 >= i2 )
+			return (lcache_base + i1) % lcache_size;
 
-		if (BufSize > 0 && Buf[BufSize-1]==L'\n')
-		{
-			--BufSize; ++Skipped;
-		}
-		if (BufSize > 0 && Buf[BufSize-1] == L'\r')
-		{
-			--BufSize; ++Skipped;
-		}
-
-		int I0 = BufSize > MAX_VIEWLINE ? BufSize - MAX_VIEWLINE : 0;
-		for (I = BufSize-1; I >= I0; I--)
-		{
-			if (Buf[I] == L'\n' || Buf[I] == L'\r')
-				break;
-		}
+		i = (i1 + i2) / 2;
+		j = (lcache_base + i) % lcache_size;
+		if (_abs64(lcache_lines[j]) < start)
+			i1 = i;
+		else
+			i2 = i;
 	}
+}
 
-	if (!VM.Wrap)
+void Viewer::Up( int nlines )
+{
+	assert( nlines > 0 );
+
+	if (!ViewFile.Opened())
+		return;
+
+   LastPage = 0;
+
+	if (FilePos <= 0)
+		return;
+
+	if (VM.Hex)
 	{
-		FilePos -= GetStrBytesNum(tBuf + (I+1), BufSize-(I+1)) + Skipped;
+		FilePos = FilePos > 16*nlines ? FilePos-16*nlines : 0;
+		return;
 	}
-	else
+
+	__int64 fpos, fpos1;
+
+	int i = CacheFindUp(fpos = FilePos);
+	if ( i >= 0 )
 	{
-		wchar_t *line = tBuf + I+1;
-		int len, line_size = BufSize - (I+1);
 		for (;;)
 		{
-			len = WrappedLength(line, line_size);
-			if ( len >= line_size )
+			fpos = _abs64(lcache_lines[i]);
+			if (--nlines == 0)
+			{
+				FilePos = fpos;
+				return;
+			}
+			if (i == lcache_base)
 				break;
-			line += len;
-			line_size -= len;
+			i = (i + lcache_size - 1) % lcache_size;
 		}
-		FilePos -= GetStrBytesNum(line, line_size + Skipped);
 	}
-	return;
-}
 
-int Viewer::WrappedLength(wchar_t *str, int line_size)
-{
-	int width = 0, off = 0, wrap_pos = -1, wrap_ch, tab_size = ViOpt.TabSize;
-	bool wwrap = VM.Wrap && VM.WordWrap;
+	static const int portion_size = 256;
+	static const int max_backward = 32*1024;
 
-	for (;;)
+	union {
+		char c1[portion_size];
+		wchar_t c2[portion_size/(int)sizeof(wchar_t)];
+	} buff;
+
+	int j, buff_size, nr, ch_size = getCharSize(VM.CodePage);
+	int len[max_backward/40]; // suppose minimum line with is 80 chars...
+
+	while ( nlines > 0 )
 	{
-		if (off >= line_size)
-			return off;
+		if ( fpos <= 0 )
+		{
+			FilePos = 0;
+			return;
+		}
 
-		wchar_t ch = str[off++];
-		width += L'\t' != ch ? 1 : tab_size - width % tab_size;
+		fpos1 = fpos;
 
-		if (width >= Width)
+		// backward CR-LF search
+		//
+		for ( j = 0; j < max_backward/portion_size; ++j )
 		{
-			if (!wwrap || wrap_pos <= 0)
-				return off;
-			while (wrap_pos < line_size && is_space_or_nul(str[wrap_pos]))
-				++wrap_pos;
-			return wrap_pos;
+			buff_size = (fpos > (__int64)portion_size ? portion_size : (int)fpos);
+			fpos -= buff_size;
+			vseek(fpos, SEEK_SET);
+
+			if ( ch_size <= 1 )
+			{
+				DWORD nread = 0;
+				Reader.Read(buff.c1, static_cast<DWORD>(buff_size), &nread);
+				if ((nr = (int)nread) != buff_size)
+				{
+					return; //??? error handling
+				}
+				if ( 0 == j )
+				{
+					if ( nr > 0 && '\n' == buff.c1[nr-1] )
+					{
+						--nr;
+					}
+					if ( nr > 0 && '\r' == buff.c1[nr-1] )
+					{
+						--nr;
+					}
+				}
+				for ( i = nr-1; i >= 0; --i )
+				{
+					if ( '\n' == buff.c1[i] || '\r' == buff.c1[i] )
+						break;
+				}
+				if ( i >= 0 )
+				{
+					fpos += i + 1;
+					break;
+				}
+			}
+			else
+			{
+				nr = vread(buff.c2, buff_size);
+				if ( nr != buff_size / ch_size )
+				{
+					return; //??? error handling
+				}
+				if ( 0 == j )
+				{
+					if ( nr > 0 && L'\n' == buff.c2[nr-1] )
+					{
+						--nr;
+					}
+					if ( nr > 0 && L'\r' == buff.c2[nr-1] )
+					{
+						--nr;
+					}
+				}
+				for ( i = nr-1; i >= 0; --i )
+				{
+					if ( L'\n' == buff.c2[i] || L'\r' == buff.c2[i] )
+						break;
+				}
+				if ( i >= 0 )
+				{
+					fpos += ch_size * (i + 1);
+					break;
+				}
+			}
 		}
 
-		if (!wwrap)
-			continue;
+		// split read portion 
+		//
+		vseek(vString.nFilePos = fpos, SEEK_SET);
+		for (i = 0; i < (int)ARRAYSIZE(len); ++i)
+		{
+			ReadString(&vString, -1, false);
+			len[i] = (vString.have_eol ? -1 : +1) * vString.linesize;
+			if ((vString.nFilePos += vString.linesize) >= fpos1)
+			{
+				fpos1 = vString.nFilePos;
+				break;
+			}
+		}
+		assert(i < (int)ARRAYSIZE(len));
+		if (i >= (int)ARRAYSIZE(len))
+			--i;
 
-		if ((wrap_ch = wrapped_char(ch)) >= 0)
-			wrap_pos = off - 1 + wrap_ch;
+		while ( i >= 0 )
+		{
+			int l = len[i--];
+			bool eol = false;
+			if (l < 0)
+			{
+				eol = true;
+				l = -l;
+			}
+			fpos1 -= l;
+			CacheLine(fpos1, l, eol);
+			if (--nlines == 0)
+				FilePos = fpos1;
+		}
 	}
 }
 
-int Viewer::GetStrBytesNum(const wchar_t *Str, int Length)
+
+int Viewer::GetStrBytesNum( const wchar_t *Str, int Length )
 {
-	if (IsUnicodeCodePage(VM.CodePage))
-		return Length;
+	int ch_size = getCharSize(VM.CodePage);
+	if (ch_size > 0)
+		return Length * ch_size;
 	else
-		return WideCharToMultiByte(VM.CodePage, 0, Str, Length, nullptr, 0, nullptr, nullptr);
+		return WideCharToMultiByte(VM.CodePage,0, Str,Length, nullptr,0, nullptr,nullptr);
 }
 
 void Viewer::SetViewKeyBar(KeyBar *ViewKeyBar)
@@ -1938,7 +2142,6 @@
 	ChangeViewKeyBar();
 }
 
-
 void Viewer::ChangeViewKeyBar()
 {
 	if (ViewKeyBar)
@@ -1971,6 +2174,7 @@
 	CtrlObject->Plugins.CurViewer=this; //HostFileViewer;
 }
 
+
 enum SEARCHDLG
 {
 	SD_DOUBLEBOX,
@@ -1991,9 +2195,18 @@
 
 enum
 {
- DM_SDSETVISIBILITY = DM_USER+1,
+	DM_SDSETVISIBILITY = DM_USER + 1,
+	DM_SDREXVISIBILITY = DM_USER + 2
 };
 
+struct MyDialogData
+{
+   Viewer      *viewer;
+	bool edit_autofocus;
+   bool       hex_mode;
+	bool      recursive;
+};
+
 INT_PTR WINAPI ViewerSearchDlgProc(HANDLE hDlg,int Msg,int Param1,void* Param2)
 {
 	switch (Msg)
@@ -2010,42 +2223,133 @@
 			SendDlgMessage(hDlg,DM_SHOWITEM,SD_EDIT_TEXT,ToPtr(!Param1));
 			SendDlgMessage(hDlg,DM_SHOWITEM,SD_EDIT_HEX,ToPtr(Param1));
 			SendDlgMessage(hDlg,DM_ENABLE,SD_CHECKBOX_CASE,ToPtr(!Param1));
-			SendDlgMessage(hDlg,DM_ENABLE,SD_CHECKBOX_WORDS,ToPtr(!Param1));
-			//SendDlgMessage(hDlg,DM_ENABLE,SD_CHECKBOX_REGEXP,!Param1);
+			int re = SendDlgMessage(hDlg, DM_GETCHECK,SD_CHECKBOX_REGEXP,0) == BSTATE_CHECKED;
+			int ww = !Param1 && !re;
+			SendDlgMessage(hDlg,DM_ENABLE,SD_CHECKBOX_WORDS,ToPtr(ww));
+			SendDlgMessage(hDlg,DM_ENABLE,SD_CHECKBOX_REGEXP,ToPtr(!Param1));
+			SendDlgMessage(hDlg, DM_SDREXVISIBILITY, re && !Param1, 0);
 			return TRUE;
 		}
+		case DM_SDREXVISIBILITY:
+		{
+		   int show = 1;
+			if ( Param1 )
+			{
+				int tlen = (int)SendDlgMessage(hDlg, DM_GETTEXTPTR, SD_EDIT_TEXT, 0);
+				show = 0;
+				if ( tlen > 0 )
+				{
+					RegExp re;
+					wchar_t t[128], *tmp = t;
+					if (tlen > (int)ARRAYSIZE(t))
+						tmp = new wchar_t[tlen];
+					SendDlgMessage(hDlg, DM_GETTEXTPTR, SD_EDIT_TEXT, tmp);
+					string sre = tmp;
+					InsertRegexpQuote(sre);
+					show = re.Compile(sre.CPtr(), OP_PERLSTYLE);
+					if (tmp != t)
+						delete[] tmp;;
+				}
+			}
+			SendDlgMessage(hDlg, DM_ENABLE, SD_TEXT_SEARCH, ToPtr(show));
+			SendDlgMessage(hDlg, DM_ENABLE, SD_BUTTON_OK,   ToPtr(show));
+			return TRUE;
+		}
+		case DN_KILLFOCUS:
+		{
+			if ( SD_EDIT_TEXT == Param1 || SD_EDIT_HEX == Param1 )
+			{
+				MyDialogData *my = (MyDialogData *)SendDlgMessage(hDlg, DM_GETITEMDATA, SD_EDIT_TEXT, 0);
+				my->hex_mode = (SD_EDIT_HEX == Param1);
+			}
+			break;
+		}
 		case DN_BTNCLICK:
 		{
+			bool need_focus = false;
+			MyDialogData *my = (MyDialogData *)SendDlgMessage(hDlg, DM_GETITEMDATA, SD_EDIT_TEXT, 0);
+			int cradio = (my->hex_mode ? SD_RADIO_HEX : SD_RADIO_TEXT);
+
 			if ((Param1 == SD_RADIO_TEXT || Param1 == SD_RADIO_HEX) && Param2)
 			{
-				SendDlgMessage(hDlg, DM_ENABLEREDRAW, FALSE, 0);
-				Viewer *viewer = (Viewer *)SendDlgMessage(hDlg, DM_GETITEMDATA, SD_EDIT_TEXT, 0);
+				need_focus = true;
+				if ( Param1 != cradio)
+				{
+					bool new_hex = (Param1 == SD_RADIO_HEX);
 
-				int sd_dst = Param1 == SD_RADIO_HEX  ? SD_EDIT_HEX : SD_EDIT_TEXT;
-				int sd_src = Param1 == SD_RADIO_TEXT ? SD_EDIT_HEX : SD_EDIT_TEXT;
+					SendDlgMessage(hDlg, DM_ENABLEREDRAW, FALSE, 0);
 
-				const wchar_t *ps = (const wchar_t *)SendDlgMessage(hDlg, DM_GETCONSTTEXTPTR, sd_src, 0);
-				string strTo;
-				viewer->SearchTextTransform(strTo, ps, Param1 == SD_RADIO_TEXT);
+					int sd_dst = new_hex ? SD_EDIT_HEX : SD_EDIT_TEXT;
+					int sd_src = new_hex ? SD_EDIT_TEXT : SD_EDIT_HEX;
 
-				SendDlgMessage(hDlg, DM_SETTEXTPTR, sd_dst, const_cast<wchar_t*>(strTo.CPtr()));
-				SendDlgMessage(hDlg, DM_SDSETVISIBILITY, Param1 == SD_RADIO_HEX, 0);
+					EditorSetPosition esp;
+					esp.CurPos = -1;
+					SendDlgMessage(hDlg, DM_GETEDITPOSITION, sd_src, &esp);
+					const wchar_t *ps = (const wchar_t *)SendDlgMessage(hDlg, DM_GETCONSTTEXTPTR, sd_src, 0);
+					string strTo;
+					my->viewer->SearchTextTransform(strTo, ps, !new_hex, esp.CurPos);
 
-				if (!strTo.IsEmpty())
-				{
-					int changed = (int)SendDlgMessage(hDlg, DM_EDITUNCHANGEDFLAG, sd_src, ToPtr(-1));
-					SendDlgMessage(hDlg, DM_EDITUNCHANGEDFLAG, sd_dst, ToPtr(changed));
+					SendDlgMessage(hDlg, DM_SETTEXTPTR, sd_dst, ToPtr((INT_PTR)strTo.CPtr()));
+					SendDlgMessage(hDlg, DM_SDSETVISIBILITY, new_hex, 0);
+					if (esp.CurPos >= 0)
+					{
+						int p = esp.CurPos;
+						if (SendDlgMessage(hDlg, DM_GETEDITPOSITION, sd_dst, &esp))
+						{
+							esp.CurPos = esp.CurTabPos = p;
+							esp.LeftPos = 0;
+							SendDlgMessage(hDlg, DM_SETEDITPOSITION, sd_dst, &esp);
+						}
+					}
+
+					if (!strTo.IsEmpty())
+					{
+						int changed = (int)SendDlgMessage(hDlg, DM_EDITUNCHANGEDFLAG, sd_src, ToPtr(-1));
+						SendDlgMessage(hDlg, DM_EDITUNCHANGEDFLAG, sd_dst, ToPtr(changed));
+					}
+
+					SendDlgMessage(hDlg,DM_ENABLEREDRAW,TRUE,0);
+					my->hex_mode = new_hex;
+					if ( !my->edit_autofocus )
+						return TRUE;
 				}
+			}
+			else if ( Param1 == SD_CHECKBOX_REGEXP )
+			{
+				SendDlgMessage(hDlg, DM_SDSETVISIBILITY, my->hex_mode, 0);
+			}
 
-				SendDlgMessage(hDlg,DM_ENABLEREDRAW,TRUE,0);
+			if ( my->edit_autofocus && !my->recursive )
+			{
+				if ( need_focus
+				  || Param1 == SD_CHECKBOX_CASE
+				  || Param1 == SD_CHECKBOX_WORDS
+				  || Param1 == SD_CHECKBOX_REVERSE
+				  || Param1 == SD_CHECKBOX_REGEXP
+				){
+					my->recursive = true;
+					SendDlgMessage(hDlg, DM_SETFOCUS, my->hex_mode ? SD_EDIT_HEX : SD_EDIT_TEXT, 0);
+					my->recursive = false;
+				}
+			}
+
+			if (need_focus)
 				return TRUE;
-			}
+			else
+				break;
 		}
+		case DN_EDITCHANGE:
+		{
+			if ( Param1 == SD_EDIT_TEXT && SendDlgMessage(hDlg,DM_GETCHECK,SD_CHECKBOX_REGEXP,0) == BSTATE_CHECKED )
+				SendDlgMessage(hDlg, DM_SDREXVISIBILITY, 1, 0);
+			break;
+		}
 		case DN_HOTKEY:
 		{
 			if (Param1==SD_TEXT_SEARCH)
 			{
-				SendDlgMessage(hDlg,DM_SETFOCUS,(SendDlgMessage(hDlg,DM_GETCHECK,SD_RADIO_HEX,0) == BSTATE_CHECKED)?SD_EDIT_HEX:SD_EDIT_TEXT,0);
+				MyDialogData *my = (MyDialogData *)SendDlgMessage(hDlg, DM_GETITEMDATA, SD_EDIT_TEXT, 0);
+				SendDlgMessage(hDlg, DM_SETFOCUS, (my->hex_mode ? SD_EDIT_HEX : SD_EDIT_TEXT), 0);
 				return FALSE;
 			}
 		}
@@ -2059,10 +2363,13 @@
 static void PR_ViewerSearchMsg()
 {
 	PreRedrawItem preRedrawItem=PreRedraw.Peek();
-	ViewerSearchMsg((const wchar_t*)preRedrawItem.Param.Param1,(int)(INT_PTR)preRedrawItem.Param.Param2);
+	const wchar_t *name = (const wchar_t*)preRedrawItem.Param.Param1;
+	int percent = (int)(INT_PTR)preRedrawItem.Param.Param2;
+	int search_hex = (int)(INT_PTR)preRedrawItem.Param.Param3;
+	ViewerSearchMsg(name, percent, search_hex);
 }
 
-void ViewerSearchMsg(const wchar_t *MsgStr,int Percent)
+void ViewerSearchMsg(const wchar_t *MsgStr, int Percent, int SearchHex)
 {
 	string strProgress;
 
@@ -2093,6 +2400,7 @@
 	PreRedrawItem preRedrawItem=PreRedraw.Peek();
 	preRedrawItem.Param.Param1=(void*)MsgStr;
 	preRedrawItem.Param.Param2=(LPVOID)(INT_PTR)Percent;
+	preRedrawItem.Param.Param3=(LPVOID)(INT_PTR)SearchHex;
 	PreRedraw.SetParam(preRedrawItem.Param);
 }
 
@@ -2109,15 +2417,17 @@
 	}
 }
 
-static int hex2ss(const wchar_t *from, char *c1, int mb)
+static int hex2ss(const wchar_t *from, char *c1, int mb, int *pos = 0)
 {
-	int nb, i, v, sub = 0;
+	int nb, i, v, sub = 0, ps = 0, p0 = (pos ? *pos : -1), p1 = -1;
 	wchar_t ch;
 
 	nb = i = v = 0;
 	for (;;)
 	{
 		ch = *from++;
+		++ps;
+
 		if      (ch >= L'0' && ch <= L'9') sub = L'0';
 		else if (ch >= L'A' && ch <= L'F') sub = L'A' - 10;
 		else if (ch >= L'a' && ch <= L'f') sub = L'a' - 10;
@@ -2125,6 +2435,8 @@
 		{
 			if (i > 0)
 			{
+				if ( p0 >= 0 && ps > p0 && p1 < 0 )
+					p1 = nb;
 				c1[nb++] = (char)v;
 				i = 0;
 			}
@@ -2136,6 +2448,8 @@
 		v = v*16 + ((int)ch - sub);
 		if (++i >= 2)
 		{
+			if ( p0 >= 0 && ps > p0 && p1 < 0 )
+				p1 = nb;
 			c1[nb++] = (char)v;
 			i = 0;
 			if (nb >= mb)
@@ -2145,10 +2459,12 @@
 #ifdef _DEBUG
 	if (nb < mb) c1[nb] = '\0';
 #endif
+	if ( p1 >= 0 && pos )
+		*pos = p1;
 	return nb;
 }
 
-void Viewer::SearchTextTransform( UnicodeString &to, const wchar_t *from, bool hex2text )
+void Viewer::SearchTextTransform( UnicodeString &to, const wchar_t *from, bool hex2text, int &pos )
 {
 	int nb, i, v;
 	char c1[128];
@@ -2156,7 +2472,7 @@
 
 	if (hex2text)
 	{
-		nb = hex2ss(from, c1, ARRAYSIZE(c1));
+		nb = hex2ss(from, c1, ARRAYSIZE(c1), &pos);
 
 		if (IsUnicodeCodePage(VM.CodePage))
 		{
@@ -2171,10 +2487,16 @@
 					ch = 0xffff; // encode L'\0'
 				to += ch;
 			}
+			if ( pos >= 0 )
+				pos /= 2;
 		}
 		else
 		{
 			int nw = MultiByteToWideChar(VM.CodePage,0, c1,nb, ss,ARRAYSIZE(ss));
+			if ( pos >= 0 )
+			{
+				pos = MultiByteToWideChar(VM.CodePage,0, c1,pos, NULL,0);
+			}
 			for (i=0; i < nw; ++i)
 				if (!ss[i])
 					ss[i] = 0xffff;
@@ -2184,8 +2506,12 @@
 	}
 	else // text2hex
 	{
+		int ps = 0, pd = 0, p0 = pos, p1 = -1;
 		while (*from)
 		{
+			if ( ps == p0 )
+				p1 = pd;
+			++ps;
 			ch = *from++;
 			if (0xffff == ch)	// 0xffff - invalid unicode char
 				ch = 0x0000;   // it used to transfer '\0' in zero terminated string
@@ -2199,398 +2525,737 @@
 					nb = 2; c1[0] = (char)HIBYTE(ch); c1[1] = (char)LOBYTE(ch);
 				break;
 				default:
-					nb = WideCharToMultiByte(VM.CodePage,0, &ch, 1, c1, 4, nullptr, nullptr);
+					nb = WideCharToMultiByte(VM.CodePage,0, &ch,1, c1,4, NULL,NULL);
 				break;
 			}
 
 			ss2hex(to, c1, nb, L'\0');
+			pd += nb * 3;
 		}
+		pos = p1;
 	}
 }
 
-/* $ 27.01.2003 VVM
-   + �������� Next ����� �������� ������:
-   0 - ���� �����
-   1 - ��������� ����� �� ������� ������
-   2 - ��������� ����� � ����� �����
-*/
+struct Viewer::search_data
+{
+	INT64 CurPos;
+	INT64 MatchPos;
+	const char *search_bytes;
+	const wchar_t *search_text;
+	int search_len;
+	int  ch_size;
+	bool is_utf8;
+   bool first_Rex;
+	RegExp *pRex;
 
-void Viewer::Search(int Next,int FirstChar)
-{
-	const wchar_t *TextHistoryName=L"SearchText";
-	const wchar_t *HexMask=L"HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH ";
-	FarDialogItem SearchDlgData[]=
+	search_data()
 	{
-		{DI_DOUBLEBOX,3,1,72,11,0,nullptr,nullptr,0,MSG(MViewSearchTitle)},
-		{DI_TEXT,5,2,0,2,0,nullptr,nullptr,0,MSG(MViewSearchFor)},
-		{DI_EDIT,5,3,70,3,0,TextHistoryName,nullptr,DIF_FOCUS|DIF_HISTORY|DIF_USELASTHISTORY,L""},
-		{DI_FIXEDIT,5,3,70,3,0,nullptr,HexMask,DIF_MASKEDIT,L""},
-		{DI_TEXT,3,4,0,4,0,nullptr,nullptr,DIF_SEPARATOR,L""},
-		{DI_RADIOBUTTON,5,5,0,5,1,nullptr,nullptr,DIF_GROUP,MSG(MViewSearchForText)},
-		{DI_RADIOBUTTON,5,6,0,6,0,nullptr,nullptr,0,MSG(MViewSearchForHex)},
-		{DI_CHECKBOX,40,5,0,5,0,nullptr,nullptr,0,MSG(MViewSearchCase)},
-		{DI_CHECKBOX,40,6,0,6,0,nullptr,nullptr,0,MSG(MViewSearchWholeWords)},
-		{DI_CHECKBOX,40,7,0,7,0,nullptr,nullptr,0,MSG(MViewSearchReverse)},
-		{DI_CHECKBOX,40,8,0,8,0,nullptr,nullptr,DIF_DISABLE,MSG(MViewSearchRegexp)},
-		{DI_TEXT,3,9,0,9,0,nullptr,nullptr,DIF_SEPARATOR,L""},
-		{DI_BUTTON,0,10,0,10,0,nullptr,nullptr,DIF_DEFAULTBUTTON|DIF_CENTERGROUP,MSG(MViewSearchSearch)},
-		{DI_BUTTON,0,10,0,10,0,nullptr,nullptr,DIF_CENTERGROUP,MSG(MViewSearchCancel)},
-	};
-	MakeDialogItemsEx(SearchDlgData,SearchDlg);
-	string strSearchStr;
-	char search_bytes[128];
-	string strMsgStr;
-	__int64 MatchPos=0;
-	int SearchLength,Case,WholeWords,ReverseSearch,Match,SearchRegexp;
+		CurPos = MatchPos = -1;
+		search_bytes = nullptr;
+		search_text  = nullptr;
+		search_len = ch_size = 0;
+		is_utf8 = false;
+		first_Rex = true;
+		pRex = nullptr;
+	}
 
-	if (!ViewFile.Opened() || (Next && strLastSearchStr.IsEmpty()))
-		return;
+	~search_data()
+	{
+		 delete pRex;
+	}
+};
 
-	if (!strLastSearchStr.IsEmpty())
-		strSearchStr = strLastSearchStr;
+int Viewer::search_hex_forward( search_data* sd )
+{
+	char *buff = (char *)Search_buffer;
+	const char *search_str = sd->search_bytes;
+	int bsize = Search_buffer_size * static_cast<int>(sizeof(wchar_t)), slen = sd->search_len;
+	INT64 to, cpos = sd->CurPos;
+
+	bool up_half = cpos >= StartSearchPos;
+	if ( up_half )
+	{
+		if ( (to = FileSize) - cpos <= bsize )
+			SetFileSize();
+		to = FileSize;
+	}
 	else
-		strSearchStr.Clear();
+	{
+		to = StartSearchPos + slen - 1;
+		if ( to > FileSize )
+			to = FileSize;
+	}
 
-	SearchDlg[SD_RADIO_TEXT].Selected=!LastSearchHex;
-	SearchDlg[SD_RADIO_HEX].Selected=LastSearchHex;
-	SearchDlg[SD_CHECKBOX_CASE].Selected=LastSearchCase;
-	SearchDlg[SD_CHECKBOX_WORDS].Selected=LastSearchWholeWords;
-	SearchDlg[SD_CHECKBOX_REVERSE].Selected=LastSearchReverse;
-	SearchDlg[SD_CHECKBOX_REGEXP].Selected=LastSearchRegexp;
+	int nb = (to - cpos < bsize ? static_cast<int>(to - cpos) : bsize);
+	vseek(cpos, SEEK_SET);
+	DWORD nr = 0;
+	Reader.Read(buff, static_cast<DWORD>(nb), &nr);
+	int n1 = static_cast<int>(nr);
+	if ( n1 != nb )
+		SetFileSize();
 
-	if (SearchFlags.Check(REVERSE_SEARCH))
-		SearchDlg[SD_CHECKBOX_REVERSE].Selected=!SearchDlg[SD_CHECKBOX_REVERSE].Selected;
+	if ( n1 < slen )
+	{
+		if ( !up_half || StartSearchPos <= 0 )
+			return -1;
+		sd->CurPos = 0;
+		return 0;
+	}
 
-	if (SearchDlg[SD_RADIO_HEX].Selected)
+	char *ps = buff;
+	while ( nullptr != (ps = static_cast<char *>(memchr(ps, search_str[0], n1-slen+1))) )
 	{
-		int nb = hex2ss(strSearchStr.CPtr(), search_bytes, ARRAYSIZE(search_bytes));
-		strSearchStr.Clear();
-		ss2hex(strSearchStr, search_bytes, nb, L'\0');
-		SearchDlg[SD_EDIT_HEX].strData = strSearchStr;
+		if ( slen <= 1 || 0 == memcmp(ps+1, search_str+1, slen-1) )
+		{
+			sd->MatchPos = cpos + static_cast<INT64>(ps - buff);
+			return +1; // found
+		}
+		++ps;
+		n1 = static_cast<int>(nr - (ps - buff)); 
 	}
+
+	sd->CurPos = cpos + nr - slen + 1;
+	return (up_half || sd->CurPos < StartSearchPos ? 0 : -1);
+}
+
+int Viewer::search_hex_backward( search_data* sd )
+{
+	char *buff = (char *)Search_buffer;
+	const char *search_str = sd->search_bytes;
+	int bsize = Search_buffer_size * static_cast<int>(sizeof(wchar_t)), slen = sd->search_len;
+	INT64 to, cpos = sd->CurPos;
+
+	bool lo_half = cpos <= StartSearchPos;
+	if ( lo_half )
+	{
+		to = 0;
+	}
 	else
-		SearchDlg[SD_EDIT_TEXT].strData = strSearchStr;
+	{
+		to = StartSearchPos - slen + 1;
+		if ( to < 0 )
+			to = 0;
+	}
 
-	SearchDlg[SD_EDIT_TEXT].UserData = this;
+	int nb = (cpos - to < bsize ? static_cast<int>(cpos - to) : bsize);
+	vseek(cpos-nb, SEEK_SET);
+	DWORD nr = 0;
+	Reader.Read(buff, static_cast<DWORD>(nb), &nr);
+	int n1 = static_cast<int>(nr);
+	if ( n1 != nb )
+	{
+		SetFileSize();
+		cpos = vtell();
+	}
 
-	if (!Next)
+	if ( n1 < slen )
 	{
-		SearchFlags.Flags = 0;
-		Dialog Dlg(SearchDlg,ARRAYSIZE(SearchDlg),ViewerSearchDlgProc);
-		Dlg.SetPosition(-1,-1,76,13);
-		Dlg.SetHelp(L"ViewerSearch");
+		if ( !lo_half )
+			return -1;
+		SetFileSize();
+		if ( StartSearchPos >= FileSize )
+			return -1;
+		sd->CurPos = FileSize;
+		return 0;
+	}
 
-		if (FirstChar)
+	for ( char *ps = buff + n1 - 1; ps >= buff + slen - 1; --ps )
+	{
+		if ( *ps == search_str[slen-1] )
 		{
-			Dlg.InitDialog();
-			Dlg.Show();
-			Dlg.ProcessKey(FirstChar);
+			if ( slen <= 1 || 0 == memcmp(ps-slen+1, search_str, slen-1) )
+			{
+				sd->MatchPos = static_cast<INT64>(cpos-n1+(ps-slen+1-buff));
+				return +1;
+			}
 		}
+	}
 
-		Dlg.Process();
+	sd->CurPos = cpos - nr + slen - 1;
+	return (lo_half || sd->CurPos > StartSearchPos ? 0 : -1);
+}
 
-		if (Dlg.GetExitCode()!=SD_BUTTON_OK)
-			return;
+int Viewer::search_text_forward( search_data* sd )
+{
+	int bsize = 8192, slen = sd->search_len, ww = (LastSearchWholeWords ? 1 : 0);
+	wchar_t prev_char = L'\0', *buff = Search_buffer, *t_buff = (sd->is_utf8 ? buff + bsize : nullptr);
+	const wchar_t *search_str = sd->search_text;
+	INT64 to1, to, cpos = sd->CurPos;
+
+	vseek(cpos, SEEK_SET);
+	if ( ww )
+		prev_char = vgetc_prev();
+
+	bool up_half = cpos >= StartSearchPos;
+	if ( up_half )
+	{
+		if ( (to = FileSize) - cpos <= bsize )
+			SetFileSize();
+		to = FileSize;
 	}
+	else
+	{
+		to = StartSearchPos;
+		if ( to > FileSize )
+			to = FileSize;
+	}
 
-	SearchHex=SearchDlg[SD_RADIO_HEX].Selected;
-	Case=SearchDlg[SD_CHECKBOX_CASE].Selected;
-	WholeWords=SearchDlg[SD_CHECKBOX_WORDS].Selected;
-	ReverseSearch=SearchDlg[SD_CHECKBOX_REVERSE].Selected;
-	SearchRegexp=SearchDlg[SD_CHECKBOX_REGEXP].Selected;
+   int nb = (to - cpos > bsize ? bsize : static_cast<int>(to - cpos));
+	int nw = vread(buff, nb, t_buff);
+	to1 = vtell();
+	if ( !up_half && nb + 3*(slen+ww) < bsize && !veof() )
+	{
+		int nw1 = vread(buff+nw, 3*(slen+ww), t_buff ? t_buff+nw : nullptr);
+		nw1 = (nw1 > slen+ww-1 ? slen+ww-1 : nw1);
+		nw += nw1;
+		to1 = to + (t_buff ? GetStrBytesNum(t_buff, nw1) : sd->ch_size * nw1);
+	}
 
-	bool t_utf8 = !SearchHex && VM.CodePage == CP_UTF8;
+	int is_eof = (to1 >= FileSize ? 1 : 0), iLast = nw - slen - ww + is_eof;
+	if ( !LastSearchCase )
+		CharUpperBuff(buff, nw);
 
-	if (SearchHex)
+	for ( int i = 0; i <= iLast; ++i )
 	{
-		strSearchStr = SearchDlg[SD_EDIT_HEX].strData;
-		SearchLength = hex2ss(strSearchStr.CPtr(), search_bytes, ARRAYSIZE(search_bytes));
-		strSearchStr.Clear();
-		ss2hex(strSearchStr, search_bytes, SearchLength, L' ');
+		if ( ww )
+		{
+			if ( !is_word_div(i > 0 ? buff[i-1] : prev_char))
+				continue;
+			if ( !(i == iLast && is_eof) && !is_word_div(buff[i+slen]) )
+				continue;
+		}
+		if ( buff[i] != search_str[0]
+		 || (slen > 1 && buff[i+1] != search_str[1])
+		 || (slen > 2 && 0 != wmemcmp(buff+i+2, search_str+2, slen-2))
+		) continue;
+
+		sd->MatchPos = cpos + GetStrBytesNum(t_buff, i);
+		sd->search_len = GetStrBytesNum(t_buff+i, slen);
+		return +1;
 	}
+
+	if ( up_half && is_eof )
+	{
+		sd->CurPos = 0;
+		return (StartSearchPos > 0 ? 0 : -1);
+	}
 	else
 	{
-		strSearchStr = SearchDlg[SD_EDIT_TEXT].strData;
-		SearchLength = (int)strSearchStr.GetLength();
-		size_t pos = 0;
-		while (strSearchStr.Pos(pos, 0xffff))
-			strSearchStr.Replace(pos, 1, L'\0');
+		if ( iLast < 0 || (!up_half && to1 > StartSearchPos) )
+			return -1;
+		sd->CurPos = to1;
+		return 0;
 	}
+}
 
-	strLastSearchStr = strSearchStr;
-	LastSearchHex=SearchHex;
-	LastSearchCase=Case;
-	LastSearchWholeWords=WholeWords;
+int Viewer::search_text_backward( search_data* sd )
+{
+	int bsize = 8192, slen = sd->search_len, ww = (LastSearchWholeWords ? 1 : 0);
+	wchar_t *buff = Search_buffer, *t_buff = (sd->is_utf8 ? buff + bsize : nullptr);
+	const wchar_t *search_str = sd->search_text;
+	INT64 to1, to, cpos = sd->CurPos;
 
-	if (!SearchFlags.Check(REVERSE_SEARCH))
-		LastSearchReverse=ReverseSearch;
+	bool up_half = cpos > StartSearchPos;
+	to = (up_half ? StartSearchPos : 0);
 
-	LastSearchRegexp=SearchRegexp;
+	int nb = (cpos - to > bsize ? bsize : static_cast<int>(cpos- to));
+	if ( up_half && nb + 3*(slen+ww) < bsize && cpos > nb )
+	{
+		if ( sd->ch_size > 0 )
+		{
+			nb += sd->ch_size * (slen + ww - 1);
+			if (cpos < nb)
+				nb = static_cast<int>(cpos);
+		}
+		else
+		{
+			to1 = cpos - nb - 3*(slen + ww - 1);
+			if (to1 < 0)
+				to1 = 0;
+			int nb1 = static_cast<int>(cpos - nb - to1);
+			vseek(to1, SEEK_SET);
+			int nw1 = vread(buff, nb1, t_buff);
+			if ( nw1 > slen + ww - 1 )
+				nb1 = GetStrBytesNum(t_buff + nw1 - (slen + ww - 1), slen + ww - 1);
+			nb += nb1;
+		}
+	}
 
-	if (!SearchLength)
-		return;
+	cpos -= nb;
+	vseek(cpos, SEEK_SET);
+	int nw = vread(buff, nb, t_buff);
+	if ( !LastSearchCase )
+		CharUpperBuff(buff, nw);
 
+	int is_eof = (veof() ? 1 : 0), iFirst = ww * (cpos > 0 ? 1 : 0), iLast = nw - slen - ww + ww*is_eof;
+	for ( int i = iLast; i >= iFirst; --i )
 	{
-		TPreRedrawFuncGuard preRedrawFuncGuard(PR_ViewerSearchMsg);
-		//SaveScreen SaveScr;
-		SetCursorType(FALSE,0);
-		strMsgStr = strSearchStr;
+		if ( ww )
+		{
+			if ( i > 0 && !is_word_div(buff[i-1]) )
+				continue;
+			if ( !(i == iLast && is_eof) && !is_word_div(buff[i+slen]) )
+				continue;
+		}
+		if ( buff[i] != search_str[0]
+		|| (slen > 1 && buff[i+1] != search_str[1])
+		|| (slen > 2 && 0 != wmemcmp(buff+i+2, search_str+2, slen-2))
+		) continue;
 
-		if (strMsgStr.GetLength()+18 > static_cast<DWORD>(ObjWidth))
-			TruncStrFromEnd(strMsgStr, ObjWidth-18);
+		sd->MatchPos = cpos + GetStrBytesNum(t_buff, i);
+		sd->search_len = GetStrBytesNum(t_buff+i, slen);
+		return +1;
+	}
 
-		if (!SearchHex)
-			InsertQuote(strMsgStr);
+	int ret = 0;
+	if ( up_half )
+		ret = (cpos <= StartSearchPos ? -1 : 0);
+	else
+	{
+		if ( cpos <= 0 )
+		{
+			SetFileSize();
+			cpos = FileSize;
+			ret = (StartSearchPos >= FileSize ? -1 : 0);
+		}
+	}
+	sd->CurPos = cpos;
+   return ret;
+}
 
-		if (SearchHex)
-			Case = 1 + (WholeWords = 0);
+int Viewer::read_line(wchar_t *buf, wchar_t *tbuf, INT64 cpos, int adjust, INT64 &lpos, int &lsize)
+{
+	int llen = 0;
 
-		if (!Case && !SearchHex)
-			strSearchStr.Upper();
+	INT64 save_FilePos = FilePos, save_LastPage = LastPage;
+	int save_Hex = VM.Hex, save_Wrap = VM.Wrap, save_WordWrap = VM.WordWrap;
+	VM.Hex = VM.Wrap = VM.WordWrap = 0;
 
-		SelectSize = 0;
+	FilePos = cpos;
+	if ( adjust )
+	{
+		if ( adjust > 0 )
+			AdjustFilePos();
+		else
+			Up(1);
+	}
 
-		if (Next)
+	vseek(lpos = vString.nFilePos = FilePos, SEEK_SET);
+	vString.linesize = 0;
+	ReadString(&vString, -1, false); // read unwrapped text line
+
+	vseek(FilePos, SEEK_SET);
+	llen = vread(buf, lsize = vString.linesize, tbuf);
+	if ( llen > 0 && vString.have_eol ) // remove eol-s
+	{
+		--llen;
+		if ( llen > 0 && (buf[llen-1] == L'\r' || buf[llen-1] == L'\n') )
+			--llen;
+	}
+	buf[llen >= 0 ? llen : 0] = L'\0';
+
+   VM.Hex = save_Hex; VM.Wrap = save_Wrap; VM.WordWrap = save_WordWrap;
+	FilePos = save_FilePos; LastPage = save_LastPage;
+	return llen;
+}
+
+int Viewer::search_regex_forward( search_data* sd )
+{
+	assert(sd->pRex);
+	assert(Search_buffer_size >= 2*MAX_VIEWLINEB);
+
+	wchar_t *line = Search_buffer, *t_line = sd->is_utf8 ? Search_buffer + MAX_VIEWLINEB : nullptr;
+	INT64 cpos = sd->CurPos, bpos = 0;
+
+	int first = (sd->first_Rex ? +1 : 0);
+	sd->first_Rex = false;
+	bool up_half = cpos >= StartSearchPos;
+
+	int lsize = 0, nw = read_line(line, t_line, cpos, first, bpos, lsize);
+   if ( lsize <= 0 )
+	{
+		sd->CurPos = 0;
+		return (up_half && StartSearchPos > 0 ? 0 : -1);
+	}
+
+	int off = 0;
+	for (;;)
+	{
+		if ( off > nw )
+			break;
+
+		SMatch m[1];
+		int n = static_cast<int>(ARRAYSIZE(m));
+		if ( !sd->pRex->SearchEx(line, line+off, line+nw, m, n) )  // doesn't match
+			break;
+
+		INT64 fpos = bpos + GetStrBytesNum(t_line, m[0].start);
+		if ( fpos < cpos )
 		{
-			if (Next == 2)
-			{
-				SearchFlags.Set(SEARCH_MODE2);
-				LastSelPos = ReverseSearch?FileSize:0;
-			}
-			else
-			{
-				LastSelPos = SelectPos + (ReverseSearch?-1:1);
-			}
+			off = m[0].start + 1; // skip
+			continue;
 		}
-		else
+		else if ( !up_half && fpos >= StartSearchPos )
 		{
-			LastSelPos = FilePos;
+			break; // done - not found
+		}
+		else // found
+		{
+			sd->MatchPos = fpos;
+			sd->search_len = GetStrBytesNum(t_line+off, m[0].end - m[0].start);
+			return +1;
+		}
+	}
 
-			if (!LastSelPos || LastSelPos == FileSize)
-				SearchFlags.Set(SEARCH_MODE2);
+	sd->CurPos = vtell();
+	if ( up_half )
+	{
+		if ( veof() )
+		{
+			sd->CurPos = 0;
+			return (StartSearchPos > 0 ? 0 : -1);
 		}
+		return 0;
+	}
+	else
+	{
+		return (sd->CurPos >= StartSearchPos ? -1 : 0);
+	}
+}
 
-		vseek(LastSelPos,SEEK_SET);
-		Match = 0;
+int Viewer::search_regex_backward( search_data* sd )
+{
+	assert(sd->pRex);
+	assert(Search_buffer_size >= 2*MAX_VIEWLINEB);
 
-		if (SearchLength > 0 && (!ReverseSearch || LastSelPos > 0))
+	wchar_t *line = Search_buffer, *t_line = sd->is_utf8 ? Search_buffer + MAX_VIEWLINEB : nullptr;
+	INT64 cpos = sd->CurPos, bpos = 0, prev_pos = -1;
+
+	bool up_half = cpos > StartSearchPos;
+
+	int off=0, lsize=0, nw, prev_len = -1, flen;
+	nw = read_line(line, t_line, cpos, -1, bpos, lsize);
+	for (;;)
+	{
+		if ( lsize <= 0 || off > nw )
+			break;
+
+		SMatch m[1];
+		int n = static_cast<int>(ARRAYSIZE(m));
+		if ( !sd->pRex->SearchEx(line, line+off, line+nw, m, n) )
+			break;
+
+		INT64 fpos = bpos + GetStrBytesNum(t_line, m[0].start);
+		flen = GetStrBytesNum(t_line + m[0].start, m[0].end - m[0].start);
+		if ( fpos+flen > cpos )
+			break;
+
+		if ( !(up_half && fpos+flen <= StartSearchPos) )
 		{
-			wchar_t *Buf = new wchar_t[8192 + 8192], *t_Buf = t_utf8 ? Buf + 8192 : nullptr;
-			int buf_size = 8192, seek_size = 1;
-			char *byte_buff = nullptr;
-			if (SearchHex)
-			{
-				byte_buff = (char *)Buf;
-				buf_size *= 2*(int)sizeof(wchar_t);
-				seek_size = IsUnicodeCodePage(VM.CodePage) ? 2 : 1;
-			}
+			prev_pos = fpos;
+			prev_len = flen;
+		}
 
-			__int64 CurPos=LastSelPos;
-			int BufSize = buf_size;
+		off = m[0].start + 1; // skip
+		continue;
+	}
 
-			if (ReverseSearch)
-			{
-				/* $ 01.08.2000 KM
-				   ������� �������� CurPos � ���� Whole words
-				*/
-				if (WholeWords)
-					CurPos -= BufSize/seek_size - SearchLength/seek_size + 1;
-				else
-					CurPos -= BufSize/seek_size - SearchLength/seek_size;
+	if ( prev_len >= 0 )
+	{
+		sd->MatchPos = prev_pos;
+		sd->search_len = prev_len;
+		return +1;
+	}
+	
+	if ( (sd->CurPos = bpos) <= 0 )
+	{
+		SetFileSize();
+		sd->CurPos = FileSize;
+		return (StartSearchPos >= FileSize ? -1 : 0);
+	}
+   return (up_half && bpos <= StartSearchPos ? -1 : 0);
+}
 
-				if (CurPos<0)
-					BufSize += (int)CurPos * seek_size;
-			}
 
-			int ReadSize;
-			TaskBar TB;
-			wakeful W;
-			INT64 StartPos = vtell();
-			DWORD StartTime = GetTickCount();
+/*
+ + �������� Next ����� �������� ������:
+ 0 - ���� �����
+ 1 - ��������� ����� �� ������� ������
+-1 - ��������� ����� �� ������� ������ � ��������������� �����������
+ 2 - ��������� ����� � �����/���� �����
+*/
+void Viewer::Search(int Next,int FirstChar)
+{
+	if (!ViewFile.Opened() || (Next && strLastSearchStr.IsEmpty()))
+		return;
 
-			while (!Match)
-			{
-				/* $ 01.08.2000 KM
-				   �������� ������ if (ReverseSearch && CurPos<0) �� if (CurPos<0),
-				   ��� ��� ��� ����� ����� � LastSelPos=0xFFFFFFFF, �����
-				   ���������� ��� � �� ������.
-				*/
-				if (CurPos < 0)
-					CurPos = 0;
+	string strSearchStr, strMsgStr;
+	char search_bytes[128];
+	int Case,WholeWords,ReverseSearch,SearchRegexp,SearchHex;
 
-				vseek(CurPos, SEEK_SET);
+	SearchHex = LastSearchHex;
+	Case = LastSearchCase;
+	WholeWords = LastSearchWholeWords;
+	ReverseSearch = LastSearchReverse;
+	SearchRegexp = LastSearchRegexp;
+	strSearchStr.Clear();
+	if (!strLastSearchStr.IsEmpty())
+		strSearchStr = strLastSearchStr;
 
-				if (SearchHex)
-				{
-					DWORD nr = 0;
-					Reader.Read(byte_buff, (DWORD)BufSize, &nr);
-					ReadSize = (int)nr;
-				}
-				else
-				{
-					ReadSize = vread(Buf, BufSize, t_Buf);
-				}
-				if (ReadSize <= 0)
-					break;
+	search_data sd;
+	int (Viewer::* searcher)( Viewer::search_data *p_sd ) = nullptr;
 
-				DWORD CurTime=GetTickCount();
+	if ( !Next )
+	{
+		const wchar_t *TextHistoryName=L"SearchText";
+		const wchar_t *HexMask = L"HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH " ;
+		FarDialogItem SearchDlgData[]=
+		{
+			{DI_DOUBLEBOX,3,1,72,11,0,nullptr,nullptr,0,MSG(MViewSearchTitle)},
+			{DI_TEXT,5,2,0,2,0,nullptr,nullptr,0,MSG(MViewSearchFor)},
+			{DI_EDIT,5,3,70,3,0,TextHistoryName,nullptr,DIF_FOCUS|DIF_HISTORY|DIF_USELASTHISTORY,L""},
+			{DI_FIXEDIT,5,3,70,3,0,nullptr,HexMask,DIF_MASKEDIT,L""},
+			{DI_TEXT,3,4,0,4,0,nullptr,nullptr,DIF_SEPARATOR,L""},
+			{DI_RADIOBUTTON,5,5,0,5,1,nullptr,nullptr,DIF_GROUP,MSG(MViewSearchForText)},
+			{DI_RADIOBUTTON,5,6,0,6,0,nullptr,nullptr,0,MSG(MViewSearchForHex)},
+			{DI_CHECKBOX,40,5,0,5,0,nullptr,nullptr,0,MSG(MViewSearchCase)},
+			{DI_CHECKBOX,40,6,0,6,0,nullptr,nullptr,0,MSG(MViewSearchWholeWords)},
+			{DI_CHECKBOX,40,7,0,7,0,nullptr,nullptr,0,MSG(MViewSearchReverse)},
+			{DI_CHECKBOX,40,8,0,8,0,nullptr,nullptr,DIF_DISABLE,MSG(MViewSearchRegexp)},
+			{DI_TEXT,3,9,0,9,0,nullptr,nullptr,DIF_SEPARATOR,L""},
+			{DI_BUTTON,0,10,0,10,0,nullptr,nullptr,DIF_DEFAULTBUTTON|DIF_CENTERGROUP,MSG(MViewSearchSearch)},
+			{DI_BUTTON,0,10,0,10,0,nullptr,nullptr,DIF_CENTERGROUP,MSG(MViewSearchCancel)},
+		};
+		MakeDialogItemsEx(SearchDlgData,SearchDlg);
 
-				if (CurTime-StartTime>RedrawTimeout)
-				{
-					StartTime=CurTime;
+		SearchDlg[SD_RADIO_TEXT].Selected=!LastSearchHex;
+		SearchDlg[SD_RADIO_HEX].Selected=LastSearchHex;
+		SearchDlg[SD_CHECKBOX_CASE].Selected=LastSearchCase;
+		SearchDlg[SD_CHECKBOX_WORDS].Selected=LastSearchWholeWords;
+		SearchDlg[SD_CHECKBOX_REVERSE].Selected=LastSearchReverse;
+		SearchDlg[SD_CHECKBOX_REGEXP].Selected=LastSearchRegexp;
 
-					if (CheckForEscSilent())
-					{
-						if (ConfirmAbortOp())
-						{
-							Redraw();
-							return;
-						}
-					}
+		if (SearchDlg[SD_RADIO_HEX].Selected)
+		{
+			int nb = hex2ss(strSearchStr.CPtr(), search_bytes, ARRAYSIZE(search_bytes));
+			strSearchStr.Clear();
+			ss2hex(strSearchStr, search_bytes, nb, L'\0');
+			SearchDlg[SD_EDIT_HEX].strData = strSearchStr;
+		}
+		else
+			SearchDlg[SD_EDIT_TEXT].strData = strSearchStr;
 
-					INT64 Total=ReverseSearch?StartPos:FileSize-StartPos;
-					INT64 Current=_abs64(CurPos-StartPos);
-					int Percent=Total>0?static_cast<int>(Current*100/Total):-1;
-					// � ����� ���� ���� ����������� �������, �� ���������
-					// �������� ����� �� ���� 100. ����������� ��� ������.
-					if (Percent>100)
-					{
-						SetFileSize();
-						Total=FileSize-StartPos;
-						Percent=Total>0?static_cast<int>(Current*100/Total):-1;
-						if (Percent>100)
-						{
-							Percent=100;
-						}
-					}
-					ViewerSearchMsg(strMsgStr,Percent);
-				}
+		MyDialogData my;
+		//
+		my.viewer = this;
+		my.edit_autofocus = (ViOpt.SearchEditFocus != 0);
+		my.hex_mode = (LastSearchHex != 0);
+		my.recursive = false;
+		//
+		SearchDlg[SD_EDIT_TEXT].UserData = &my;
 
-				/* $ 01.08.2000 KM
-				   ������� ����� �������� �� Case sensitive � Hex
-				   � ���� ���, ����� Buf ��������� � ������� ��������
-				*/
-				if (!Case && !SearchHex)
-					CharUpperBuff(Buf, ReadSize);
+		Dialog Dlg(SearchDlg,ARRAYSIZE(SearchDlg),ViewerSearchDlgProc);
+		Dlg.SetPosition(-1,-1,76,13);
+		Dlg.SetHelp(L"ViewerSearch");
 
-				/* $ 01.08.2000 KM
-				   ����� ����� ������ ����� ��������� ��������� ������
-				   � Buf � ������� ��������, ���� ����� �� ����������������
-				   ��� �� ���� Hex-������ � � ���� � ���� ����������� ��� ������
-				*/
-				int MaxSize=ReadSize-SearchLength+1;
-				int Increment=ReverseSearch ? -seek_size : +seek_size;
+		if (FirstChar)
+		{
+			Dlg.InitDialog();
+			Dlg.Show();
+			Dlg.ProcessKey(FirstChar);
+		}
 
-				for (int I=ReverseSearch ? MaxSize-1:0; I<MaxSize && I>=0; I+=Increment)
-				{
-					/* $ 01.08.2000 KM
-					   ��������� ������ "Whole words"
-					*/
-					int locResultLeft=FALSE;
-					int locResultRight=FALSE;
+		Dlg.Process();
 
-					if (WholeWords)
-					{
-						if (I)
-						{
-							if (IsSpace(Buf[I-1]) || IsEol(Buf[I-1]) ||
-							        (wcschr(Opt.strWordDiv,Buf[I-1])))
-								locResultLeft=TRUE;
-						}
-						else
-						{
-							locResultLeft=TRUE;
-						}
+		if (Dlg.GetExitCode()!=SD_BUTTON_OK)
+			return;
 
-						if (ReadSize!=BufSize && I+SearchLength>=ReadSize)
-							locResultRight=TRUE;
-						else if (I+SearchLength<ReadSize &&
-						         (IsSpace(Buf[I+SearchLength]) || IsEol(Buf[I+SearchLength]) ||
-						          (wcschr(Opt.strWordDiv,Buf[I+SearchLength]))))
-							locResultRight=TRUE;
-					}
-					else
-					{
-						locResultLeft=TRUE;
-						locResultRight=TRUE;
-					}
+		SearchHex=SearchDlg[SD_RADIO_HEX].Selected;
+		Case=SearchDlg[SD_CHECKBOX_CASE].Selected;
+		WholeWords=SearchDlg[SD_CHECKBOX_WORDS].Selected;
+		ReverseSearch=SearchDlg[SD_CHECKBOX_REVERSE].Selected;
+		SearchRegexp=SearchDlg[SD_CHECKBOX_REGEXP].Selected;
 
-					if (!SearchHex)
-					{
-						Match = locResultLeft && locResultRight && strSearchStr.At(0) == Buf[I] &&
-							( SearchLength==1 ||
-								( strSearchStr.At(1) == Buf[I+1] &&
-									(SearchLength==2 || !memcmp(strSearchStr.CPtr()+2,&Buf[I+2],(SearchLength-2)*sizeof(wchar_t)))
-								)
-							)
-						;
-					}
-					else
-					{
-						if ((Match = search_bytes[0] == byte_buff[I+0]) && SearchLength > 1)
-							if ((Match = search_bytes[1] == byte_buff[I+1]) && SearchLength > 2)
-								if ((Match = search_bytes[2] == byte_buff[I+2]) && SearchLength > 3)
-									if ((Match = search_bytes[3] == byte_buff[I+3]) && SearchLength > 4)
-										Match = !memcmp(search_bytes+4, byte_buff+I+4, SearchLength-4);
-					}
+		if (SearchHex)
+		{
+			strSearchStr = SearchDlg[SD_EDIT_HEX].strData;
+			int len = hex2ss(strSearchStr.CPtr(), search_bytes, ARRAYSIZE(search_bytes));
+			strSearchStr.Clear();
+			ss2hex(strSearchStr, search_bytes, len, L' ');
+		}
+		else
+		{
+			strSearchStr = SearchDlg[SD_EDIT_TEXT].strData;
+			size_t pos = 0;
+			while (strSearchStr.Pos(pos, 0xffff))
+				strSearchStr.Replace(pos, 1, L'\0');
+		}
+	}
 
-					if (Match)
+	LastSearchCase = Case;
+	LastSearchWholeWords = WholeWords;
+	LastSearchReverse = ReverseSearch;
+	LastSearchRegexp = SearchRegexp;
+
+	if (Next == -1)
+		ReverseSearch = !ReverseSearch;
+
+	strMsgStr = strLastSearchStr = strSearchStr;
+	if (strMsgStr.GetLength()+18 > static_cast<DWORD>(ObjWidth))
+		TruncStrFromEnd(strMsgStr, ObjWidth-18);
+
+	sd.search_len = (int)strSearchStr.GetLength();
+	if (0 != (LastSearchHex = SearchHex))
+	{
+		sd.search_len = hex2ss(strSearchStr.CPtr(), search_bytes, ARRAYSIZE(search_bytes));
+		sd.search_bytes = search_bytes;
+		sd.ch_size = 1;
+		Case = 1 + (WholeWords = SearchRegexp = 0);
+		searcher = (ReverseSearch ? &Viewer::search_hex_backward : &Viewer::search_hex_forward);
+	}
+	else
+	{
+		sd.is_utf8 = VM.CodePage == CP_UTF8;
+		sd.ch_size = getCharSize(VM.CodePage);
+		sd.search_text = strSearchStr.CPtr();
+
+		if ( SearchRegexp )
+		{
+			WholeWords = 0;
+			searcher = (ReverseSearch ? &Viewer::search_regex_backward : &Viewer::search_regex_forward);
+			InsertRegexpQuote(strMsgStr);
+			sd.pRex = new RegExp;
+			string strSlash = strSearchStr;
+			InsertRegexpQuote(strSlash);
+			if ( !sd.pRex->Compile(strSlash, OP_PERLSTYLE | OP_OPTIMIZE | (Case ? 0 : OP_IGNORECASE)) )
+				return; // wrong regular expression...
+		}
+		else
+		{
+			searcher = (ReverseSearch ? &Viewer::search_text_backward : &Viewer::search_text_forward);
+			InsertQuote(strMsgStr);
+		}
+	}
+		
+	if (!Case && !SearchRegexp)
+	{
+		strSearchStr.Upper();
+		sd.search_text = strSearchStr.CPtr();
+	}
+
+	int found = 0;
+
+	int search_direction = ReverseSearch ? -1 : +1;
+	switch (Next)
+	{
+		case 2:
+			StartSearchPos = LastSelectPos = (ReverseSearch ? FileSize : 0);
+		break;
+		case +1: case -1:
+			if ( SelectPos >= 0 && LastSelectSize >= 0 )
+			{
+				if (sd.ch_size >= 1)
+					LastSelectPos = SelectPos + (ReverseSearch ? LastSelectSize-sd.ch_size : sd.ch_size);
+				else
+				{
+					INT64 prev_pos = SelectPos;
+					vseek(SelectPos, SEEK_SET);
+					for (;;)
 					{
-						MatchPos = CurPos + I / seek_size;
-						if (t_utf8)
+						wchar_t ch;
+						bool ok_getc = vgetc(&ch);
+						LastSelectPos = vtell();
+						if (!ReverseSearch || !ok_getc)
+							break;
+						if ( LastSelectPos >= SelectPos + LastSelectSize )
 						{
-							MatchPos = CurPos + GetStrBytesNum(t_Buf, I);
-							SearchLength = GetStrBytesNum(t_Buf+I, SearchLength);
+							LastSelectPos = prev_pos;
+							break;
 						}
-						break;
+						prev_pos = LastSelectPos;
 					}
 				}
+				if (search_direction != LastSearchDirection)
+					StartSearchPos = LastSelectPos;
+				else if ( LastSelectPos == StartSearchPos ) // ������ �
+					found = -1;									  // ������������
 
-				if ((ReverseSearch && CurPos <= 0) || (!ReverseSearch && veof()))
-					break;
+				break;
+			} // else pass to case 0 (below)
+		case 0:
+			LastSelectSize = SelectSize = -1;
+			StartSearchPos = LastSelectPos = (ReverseSearch ? EndOfScreen(0) : BegOfScreen());
+		break;
+	}
+	LastSearchDirection = search_direction;
 
-				int asz = buf_size;
-				if (t_utf8)
-					asz = GetStrBytesNum(t_Buf, ReadSize);
+	if (!sd.search_len || (__int64)sd.search_len > FileSize)
+		return;
 
-				if (ReverseSearch)
+	sd.CurPos = LastSelectPos;
+	if ( !found )
+	{
+		TPreRedrawFuncGuard preRedrawFuncGuard(PR_ViewerSearchMsg);
+		SetCursorType(FALSE,0);
+
+		DWORD start_time = GetTickCount();
+		for (;;)
+		{
+			found = (this->*searcher)(&sd);
+			if ( found != 0 )
+				break;
+
+			DWORD cur_time = GetTickCount();
+			if ( cur_time - start_time > RedrawTimeout )
+			{
+				start_time = cur_time;
+
+				if (CheckForEscSilent())
 				{
-					/* $ 01.08.2000 KM
-					   ������� �������� CurPos � ���� Whole words
-					*/
-					if (WholeWords)
-						CurPos -= asz/seek_size - SearchLength/seek_size + 1;
-					else
-						CurPos -= asz/seek_size - SearchLength/seek_size;
+					if (ConfirmAbortOp())
+					{
+						Redraw();
+						return;
+					}
 				}
-				else
+
+				int percent = -1;
+				INT64 done, total = FileSize;
+				if ( total > 0 )
 				{
-					if (WholeWords)
-						CurPos += asz/seek_size - SearchLength/seek_size + 1;
+					if ( !ReverseSearch )
+					{
+						if ( sd.CurPos >= StartSearchPos )
+							done = sd.CurPos - StartSearchPos;
+						else
+							done = (FileSize - StartSearchPos) + sd.CurPos;
+					}
 					else
-						CurPos += asz/seek_size - SearchLength/seek_size;
+					{
+						if ( sd.CurPos <= StartSearchPos )
+							done = StartSearchPos - sd.CurPos;
+						else
+							done = StartSearchPos + (FileSize - sd.CurPos);
+					}
+					percent = static_cast<int>(done*100/total);
 				}
+				ViewerSearchMsg(strMsgStr, percent, SearchHex);
 			}
-
-			delete[] Buf;
 		}
 	}
 
-	if (Match)
+	if ( sd.MatchPos >= 0 )
 	{
-		/* $ 24.01.2003 KM
-		   ! �� �������� ������ �������� �� ���� ������ ��
-		     ���� ������������ ����.
-		*/
-		SelectText(MatchPos,SearchLength,ReverseSearch?0x2:0);
-		// ������� ��������� �� ��������� ����� ������ �� ����.
+		SelectText(sd.MatchPos, sd.search_len, ReverseSearch?0x2:0);
+		LastSelectSize = SelectSize;
+
+		// ������� ��������� �� ��������� �������� ������ �� ����.
 		int FromTop=(ScrY-(Opt.ViOpt.ShowKeyBar?2:1))/4;
 
 		if (FromTop<0 || FromTop>ScrY)
 			FromTop=0;
 
-		for (int i=0; i<FromTop; i++)
-			Up();
+		Up(FromTop);
 
 		AdjustSelPosition = TRUE;
 		Show();
@@ -2598,24 +3263,13 @@
 	}
 	else
 	{
-		//Show();
-		/* $ 27.01.2003 VVM
-		   + ����� ������� ������ ������� � ������� ������ � �����/���� */
-		if (SearchFlags.Check(SEARCH_MODE2))
-		{
-			Message(MSG_WARNING,1,MSG(MViewSearchTitle),
-			        (SearchHex?MSG(MViewSearchCannotFindHex):MSG(MViewSearchCannotFind)),strMsgStr,MSG(MOk));
-		}
-		else
-		{
-			if (!Message(MSG_WARNING,2,MSG(MViewSearchTitle),
-			            (SearchHex?MSG(MViewSearchCannotFindHex):MSG(MViewSearchCannotFind)),strMsgStr,
-			            (ReverseSearch?MSG(MViewSearchFromEnd):MSG(MViewSearchFromBegin)),
-			            MSG(MHYes),MSG(MHNo)))
-			{
-				Search(2,0);
-			}
-		}
+		Message(
+			MSG_WARNING, 1,
+			MSG(MViewSearchTitle),
+			(SearchHex ? MSG(MViewSearchCannotFindHex) : MSG(MViewSearchCannotFind)),
+			strMsgStr,
+			MSG(MOk)
+		);
 	}
 }
 
@@ -2625,37 +3279,31 @@
 	return(VM.Wrap);
 }
 
-
 void Viewer::SetWrapMode(int Wrap)
 {
 	Viewer::VM.Wrap=Wrap;
 }
 
-
 void Viewer::EnableHideCursor(int HideCursor)
 {
 	Viewer::HideCursor=HideCursor;
 }
 
-
 int Viewer::GetWrapType()
 {
 	return(VM.WordWrap);
 }
 
-
 void Viewer::SetWrapType(int TypeWrap)
 {
 	Viewer::VM.WordWrap=TypeWrap;
 }
 
-
 void Viewer::GetFileName(string &strName)
 {
 	strName = strFullFileName;
 }
 
-
 void Viewer::ShowConsoleTitle()
 {
 	string strTitle;
@@ -2663,7 +3311,6 @@
 	ConsoleTitle::SetFarTitle(strTitle);
 }
 
-
 void Viewer::SetTempViewName(const wchar_t *Name, BOOL DeleteFolder)
 {
 	if (Name && *Name)
@@ -2677,7 +3324,6 @@
 	Viewer::DeleteFolder=DeleteFolder;
 }
 
-
 void Viewer::SetTitle(const wchar_t *Title)
 {
 	if (!Title)
@@ -2686,19 +3332,16 @@
 		strTitle = Title;
 }
 
-
 void Viewer::SetFilePos(__int64 Pos)
 {
 	FilePos=Pos;
 };
 
-
 void Viewer::SetPluginData(const wchar_t *PluginData)
 {
 	Viewer::strPluginData = NullToEmpty(PluginData);
 }
 
-
 void Viewer::SetNamesList(NamesList *List)
 {
 	if (List)
@@ -2825,10 +3468,15 @@
 
 	if (IsUnicodeCodePage(VM.CodePage))
 	{
-		Reader.Read(Buf, Count*2, &ReadSize);
+		int rev = (CP_REVERSEBOM == VM.CodePage ? 1 : 0);
+
+		Reader.Read(Buf, Count, &ReadSize);
 		if (0 != (ReadSize & 1))
-			((char *)Buf)[ReadSize++] = '\0';
-
+		{
+			((char *)Buf)[ReadSize-1+rev] = (char)(REPLACE_CHAR & 0xff);
+			((char *)Buf)[ReadSize-0-rev] = (char)(REPLACE_CHAR >> 8);
+			++ReadSize;
+		}
 		if (CP_REVERSEBOM == VM.CodePage)
 		{
 			for (DWORD i=0; i<ReadSize; i+=2)
@@ -2838,7 +3486,6 @@
 				((char *)Buf)[i+1] = t;
 			}
 		}
-
 		ReadSize /= 2;
 	}
 	else
@@ -2871,15 +3518,11 @@
 
 bool Viewer::vseek(__int64 Offset, int Whence)
 {
-	int seek_size = IsUnicodeCodePage(VM.CodePage) ? 2 : 1;
-	Offset *= seek_size;
-
 	if (FILE_CURRENT == Whence)
 	{
 		if (vgetc_ready)
 		{
 			int tail = vgetc_cb - vgetc_ib;
-			tail += (seek_size - 1) * (tail & 1);
 			Offset += tail;
 			vgetc_ready = false;
 		}
@@ -2898,9 +3541,6 @@
 	if (vgetc_ready)
 		Ptr -= (vgetc_cb - vgetc_ib);
 
-	if (IsUnicodeCodePage(VM.CodePage))
-		Ptr=(Ptr+(Ptr&1))/2;
-
 	return Ptr;
 }
 
@@ -2927,9 +3567,6 @@
 		DWORD nr = 0;
 		Reader.Read(vgetc_buffer + vgetc_cb, (DWORD)(ARRAYSIZE(vgetc_buffer)-vgetc_cb), &nr);
 		vgetc_cb += (int)nr;
-
-		if (0 != (vgetc_cb & 1) && IsUnicodeCodePage(VM.CodePage))
-			vgetc_buffer[vgetc_cb++] = 0;
 	}
 
 	vgetc_ready = true;
@@ -2951,11 +3588,17 @@
 	switch (VM.CodePage)
 	{
 		case CP_REVERSEBOM:
-			*pCh = (wchar_t)((vgetc_buffer[vgetc_ib] << 8) | vgetc_buffer[vgetc_ib+1]);
+			if (vgetc_ib == vgetc_cb-1)
+				*pCh = REPLACE_CHAR;
+			else
+				*pCh = (wchar_t)((vgetc_buffer[vgetc_ib] << 8) | vgetc_buffer[vgetc_ib+1]);
 			vgetc_ib += 2;
 		break;
 		case CP_UNICODE:
-			*pCh = (wchar_t)((vgetc_buffer[vgetc_ib+1] << 8) | vgetc_buffer[vgetc_ib]);
+			if (vgetc_ib == vgetc_cb-1)
+				*pCh = REPLACE_CHAR;
+			else
+				*pCh = (wchar_t)((vgetc_buffer[vgetc_ib+1] << 8) | vgetc_buffer[vgetc_ib]);
 			vgetc_ib += 2;
 		break;
 		case CP_UTF8:
@@ -2978,6 +3621,54 @@
 	return true;
 }
 
+wchar_t Viewer::vgetc_prev()
+{
+	INT64 pos = vtell();
+	if ( pos <= 0 )
+		return L'\0';
+
+	int ch_size = getCharSize(VM.CodePage);
+	if ( pos < ch_size )
+		return REPLACE_CHAR;
+
+	int nb = (ch_size >= 1 ? ch_size : (pos > 4 ? 4 : static_cast<int>(pos)));
+	DWORD nr = 0;
+
+	char ss[4];
+	if ( vseek(-nb, SEEK_CUR) )
+		 Reader.Read(ss, static_cast<DWORD>(nb), &nr);
+
+	vseek(pos, SEEK_SET);
+	
+	wchar_t ch = REPLACE_CHAR;
+	if ( static_cast<int>(nr) == nb )
+	{
+		switch ( VM.CodePage )
+		{
+			case CP_REVERSEBOM:
+				ch = MAKEWORD(ss[1], ss[0]);
+			break;
+			case CP_UNICODE:
+				ch = MAKEWORD(ss[0], ss[1]);
+			break;
+			case CP_UTF8:
+			{
+				int tail = 0;
+				wchar_t w[4];
+				int nw = utf8_to_WideChar(ss, nb, w,nullptr, 4,tail);
+				if ( !tail && nw > 0 )
+					ch = w[nw-1];
+				break;
+			}
+			default:
+				MultiByteToWideChar(VM.CodePage, 0, (LPCSTR)ss,1, &ch,1);
+			break;
+		}
+	}
+	return ch;
+}
+
+
 #define RB_PRC 3
 #define RB_HEX 4
 #define RB_DEC 5
@@ -3060,9 +3751,6 @@
 				//  Percent=0;
 				Offset=FileSize/100*Percent;
 
-				if (IsUnicodeCodePage(VM.CodePage))
-					Offset*=2;
-
 				while (ToPercent64(Offset,FileSize)<Percent)
 					Offset++;
 			}
@@ -3094,31 +3782,16 @@
 				//  Percent=0;
 				Offset=FileSize/100*Percent;
 
-				if (IsUnicodeCodePage(VM.CodePage))
-					Offset*=2;
-
 				while (ToPercent64(Offset,FileSize)<Percent)
 					Offset++;
 			}
 		}
 
-		if (Relative)
-		{
-			if (Relative==-1 && Offset>FilePos)   // ���� ���, if (FilePos<0) �� ������� - FilePos � ��� unsigned long
-				FilePos=0;
-			else
-				FilePos=IsUnicodeCodePage(VM.CodePage)? FilePos+Offset*Relative/2 : FilePos+Offset*Relative;
-		}
-		else
-			FilePos=IsUnicodeCodePage(VM.CodePage) ? Offset/2:Offset;
-
-		if (FilePos>FileSize || FilePos<0)     // � ���� ��� �����?
-			FilePos=FileSize;     // ��� ��� ����� ����� ����
+		FilePos = (Relative ? FilePos + Offset*Relative : Offset);
+		FilePos = (FilePos < 0 ? 0 : (FilePos > FileSize ? FileSize : FilePos));
 	}
-	// �������
 	AdjustFilePos();
 
-//  LastSelPos=FilePos;
 	if (!(Flags&VSP_NOREDRAW))
 		Show();
 }
@@ -3129,6 +3802,9 @@
 
 	if (!VM.Hex)
 	{
+		int ch_size = getCharSize(VM.CodePage);
+		FilePos -= FilePos % ch_size;
+
 		vseek(FilePos, SEEK_SET);
 		if (VM.CodePage != CP_UTF8)
 		{
@@ -3140,7 +3816,7 @@
 			if (vgetc_ib < vgetc_cb)
 			{
 				if (0x80 == (vgetc_buffer[vgetc_ib] & 0xC0))
-		{
+				{
 					if (++vgetc_ib < vgetc_cb)
 					{
 						if (0x80 == (vgetc_buffer[vgetc_ib] & 0xC0))
@@ -3152,18 +3828,18 @@
 							}
 						}
 					}
-		}
+				}
 				else
-		{
+				{
 					vgetc(&ch);
 				}
 			}
 		}
 
-		   FilePos = vtell();
-			Up();
-		}
+		FilePos = vtell();
+		Up(1);
 	}
+}
 
 void Viewer::SetFileSize()
 {
@@ -3173,13 +3849,6 @@
 	UINT64 uFileSize=0; // BUGBUG, sign
 	ViewFile.GetSize(uFileSize);
 	FileSize=uFileSize;
-
-	/* $ 20.02.2003 IS
-	   ����� ���������� FilePos � FileSize, FilePos �� ������� ������
-	   �������� � ��� ����, ������ FileSize ���� ���� ������
-	*/
-	if (IsUnicodeCodePage(VM.CodePage))
-		FileSize=(FileSize+(FileSize&1))/2;
 }
 
 
@@ -3190,85 +3859,50 @@
 	Flags=SelectFlags;
 }
 
-/* $ 19.01.2001 SVS
-   �������� - � ������� ������������� ������.
-   Flags=0x01 - �������� (����� Show())
-         0x02 - "������� �����" ?
-*/
-void Viewer::SelectText(const __int64 &MatchPos,const __int64 &SearchLength, const DWORD Flags)
+// Flags=0x01 - �������� [����� Show()]
+//       0x02 - "������� �����" ?
+//
+void Viewer::SelectText(const __int64 &match_pos,const __int64 &search_len, const DWORD flags)
 {
 	if (!ViewFile.Opened())
 		return;
 
-	int buff_size = 1024;
-	wchar_t *Buf = Up_buffer, *tBuf = (!VM.Hex && VM.CodePage == CP_UTF8) ? Buf + buff_size : nullptr;
-	__int64 StartLinePos = -1, SearchLinePos = MatchPos - buff_size;
-	if (SearchLinePos < 0)
-	{
-		buff_size += (int)SearchLinePos;
-		SearchLinePos = 0;
-	}
+	SelectPos = match_pos;
+	SelectSize = search_len;
+	SelectFlags = flags;
+	if ( SelectSize < 0 )
+		return;
 
-	vseek(SearchLinePos, SEEK_SET);
-	int ReadSize = vread(Buf, buff_size, tBuf);
-
-	for (int I=ReadSize-1; I >= 0; I--)
-	if (Buf[I]==L'\n' || Buf[I]==L'\r')
+	if ( VM.Hex )
 	{
-		if (tBuf)
-			StartLinePos = MatchPos - GetStrBytesNum(tBuf+I, ReadSize-I);
-		else
-			StartLinePos = SearchLinePos+I;
-		break;
+		 FilePos = (FilePos & 0xf) | (SelectPos & ~0xf);
+		 FilePos = (FilePos < SelectPos ? FilePos : (FilePos > 16 ? FilePos-16 : 0));
 	}
-
-	vseek(MatchPos+1, SEEK_SET);
-	SelectPos = FilePos=MatchPos;
-	SelectSize = SearchLength;
-	SelectFlags = Flags;
-
-//  LastSelPos=SelectPos+((Flags&0x2) ? -1:1);
-	if (VM.Hex)
-	{
-		FilePos&=~(IsUnicodeCodePage(VM.CodePage) ? 0x7:0xf);
-	}
 	else
 	{
-		if (SelectPos != StartLinePos)
+		FilePos = SelectPos;
+		Up(1);
+		LeftPos = 0;
+
+		if ( !VM.Wrap )
 		{
-			Up();
-			Show();  //update OutStr
-		}
+			vseek(vString.nFilePos = FilePos, SEEK_SET);
+			vString.lpData[0] = L'\0';
+			ReadString(&vString, (int)(SelectPos-FilePos));
 
-		/* $ 13.03.2001 IS
-		   ���� ��������� ����������� � ����� ������ ������ ���������� ����� � ����
-		   ����� � ����� fffe ��� feff, �� �� ����� ���������� �������, ���
-		   ����� ����� ������ �� ������ (��-�� ����, ��� ������ ������ ��
-		   ����������)
-		*/
-		SelectPosOffSet=(IsUnicodeOrUtfCodePage(VM.CodePage) && Signature
-		                 && (MatchPos+SelectSize<=ObjWidth && MatchPos<(__int64)StrLength(Strings[0]->lpData)))?1:0;
-		SelectPos-=SelectPosOffSet;
-		__int64 Length=SelectPos-StartLinePos-1;
+			wchar_t first_found_char = L'\0';
+			vgetc(&first_found_char);
 
-		if (VM.Wrap)
-			Length%=Width+1; //??
-
-		if (Length<=Width)
-			LeftPos=0;
-
-		if (Length-LeftPos>Width || Length<LeftPos)
-		{
-			LeftPos=Length;
-
-			if (LeftPos>(MAX_VIEWLINE-1) || LeftPos<0)
-				LeftPos=0;
-			else if (LeftPos>10)
-				LeftPos-=10;
+			if ( L'\r' != first_found_char && L'\n' != first_found_char )
+			{
+				int found_offset = (int)wcslen(vString.lpData);
+				if ( found_offset > Width-10 )
+					LeftPos = (Width <= 10 ? found_offset : found_offset + 10 - Width);
+			}
 		}
 	}
 
-	if (Flags&1)
+	if (0 != (flags & 1))
 	{
 		AdjustSelPosition = TRUE;
 		Show();
@@ -3276,6 +3910,7 @@
 	}
 }
 
+
 int Viewer::ViewerControl(int Command,void *Param)
 {
 	switch (Command)
@@ -3365,7 +4000,7 @@
 			}
 			else
 			{
-				SelectSize = 0;
+				SelectSize = -1;
 				Show();
 			}
 
@@ -3476,7 +4111,6 @@
 		Show();
 	}
 
-// LastSelPos=FilePos;
 	return oldHex;
 }
 
@@ -3488,7 +4122,7 @@
 	if (VM.Wrap)
 		LeftPos = 0;
 
-	if (!VM.Wrap)
+	if (!VM.Hex)
 		AdjustFilePos();
 
 	if (isRedraw)
@@ -3498,7 +4132,6 @@
 	}
 
 	Opt.ViOpt.ViewerIsWrap=VM.Wrap;
-//  LastSelPos=FilePos;
 	return oldWrap;
 }
 
@@ -3513,6 +4146,9 @@
 		LeftPos = 0;
 	}
 
+	if (!VM.Hex)
+		AdjustFilePos();
+
 	if (isRedraw)
 	{
 		ChangeViewKeyBar();
@@ -3520,6 +4156,5 @@
 	}
 
 	Opt.ViOpt.ViewerWrap=VM.WordWrap;
-//LastSelPos=FilePos;
 	return oldTypeWrap;
 }
Index: config.cpp
===================================================================
--- config.cpp	(revision 6286)
+++ config.cpp	(working copy)
@@ -595,6 +595,9 @@
 	{1, GeneralConfig::TYPE_INTEGER, NKeyViewer,L"PersistentBlocks",&Opt.ViOpt.PersistentBlocks,0, 0},
 	{1, GeneralConfig::TYPE_INTEGER, NKeyViewer,L"AnsiCodePageAsDefault",&Opt.ViOpt.AnsiCodePageAsDefault,1, 0},
 
+	{1, GeneralConfig::TYPE_INTEGER, NKeyViewer,L"MaxLineSize",&Opt.ViOpt.MaxLineSize,2048, 0},
+	{1, GeneralConfig::TYPE_INTEGER, NKeyViewer,L"SearchEditFocus",&Opt.ViOpt.SearchEditFocus,0, 0},
+
 	{1, GeneralConfig::TYPE_INTEGER, NKeyDialog, L"EditHistory",&Opt.Dialogs.EditHistory,1, 0},
 	{1, GeneralConfig::TYPE_INTEGER, NKeyDialog, L"EditBlock",&Opt.Dialogs.EditBlock,0, 0},
 	{1, GeneralConfig::TYPE_INTEGER, NKeyDialog, L"AutoComplete",&Opt.Dialogs.AutoComplete,1, 0},
@@ -920,8 +923,15 @@
 		}
 	}
 
-	Opt.ViOpt.ViewerIsWrap&=1;
-	Opt.ViOpt.ViewerWrap&=1;
+	Opt.ViOpt.ViewerIsWrap &= 1;
+	Opt.ViOpt.ViewerWrap &= 1;
+	if (!Opt.ViOpt.MaxLineSize)
+		Opt.ViOpt.MaxLineSize = 2048;
+	else if (Opt.ViOpt.MaxLineSize < 80)
+		Opt.ViOpt.MaxLineSize = 80;
+	else if (Opt.ViOpt.MaxLineSize > 16*1024)
+		Opt.ViOpt.MaxLineSize = 16*1024;
+	Opt.ViOpt.SearchEditFocus &= 1;
 
 	// ������� �������� �������� ������������ ;-)
 	if (Opt.strWordDiv.IsEmpty())
Index: config.hpp
===================================================================
--- config.hpp	(revision 6286)
+++ config.hpp	(working copy)
@@ -314,6 +314,8 @@
 	int AnsiCodePageAsDefault;
 	int ShowTitleBar;
 	int SearchRegexp;
+	int MaxLineSize; // 80..16384, default=2048
+	int SearchEditFocus; // auto-focus on edit text/hex window
 };
 
 // "�����"
NewViewer2.diff (93,375 bytes)   

2useven10

2011-05-26 14:05

developer   bugnote:0007067

Last edited: 2011-05-26 14:13

+2 небольших испоавления
ooops... одно лишнее

2useven10

2011-05-26 14:13

developer  

NewViewer3.diff (93,452 bytes)   
Index: viewer.hpp
===================================================================
--- viewer.hpp	(revision 6286)
+++ viewer.hpp	(working copy)
@@ -40,22 +40,13 @@
 #include "config.hpp"
 #include "cache.hpp"
 
-/* $ 10.07.2000 tran
-   ! modified MAXSCRY from 120 to 300
-   on win200, with Console height FAR work, but trap on viewer... */
-#define  MAXSCRY     300
+#define MAXSCRY       512                    // 300   // 120
 
-/* $ 12.07.2000 SVS
-  - ��-�� �������� ���� ������ �� 0x800 ������ FAR
-    �� Alt-F7. �������� MAX_VIEWLINE �� 1024 (0x400)
-*/
-#define MAX_VIEWLINE  0x800 // 0x400
-#define MAX_VIEWLINEB 0x80f // 0x40f
+#define MAX_VIEWLINE  (ViOpt.MaxLineSize+ 0) // 0x800 // 0x400
+#define MAX_VIEWLINEB (ViOpt.MaxLineSize+15) // 0x80f // 0x40f
 
 #define VIEWER_UNDO_COUNT   64
 
-enum {VIEW_UNWRAP=0,VIEW_WRAP=1, VIEW_WORDWRAP=2};
-
 class FileViewer;
 class KeyBar;
 
@@ -65,7 +56,7 @@
 	__int64 nFilePos;
 	__int64 nSelStart;
 	__int64 nSelEnd;
-	int  nbytes;
+	int  linesize;
 	bool bSelection;
 	bool have_eol;
 };
@@ -76,12 +67,6 @@
 	__int64 UndoLeft;
 };
 
-enum SEARCH_FLAGS
-{
-	SEARCH_MODE2   = 0x00000001,
-	REVERSE_SEARCH = 0x00000002
-};
-
 enum SHOW_MODES
 {
 	SHOW_RELOAD,
@@ -95,9 +80,6 @@
 		friend class FileViewer;
 
 	private:
-
-		BitFlags SearchFlags;
-
 		struct ViewerOptions ViOpt;
 
 		bool Signature;
@@ -105,7 +87,7 @@
 		NamesList ViewNamesList;
 		KeyBar *ViewKeyBar;
 
-		ViewerString *Strings[MAXSCRY+1];
+		ViewerString **Strings;
 
 		string strFileName;
 		string strFullFileName;
@@ -121,6 +103,8 @@
 
 		string strLastSearchStr;
 		int LastSearchCase,LastSearchWholeWords,LastSearchReverse,LastSearchHex,LastSearchRegexp;
+		int LastSearchDirection;
+		__int64 StartSearchPos;
 
 		struct ViewerMode VM;
 
@@ -128,13 +112,12 @@
 		__int64 SecondPos;
 		__int64 LastScrPos;
 		__int64 FileSize;
-		__int64 LastSelPos;
+		__int64 LastSelectPos, LastSelectSize;
 
 		__int64 LeftPos;
 		__int64 LastPage;
 		__int64 SelectPos,SelectSize;
 		DWORD SelectFlags;
-		__int64 SelectPosOffSet; // ���������� �� �������� ������ ������� � ������� �����
 		int ShowStatusLine,HideCursor;
 
 		string strTitle;
@@ -165,9 +148,20 @@
 		char *vread_buffer;
 		int vread_buffer_size;
 
-		wchar_t *Up_buffer;
-		int Up_buffer_size;
+		__int64  lcache_first;
+		__int64  lcache_last;
+		__int64 *lcache_lines;
+		int      lcache_size;
+		int      lcache_count;
+		int      lcache_base;
+	   bool     lcache_ready;
+		int      lcache_wrap;
+		int      lcache_wwrap;
+		int      lcache_width;
 
+		wchar_t *Search_buffer;
+		int Search_buffer_size;
+
 		ViewerString vString;
 
 		unsigned char vgetc_buffer[32];
@@ -181,8 +175,9 @@
 
 		void ShowPage(int nMode);
 
-		void Up();
-		int WrappedLength(wchar_t *str, int line_size);
+		void Up(int n);
+		void CacheLine(__int64 start, int length, bool have_eol);
+		int CacheFindUp(__int64 start);
 
 		void ShowHex();
 		void ShowStatus();
@@ -195,17 +190,31 @@
 		void AdjustWidth();
 		void AdjustFilePos();
 
-		void ReadString(ViewerString *pString, int MaxSize);
-		int CalcStrSize(const wchar_t *Str,int Length);
+		void ReadString(ViewerString *pString, int MaxSize, bool update_cache=true);
+		__int64 EndOfScreen( int line );
+		__int64 BegOfScreen();
+
 		void ChangeViewKeyBar();
+
 		void Search(int Next,int FirstChar);
-		void ConvertToHex(char *SearchStr,int &SearchLength);
+		//
+		struct search_data;
+		//
+		int search_hex_forward( search_data* sd );
+		int search_hex_backward( search_data* sd );
+		int search_text_forward( search_data* sd );
+		int search_text_backward( search_data* sd );
+		int search_regex_forward( search_data* sd );
+		int search_regex_backward( search_data* sd );
 
+		int read_line(wchar_t *buf, wchar_t *tbuf, INT64 cpos, int adjust, INT64 &lpos, int &lsize);
+
 		int vread(wchar_t *Buf, int Count, wchar_t *Buf2 = nullptr);
 		bool vseek(__int64 Offset, int Whence);
 		__int64 vtell();
 		bool vgetc(wchar_t *ch);
 		bool veof();
+		wchar_t vgetc_prev();
 
 		void SetFileSize();
 		int GetStrBytesNum(const wchar_t *Str, int Length);
@@ -283,5 +292,5 @@
 		int ProcessWrapMode(int newMode, bool isRedraw=TRUE);
 		int ProcessTypeWrapMode(int newMode, bool isRedraw=TRUE);
 
-		void SearchTextTransform(UnicodeString &to, const wchar_t *from, bool hex2text);
+		void SearchTextTransform(UnicodeString &to, const wchar_t *from, bool hex2text, int &pos);
 };
Index: viewer.cpp
===================================================================
--- viewer.cpp	(revision 6286)
+++ viewer.cpp	(working copy)
@@ -65,18 +65,20 @@
 #include "constitle.hpp"
 #include "console.hpp"
 #include "wakeful.hpp"
+#include "RegExp.hpp"
 
 static void PR_ViewerSearchMsg();
-static void ViewerSearchMsg(const wchar_t *Name,int Percent);
+static void ViewerSearchMsg(const wchar_t *name, int percent, int search_hex);
 
-static int InitHex=FALSE,SearchHex=FALSE;
+//static int InitHex=FALSE,SearchHex=FALSE;
 
 static int ViewerID=0;
 
 static int utf8_to_WideChar(const char *s, int nc, wchar_t *w1,wchar_t *w2, int wlen, int &tail);
 
-#define REPLACE_CHAR 0xFFFD  // Replacement
+#define REPLACE_CHAR  0xFFFD // Replacement
 #define CONTINUE_CHAR 0x203A // Single Right-Pointing Angle Quotation Mark
+#define BOM_CHAR      0xFEFF // Zero Length Space
 
 Viewer::Viewer(bool bQuickView, UINT aCodePage):
 	ViOpt(Opt.ViOpt),
@@ -85,10 +87,11 @@
 {
 	_OT(SysLog(L"[%p] Viewer::Viewer()", this));
 
-	for (int i=0; i<=MAXSCRY; i++)
+	Strings = new ViewerString*[MAXSCRY+1];
+	for (int i=0; i <= MAXSCRY; i++)
 	{
 		Strings[i] = new ViewerString();
-		Strings[i]->lpData = new wchar_t[MAX_VIEWLINEB];
+		memset(Strings[i], 0, sizeof(ViewerString));
 	}
 
 	strLastSearchStr = strGlobalSearchString;
@@ -97,19 +100,21 @@
 	LastSearchWholeWords=GlobalSearchWholeWords;
 	LastSearchReverse=GlobalSearchReverse;
 	LastSearchHex=GlobalSearchHex;
+	LastSearchDirection = GlobalSearchReverse ? -1 : +1;
+	StartSearchPos = 0;
 	VM.CodePage=DefCodePage=aCodePage;
 	// �������� ��� �����
 	VM.Wrap=Opt.ViOpt.ViewerIsWrap;
 	VM.WordWrap=Opt.ViOpt.ViewerWrap;
-	VM.Hex=InitHex;
+	VM.Hex = FALSE; //InitHex;
 	ViewKeyBar=nullptr;
 	FilePos=0;
 	LeftPos=0;
 	SecondPos=0;
 	FileSize=0;
 	LastPage=0;
-	SelectPos=SelectSize=0;
-	LastSelPos=0;
+	SelectPos = 0; SelectSize = -1;
+	LastSelectPos = 0; LastSelectSize = -1;
 	SetStatusMode(TRUE);
 	HideCursor=TRUE;
 	DeleteFolder=TRUE;
@@ -123,7 +128,6 @@
 	CtrlObject->Plugins.CurViewer=this;
 	OpenFailed=false;
 	HostFileViewer=nullptr;
-	SelectPosOffSet=0;
 	bVE_READ_Sent = false;
 	Signature = false;
 
@@ -132,8 +136,17 @@
 	vgetc_composite = L'\0';
 
 	vread_buffer = new char[vread_buffer_size = 8192];
-	Up_buffer = new wchar_t[Up_buffer_size = 2 * (3*MAX_VIEWLINE + 2)];
 
+	lcache_first = lcache_last = -1;
+	lcache_lines = new INT64[lcache_size = 8*1000];
+	lcache_count = 0;
+	lcache_base = 0;
+	lcache_ready = false;
+	lcache_wrap = lcache_wwrap = lcache_width = -1;
+	
+	Search_buffer_size = 3 * (MAX_VIEWLINEB < 8000 ? 8000 : MAX_VIEWLINEB);
+	Search_buffer = new wchar_t[Search_buffer_size];
+
 	memset(&vString, 0, sizeof(vString));
 	vString.lpData = new wchar_t[MAX_VIEWLINEB];
 }
@@ -169,7 +182,8 @@
 	}
 
 	delete[] vString.lpData;
-	delete[] Up_buffer;
+	delete[] Search_buffer;
+	delete[] lcache_lines;
 	delete[] vread_buffer;
 
 	_tran(SysLog(L"[%p] Viewer::~Viewer, TempViewName=[%s]",this,TempViewName));
@@ -192,11 +206,13 @@
 		}
 	}
 
-	for (int i=0; i<=MAXSCRY; i++)
+	for (int i=MAXSCRY; i >= 0; --i)
 	{
-		delete [] Strings[i]->lpData;
+		if (Strings[i]->lpData)
+			delete[] Strings[i]->lpData;
 		delete Strings[i];
 	}
+	delete[] Strings;
 
 	if (!OpenFailed && bVE_READ_Sent)
 	{
@@ -216,7 +232,7 @@
 	Opt.ViOpt.ViewerIsWrap=VM.Wrap;
 	Opt.ViOpt.ViewerWrap=VM.WordWrap;
 	Opt.ViOpt.SearchRegexp=LastSearchRegexp;
-	InitHex=VM.Hex;
+	//InitHex=VM.Hex;
 }
 
 
@@ -229,7 +245,7 @@
 	ViewFile.Close();
 	Reader.Clear();
 
-	SelectSize = 0; // ������� ��������
+	SelectSize = -1; // ������� ��������
 	strFileName = Name;
 
 	if (Opt.OnlyEditorViewerUsed && !StrCmp(strFileName, L"-"))
@@ -302,7 +318,7 @@
 		// �������� ������������� ��� ��� ���������� �� �� ������ �������
 		if (CachedCodePage && !IsCodePageSupported(CachedCodePage))
 			CachedCodePage = 0;
-		LastSelPos=FilePos=NewFilePos;
+		LastSelectPos=FilePos=NewFilePos;
 		LeftPos=NewLeftPos;
 	}
 	else
@@ -350,19 +366,13 @@
 			CodePageChangedByUser=TRUE;
 		}
 
-		if (!IsUnicodeOrUtfCodePage(VM.CodePage))
-		{
-			ViewFile.SetPointer(0, nullptr, FILE_BEGIN);
-		}
+		ViewFile.SetPointer(0, nullptr, FILE_BEGIN);
 	}
 	SetFileSize();
 
-	if (FilePos>FileSize)
+	if (FilePos > FileSize)
 		FilePos=0;
 
-//  if (ViOpt.AutoDetectTable && !TableChangedByUser)
-//  {
-//  }
 	ChangeViewKeyBar();
 	AdjustWidth();
 	CtrlObject->Plugins.CurViewer=this; // HostFileViewer;
@@ -379,12 +389,12 @@
 		DriveType = DRIVE_CDROM;
 	switch (DriveType) //??? make it configurable
 	{
-		case DRIVE_REMOVABLE: update_check_period = -1;   break; // flash drive or floppy: never
-		case DRIVE_FIXED:     update_check_period = +1;   break; // hard disk: 1msec
-		case DRIVE_REMOTE:    update_check_period = 1000; break; // network drive: 1sec
-		case DRIVE_CDROM:     update_check_period = -1;   break; // cd/dvd: never
-		case DRIVE_RAMDISK:   update_check_period = +1;   break; // ramdrive: 1msec
-		default:              update_check_period = -1;   break; // unknown: never
+		case DRIVE_REMOVABLE: update_check_period = -1;  break; // flash drive or floppy: never
+		case DRIVE_FIXED:     update_check_period = +1;  break; // hard disk: 1 msec
+		case DRIVE_REMOTE:    update_check_period = 500; break; // network drive: 0.5 sec
+		case DRIVE_CDROM:     update_check_period = -1;  break; // cd/dvd: never
+		case DRIVE_RAMDISK:   update_check_period = +1;  break; // ramdrive: 1 msec
+		default:              update_check_period = -1;  break; // unknown: never
 	}
 
 	return TRUE;
@@ -428,8 +438,9 @@
 		SetCursorType(0,10);
 
 	vseek(FilePos,SEEK_SET);
+	LastPage = 0;
 
-	if (!SelectSize)
+	if ( SelectSize < 0 )
 		SelectPos=FilePos;
 
 	switch (nMode)
@@ -471,7 +482,7 @@
 			}
 			FilePos = Strings[0]->nFilePos;
 			SecondPos = Strings[1]->nFilePos;
-			Strings[Y2-Y1]->nFilePos = Strings[Y2-Y1-1]->nFilePos + Strings[Y2-Y1-1]->nbytes;
+			Strings[Y2-Y1]->nFilePos = Strings[Y2-Y1-1]->nFilePos + Strings[Y2-Y1-1]->linesize;
 			vseek(Strings[Y2-Y1]->nFilePos, SEEK_SET);
 			ReadString(Strings[Y2-Y1],-1);
 			break;
@@ -487,21 +498,14 @@
 
 			if (StrLen > LeftPos)
 			{
-				if (IsUnicodeOrUtfCodePage(VM.CodePage) && Signature && !I && !Strings[I]->nFilePos)
-				{
-					FS<<fmt::LeftAlign()<<fmt::Width(Width)<<fmt::Precision(Width)<<&Strings[I]->lpData[static_cast<size_t>(LeftPos+1)];
-				}
-				else
-				{
-					FS<<fmt::LeftAlign()<<fmt::Width(Width)<<fmt::Precision(Width)<<&Strings[I]->lpData[static_cast<size_t>(LeftPos)];
-				}
+				FS<<fmt::LeftAlign()<<fmt::Width(Width)<<fmt::Precision(Width)<<&Strings[I]->lpData[static_cast<size_t>(LeftPos)];
 			}
 			else
 			{
 				FS<<fmt::Width(Width)<<L"";
 			}
 
-			if (SelectSize && Strings[I]->bSelection)
+			if ( SelectSize >= 0 && Strings[I]->bSelection)
 			{
 				__int64 SelX1;
 
@@ -532,7 +536,7 @@
 					if (LeftPos > Strings[I]->nSelEnd)
 						Length = 0;
 
-					FS<<fmt::Precision(static_cast<int>(Length))<<&Strings[I]->lpData[static_cast<size_t>(SelX1+LeftPos+SelectPosOffSet)];
+					FS<<fmt::Precision(static_cast<int>(Length))<<&Strings[I]->lpData[static_cast<size_t>(SelX1+LeftPos)];
 				}
 			}
 
@@ -571,6 +575,9 @@
 	bool bSelStartFound = false, bSelEndFound = false;
 	__int64 HexLeftPos=((LeftPos>80-ObjWidth) ? Max(80-ObjWidth,0):LeftPos);
 
+	const wchar_t BorderLine[] = {BoxSymbols[BS_V1],L' ',0};
+	int border_len = (int)wcslen(BorderLine);
+
 	for (EndFile=0,Y=Y1; Y<=Y2; Y++)
 	{
 		bSelStartFound = false;
@@ -588,12 +595,9 @@
 		if (Y==Y1+1 && !veof())
 			SecondPos=vtell();
 
-		INT64 Ptr=ViewFile.GetPointer();
-		_snwprintf(OutStr,ARRAYSIZE(OutStr),L"%010I64X: ", Ptr);
+		int out_len = _snwprintf(OutStr,ARRAYSIZE(OutStr),L"%010I64X: ", vtell());
+		SelEnd = SelStart = out_len;
 		TextPos=0;
-		int HexStrStart = (int)wcslen(OutStr);
-		SelStart = HexStrStart;
-		SelEnd = SelStart;
 		__int64 fpos = vtell();
 
 		if (fpos > SelectPos)
@@ -602,103 +606,108 @@
 		if (fpos < SelectPos+SelectSize-1)
 			bSelEndFound = true;
 
-		if (!SelectSize)
+		if ( SelectSize < 0 )
 			bSelStartFound = bSelEndFound = false;
 
-		const wchar_t BorderLine[]={BoxSymbols[BS_V1],L' ',0};
+		unsigned char line[16+3];
+		DWORD nr = 0;
+		DWORD nb = CP_UTF8 == VM.CodePage ? 16+3 : 16;
+		Reader.Read(line, nb, &nr);
+		if (nr > 16)
+			Reader.Unread(nr-16);
 
-		int out_len = (int)wcslen(OutStr);
-		int border_len = (int)wcslen(BorderLine);
+		LastPage = EndFile = (nr <= nb) && veof() ? 1 : 0;
 
-		if (IsUnicodeCodePage(VM.CodePage))
+		if (nr <= 0)
 		{
-			wchar_t line[8];
-			int nr = vread(line, 8);
-			LastPage = EndFile = veof() ? 1 : 0;
-			if (nr <= 0)
+			*OutStr = L'\0';
+		}
+		else
+		{
+			if (IsUnicodeCodePage(VM.CodePage))
 			{
-				*OutStr = L'\0';
-			}
-			else
-			{
-				for (X=0; X<8; X++)
+				int be = VM.CodePage == CP_REVERSEBOM ? 1 : 0;
+				for (X=0; X<16; X += 2)
 				{
-					if (SelectSize>0 && (SelectPos == fpos))
+					if (SelectSize >= 0 && (SelectPos == fpos || SelectPos == fpos+1))
 					{
+						bool half = SelectPos != fpos;
 						bSelStartFound = true;
-						SelStart = (int)wcslen(OutStr);
-						SelSize=SelectSize;
+						SelStart = out_len + (half ? 1+be : 0);
+						if ( 0 == (SelSize=SelectSize) )
+							SelStart += (half ? be : 0) - 1;
 					}
-					if (SelectSize>0 && (fpos == (SelectPos+SelectSize-1)))
+					if (SelectSize >= 0 && (fpos == SelectPos+SelectSize-1 || fpos+1 == SelectPos+SelectSize-1))
 					{
+						bool half = fpos == SelectPos+SelectSize-1;
 						bSelEndFound = true;
-						SelEnd = (int)wcslen(OutStr)+3;
-						SelSize=SelectSize;
+						SelEnd = out_len+3 - (half ? 1+be : 0);
+						if ( 0 == (SelSize=SelectSize) )
+							SelEnd = SelStart;
 					}
+					else if ( SelectSize == 0 && (SelectPos == fpos || SelectPos == fpos+1) )
+					{
+						bSelEndFound = true;
+						SelSize = 0;
+						SelEnd = SelStart;
+					}
 
-					if (X < nr)
+					if ((DWORD)X < nr-1) // full character
 					{
-						unsigned char b1 = HIBYTE(line[X]), b2 = LOBYTE(line[X]);
-#if 0																 // !!! ��������� �������� !!!
-						if (VM.CodePage != CP_REVERSEBOM) // ��� ������ � ���� ����
-						{                                 // ������� �������� ������������� ���
-							unsigned char t = b1; b1 = b2; b2 = t;
-						}
-#endif
-						_snwprintf(OutStr+out_len, ARRAYSIZE(OutStr)-out_len, L"%02X%02X ", b1, b2);
-						TextStr[TextPos++] = line[X] ? line[X] : L' ';
+						unsigned ch = line[X+be] + (line[X+1-be] << 8);
+						_snwprintf(OutStr+out_len, ARRAYSIZE(OutStr)-out_len, L"%04X ", ch);
+						TextStr[TextPos++] = ch ? (wchar_t)ch : L' ';
 					}
-					else
+					else if ((DWORD)X == nr-1) // half character only
 					{
+						int o1 = 2 * (1 - be);
+						_snwprintf(OutStr+out_len+o1, ARRAYSIZE(OutStr)-out_len-o1, L"%02X", line[X]);
+						OutStr[out_len+2-o1] = OutStr[out_len+2-o1+1] = L'x';
+						OutStr[out_len+4] = L' ';
+						TextStr[TextPos++] = REPLACE_CHAR;
+					}
+					else // no character
+					{
 						wcscpy(OutStr+out_len, L"     ");
 						TextStr[TextPos++] = L' ';
 					}
 					out_len += 5;
 
-					if (X == 3)
+					if (X == 3*2)
 					{
 						wcscpy(OutStr+out_len, BorderLine);
 						out_len += border_len;
 					}
-					++fpos;
+					fpos += 2;
 				}
 			}
-		}
-		else
-		{
-			unsigned char line[16+3];
-			DWORD nr = 0;
-			DWORD nb = CP_UTF8 == VM.CodePage ? 16+3 : 16;
-			Reader.Read(line, nb, &nr);
-			if (nr > 16)
-				Reader.Unread(nr-16);
-
-			LastPage = EndFile = (nr <= nb) && veof() ? 1 : 0;
-
-			if (!nr)
-			{
-				*OutStr = L'\0';
-			}
 			else
 			{
-				if (SelectSize)
+				if ( SelectSize >= 0 )
 				{
 					if (SelectPos >= fpos && SelectPos < fpos+16)
 					{
 						int off = (int)(SelectPos - fpos);
 						bSelStartFound = true;
 						SelStart = out_len + 3*off + (off < 8 ? 0 : border_len);
-						SelSize = SelectSize;
+						if ( 0 == (SelSize=SelectSize) )
+							--SelStart;
 					}
 					__int64 selectEnd = SelectPos + SelectSize - 1;
 					if (selectEnd >= fpos && selectEnd < fpos+16)
 					{
 						int off = (int)(selectEnd - fpos);
 						bSelEndFound = true;
-						SelEnd = out_len + 3*off + (off < 8 ? 0 : border_len) + 1;
-						SelSize = SelectSize;
+						SelEnd = (0 == (SelSize=SelectSize) ? SelStart : out_len + 3*off + (off < 8 ? 0 : border_len) + 1);
 					}
+					else if ( SelectSize == 0 && SelectPos == fpos )
+					{
+						bSelEndFound = true;
+						SelSize = 0;
+						SelEnd = SelStart;
+					}
 				}
+
 				for (X=0; X<16; X++)
 				{
 					int off = out_len + 3*X + (X < 8 ? 0 : border_len);
@@ -712,6 +721,7 @@
 					if (CP_UTF8 != VM.CodePage && X < (int)nr && line[X])
 						MultiByteToWideChar(VM.CodePage, 0, (LPCSTR)line+X, 1, TextStr+X, 1);
 				}
+				out_len += 3*16 + border_len;
 
 				if (CP_UTF8 == VM.CodePage)
 				{
@@ -730,7 +740,7 @@
 							first = false;
 							TextStr[ib++] = w1[iw] ? w1[iw] : L' ';
 						}
-						int clen = WideCharToMultiByte(CP_UTF8, 0, w2+iw, 1, nullptr, 0, nullptr, nullptr);
+						int clen = WideCharToMultiByte(CP_UTF8, 0, w2+iw, 1, NULL,0, NULL,NULL);
 						while (--clen > 0 && ib < 16)
 							TextStr[ib++] = CONTINUE_CHAR;
 						++iw;
@@ -740,14 +750,14 @@
 			}
 		}
 
-		TextStr[TextPos]=0;
-		wcscat(TextStr,L" ");
+		TextStr[TextPos] = L' ';
+		TextStr[TextPos+1] = L'\0';
 
-		if ((SelEnd <= SelStart) && bSelStartFound)
-			SelEnd = (int)wcslen(OutStr)-2;
+		if ((SelEnd <= SelStart) && bSelStartFound && bSelEndFound && SelectSize > 0 )
+			SelEnd = out_len-2;
 
-		wcscat(OutStr,L" ");
-		wcscat(OutStr,TextStr);
+		OutStr[out_len] = L' ';
+		wcscpy(OutStr+out_len+1, TextStr);
 
 		if (StrLength(OutStr)>HexLeftPos)
 		{
@@ -789,7 +799,7 @@
 		{
 			UINT64 Total=FileSize/16+((FileSize%16)?1:0);
 			UINT64 Top=FilePos/16+((FilePos%16)?1:0);
-ScrollBarEx(X2+(m_bQuickView?1:0),Y1,Y2-Y1+1,LastPage?Top?Total:0:Top,Total);
+			ScrollBarEx(X2+(m_bQuickView?1:0),Y1,Y2-Y1+1,LastPage?Top?Total:0:Top,Total);
 		}
 	}
 }
@@ -837,6 +847,15 @@
 	return L'\0' == ch || IsSpace(ch);
 }
 
+static bool is_word_div ( const wchar_t ch )
+{
+	static const wchar_t spaces[] = { L' ', L'\t', L'\n', L'\r', BOM_CHAR, REPLACE_CHAR, L'\0' };
+	return ( !ch
+		|| nullptr != wcschr(spaces, ch)
+		|| nullptr != wcschr(Opt.strWordDiv, ch)
+	);
+}
+
 static inline int wrapped_char( const wchar_t ch )
 {
 	static const wchar_t wrapped_chars[] = L",;>)"; // word-wrap enabled after it
@@ -849,18 +868,31 @@
 		return -1;
 }
 
-void Viewer::ReadString(ViewerString *pString, int MaxSize)
+static inline int getCharSize( UINT cp )
 {
+	if ( CP_UTF8 == cp )
+		return -1;
+	else if ( CP_UNICODE == cp || CP_REVERSEBOM == cp )
+		return +2;
+	else
+		return +1;
+}
+
+void Viewer::ReadString( ViewerString *pString, int MaxSize, bool update_cache )
+{
 	AdjustWidth();
 
 	int OutPtr = 0, nTab = 0, wrap_out = -1;
 	wchar_t ch, eol_char = L'\0';
-	__int64 fpos, sel_end, wrap_pos = -1;
+	INT64 fpos=0, fpos1, sel_end, wrap_pos = -1;
 	bool skip_space = false;
 
+	if ( !pString->lpData )
+		pString->lpData = new wchar_t[MAX_VIEWLINEB];
+
 	if (VM.Hex)
 	{
-		vseek(IsUnicodeCodePage(VM.CodePage) ? 8 : 16, FILE_CURRENT);
+		vseek(16, FILE_CURRENT);
 		pString->lpData[OutPtr] = L'\0';
 		LastPage = veof();
 		return;
@@ -869,10 +901,12 @@
 	bool bSelStartFound = false, bSelEndFound = false;
 	pString->bSelection = false;
 	sel_end = SelectPos + SelectSize;
-	fpos = vtell();
 
+	fpos1 = vtell();
 	for (;;)
 	{
+		fpos = fpos1;
+
 		if (OutPtr >= MAX_VIEWLINE)
 			break;
 
@@ -880,27 +914,31 @@
 			ch = L' ';
 		else
 		{
-			fpos = vtell();
 			if (!MaxSize-- || !vgetc(&ch))
 				break;
 		}
+		fpos1 = vtell();
 
-		if (SelectSize > 0)
+		if (SelectSize >= 0)
 		{
-			if (fpos == SelectPos)
+			if (fpos == SelectPos || (fpos < SelectPos && fpos1 > SelectPos))
 			{
 				pString->nSelStart = OutPtr;
 				bSelStartFound = true;
 			}
-			if (fpos == sel_end)
+			if (fpos == sel_end || (fpos < sel_end && fpos1 > sel_end))
 			{
-				pString->nSelEnd = OutPtr;
+				pString->nSelEnd = OutPtr + (fpos < sel_end ? 1 : 0);
 				bSelEndFound = true;
 			}
 		}
 
-		if (L'\t' == ch)
+		if (!fpos && BOM_CHAR == ch)
 		{
+			continue; // skip BOM
+		}
+		else if (L'\t' == ch)
+		{
 			nTab = ViOpt.TabSize - (OutPtr % ViOpt.TabSize);
 			continue;
 		}
@@ -913,7 +951,7 @@
 		pString->lpData[OutPtr++] = ch ? ch : L' ';
 		if (!VM.Wrap)
 			continue;
-
+	
 		if (OutPtr >= Width)
 		{
 			if (VM.WordWrap && wrap_out > 0)
@@ -965,9 +1003,12 @@
 
 	pString->have_eol = (eol_char != L'\0');
 	pString->lpData[(int)OutPtr]=0;
-	pString->nbytes = (int)(vtell() - pString->nFilePos);
+	pString->linesize = (int)(vtell() - pString->nFilePos);
 
-	if (SelectSize > 0 && OutPtr > 0)
+	if ( update_cache )
+		CacheLine(pString->nFilePos, pString->linesize, pString->have_eol);
+
+	if (SelectSize >= 0 && OutPtr > 0)
 	{
 		if (!bSelStartFound && pString->nFilePos >= SelectPos && pString->nFilePos <= sel_end)
 		{
@@ -987,8 +1028,86 @@
 
 	if (!eol_char && veof())
 		LastPage = 1;
+}	
+
+
+__int64 Viewer::EndOfScreen( int line )
+{
+	__int64 pos;
+
+	if (!VM.Hex)
+	{
+		pos = Strings[Y2-Y1+line]->nFilePos + Strings[Y2-Y1+line]->linesize;
+		if ( !line && !VM.Wrap && Strings[Y2-Y1]->linesize > 0 )
+		{
+			vseek(Strings[Y1-Y2]->nFilePos, SEEK_SET);
+			int col = 0, rmargin = (int)LeftPos + Width;
+			wchar_t ch;
+			for (;;)
+			{
+				if ( !vgetc(&ch) )
+					break;
+				if ( ch == L'\n' || ch == L'\r' )
+					break;
+				if ( ch == L'\t' )
+					col += ViOpt.TabSize - (col % ViOpt.TabSize);
+				else
+					++col;
+				if ( col >= rmargin )
+				{
+					pos = vtell();
+					break;
+				}
+			}
+		}
+	}
+	else
+		pos =	FilePos + 16 * (Y2-Y1+1+line);
+		
+   if (pos < 0)
+		pos = 0;
+	else if (pos > FileSize)
+		pos = FileSize;
+
+	return pos;
 }
 
+
+__int64 Viewer::BegOfScreen()
+{
+	__int64 pos = FilePos, prev_pos;
+
+	if (!VM.Hex && !VM.Wrap && LeftPos > 0)
+	{
+		vseek(FilePos, SEEK_SET);
+		int col = 0;
+		wchar_t ch;
+		pos = -1;
+		for (;;)
+		{
+			prev_pos = vtell();
+			if ( !vgetc(&ch) )
+				break;
+			if ( ch == L'\n' || ch == L'\r' )
+			{
+				pos = Strings[1]->nFilePos;
+				break;
+			}
+			if ( ch == L'\t' )
+				col += ViOpt.TabSize - (col % ViOpt.TabSize);
+			else
+				++col;
+			if ( col > LeftPos )	//!! ������ �������� ����� ������
+				break;				//!! ��� LeftPos=1 �� ���� 2 �������
+		}
+		if ( pos < 0 )
+			pos = (col > LeftPos ? prev_pos : vtell());
+	}
+
+	return pos;
+}
+
+
 __int64 Viewer::VMProcess(int OpCode,void *vParam,__int64 iParam)
 {
 	switch (OpCode)
@@ -996,7 +1115,7 @@
 		case MCODE_C_EMPTY:
 			return (__int64)!FileSize;
 		case MCODE_C_SELECTED:
-			return (__int64)(SelectSize?TRUE:FALSE);
+			return (__int64)(SelectSize >= 0 ?TRUE:FALSE);
 		case MCODE_C_EOF:
 			return (__int64)(LastPage || !ViewFile.Opened());
 		case MCODE_C_BOF:
@@ -1030,7 +1149,7 @@
 	*/
 	if (!ViOpt.PersistentBlocks &&
 	        Key!=KEY_IDLE && Key!=KEY_NONE && !(Key==KEY_CTRLINS||Key==KEY_CTRLNUMPAD0) && Key!=KEY_CTRLC)
-		SelectSize=0;
+		SelectSize = -1;
 
 	if (!InternalKey && !LastKeyUndo && (FilePos!=UndoData[0].UndoAddr || LeftPos!=UndoData[0].UndoLeft))
 	{
@@ -1055,7 +1174,6 @@
 		{
 			FilePos=BMSavePos.FilePos[Pos];
 			LeftPos=BMSavePos.LeftPos[Pos];
-//      LastSelPos=FilePos;
 			Show();
 		}
 
@@ -1082,25 +1200,20 @@
 		}
 		case KEY_CTRLU:
 		{
-//      if (SelectSize)
-			{
-				SelectSize = 0;
-				Show();
-			}
+			SelectSize = -1;
+			Show();
 			return TRUE;
 		}
 		case KEY_CTRLC:
 		case KEY_CTRLINS:  case KEY_CTRLNUMPAD0:
 		{
-			if (SelectSize && ViewFile.Opened())
+			if (SelectSize >= 0 && ViewFile.Opened())
 			{
-				wchar_t *SelData;
-				size_t DataSize = (size_t)SelectSize+(IsUnicodeCodePage(VM.CodePage)?sizeof(wchar_t):1);
-				__int64 CurFilePos=vtell();
-
-				if ((SelData=(wchar_t*)xf_malloc(DataSize*sizeof(wchar_t))) )
+				wchar_t *SelData = (wchar_t*)xf_malloc((size_t)SelectSize+1);
+				if ( SelData )
 				{
-					wmemset(SelData, 0, DataSize);
+					__int64 CurFilePos=vtell();
+					wmemset(SelData, 0, (size_t)SelectSize+1);
 					vseek(SelectPos,SEEK_SET);
 					vread(SelData, (int)SelectSize);
 					CopyToClipboard(SelData);
@@ -1108,7 +1221,6 @@
 					vseek(CurFilePos,SEEK_SET);
 				}
 			}
-
 			return TRUE;
 		}
 		//   �����/����� ��������
@@ -1145,6 +1257,8 @@
 
 					Reader.Clear(); // ���� ���� �� ��� ����?
 					ViewFile.FlushBuffers();
+					vseek(0, SEEK_CUR); // reset vgetc state
+					lcache_ready = false; // reset start-lines cache
 
 					if (FilePos>FileSize)
 					{
@@ -1251,19 +1365,16 @@
 		}
 		case KEY_SHIFTF2:
 		{
-			LastPage = 0;
 			ProcessTypeWrapMode(!VM.WordWrap);
 			return TRUE;
 		}
 		case KEY_F2:
 		{
-			LastPage = 0;
 			ProcessWrapMode(!VM.Wrap);
 			return TRUE;
 		}
 		case KEY_F4:
 		{
-			LastPage = 0;
 			ProcessHexMode(!VM.Hex);
 			return TRUE;
 		}
@@ -1280,23 +1391,13 @@
 		}
 		case KEY_ALTF7:
 		{
-			SearchFlags.Set(REVERSE_SEARCH);
-			Search(1,0);
-			SearchFlags.Clear(REVERSE_SEARCH);
+			Search(-1,0);
 			return TRUE;
 		}
 		case KEY_F8:
 		{
-			LastPage = 0;
-			if (IsUnicodeCodePage(VM.CodePage))
-			{
-				FilePos*=2;
-				SetFileSize();
-				SelectPos = 0;
-				SelectSize = 0;
-			}
-
 			VM.CodePage = VM.CodePage==GetOEMCP() ? GetACP() : GetOEMCP();
+			lcache_ready = false;
 			ChangeViewKeyBar();
 			Show();
 			CodePageChangedByUser=TRUE;
@@ -1304,12 +1405,12 @@
 		}
 		case KEY_SHIFTF8:
 		{
-			LastPage = 0;
 			UINT nCodePage = SelectCodePage(VM.CodePage, true, true, false, true);
+			lcache_ready = false;
 
-			if (nCodePage!=(UINT)-1)
+			if (nCodePage != static_cast<UINT>(-1))
 			{
-				if (nCodePage == (WORD)(CP_AUTODETECT & 0xffff))
+				if (nCodePage == (CP_AUTODETECT & 0xffff))
 				{
 					__int64 fpos = vtell();
 					bool detect = GetFileFormat(ViewFile,nCodePage,&Signature,true) && IsCodePageSupported(nCodePage);
@@ -1318,20 +1419,6 @@
 						nCodePage = Opt.ViOpt.AnsiCodePageAsDefault ? GetACP() : GetOEMCP();
 				}
 				CodePageChangedByUser=TRUE;
-
-				if (IsUnicodeCodePage(VM.CodePage) && !IsUnicodeCodePage(nCodePage))
-				{
-					FilePos*=2;
-					SelectPos = 0;
-					SelectSize = 0;
-				}
-				else if (!IsUnicodeCodePage(VM.CodePage) && IsUnicodeCodePage(nCodePage))
-				{
-					FilePos=(FilePos+(FilePos&1))/2; //????
-					SelectPos = 0;
-					SelectSize = 0;
-				}
-
 				VM.CodePage=nCodePage;
 				SetFileSize();
 				ChangeViewKeyBar();
@@ -1402,11 +1489,10 @@
 		{
 			if (FilePos>0 && ViewFile.Opened())
 			{
-				Up();	// LastPage = 0
+				Up(1);	// LastPage = 0
 
 				if (VM.Hex)
 				{
-					//FilePos&=~(IsUnicodeCodePage(VM.CodePage) ? 0x7:0xf);
 					Show();
 				}
 				else
@@ -1414,7 +1500,7 @@
 					ShowPage(SHOW_UP);
 					ViewerString *end = Strings[Y2-Y1];
 					LastPage = end->nFilePos >= FileSize ||
-					   (!end->have_eol && end->nFilePos + end->nbytes >= FileSize);
+						(!end->have_eol && end->nFilePos + end->linesize >= FileSize);
 				}
 			}
 
@@ -1439,9 +1525,7 @@
 		{
 			if (ViewFile.Opened())
 			{
-				for (int i=Y1; i<Y2; i++)
-					Up();
-
+				Up(Y2-Y1);
 				Show();
 			}
 
@@ -1452,25 +1536,25 @@
 			if (LastPage || !ViewFile.Opened())
 				return TRUE;
 
-			if (VM.Hex)
-				FilePos += (IsUnicodeCodePage(VM.CodePage) ? 8 : 16) * (Y2-Y1);
-			else
-				FilePos = Strings[Y2-Y1]->nFilePos;
+			FilePos = EndOfScreen(-1); // start of last screen line
 
 			if (Key == KEY_CTRLDOWN)
 			{
-				vseek(FilePos, SEEK_SET);
+				vseek(vString.nFilePos = FilePos, SEEK_SET);
 				for (int i=Y1; i<=Y2; i++)
-				ReadString(&vString,-1);
+				{
+					ReadString(&vString,-1);
+					vString.nFilePos += vString.linesize;
+				}
 
 				if (LastPage)
-			{
-				InternalKey++;
-				ProcessKey(KEY_CTRLPGDN);
-				InternalKey--;
-				return TRUE;
+				{
+					InternalKey++;
+					ProcessKey(KEY_CTRLPGDN);
+					InternalKey--;
+					return TRUE;
+				}
 			}
-			}
 
 			Show();
 			return TRUE;
@@ -1504,17 +1588,11 @@
 			{
 				if (VM.Hex)
 				{
-					FilePos--;
-
-					if (FilePos<0)
-						FilePos=0;
+					FilePos = FilePos > 0 ? FilePos-1 : 0;
 				}
 				else
 				{
-					LeftPos-=20;
-
-					if (LeftPos<0)
-						LeftPos=0;
+					LeftPos = LeftPos > 20 ? LeftPos-20 : 0;
 				}
 
 				Show();
@@ -1528,10 +1606,7 @@
 			{
 				if (VM.Hex)
 				{
-					FilePos++;
-
-					if (FilePos >= FileSize)
-						FilePos=FileSize-1; //??
+					FilePos = FilePos < FileSize-1 ? FilePos+1 : FileSize-1;
 				}
 				else if (!VM.Wrap)
 				{
@@ -1592,7 +1667,6 @@
 			if (ViewFile.Opened())
 			{
 				FilePos=0;
-				LastPage = 0;
 				Show();
 			}
 
@@ -1614,32 +1688,33 @@
 				   ��������� End (� ������) �� ����� ������ �������� �� ���������
 				   Down.
 				*/
-				unsigned int max_counter=Y2-Y1;
+				int max_counter = Y2 - Y1;
 
 				if (VM.Hex)
 				{
 					vseek(0,SEEK_END);
 					FilePos = vtell();
-					int line_chars = IsUnicodeCodePage(VM.CodePage) ? 8 : 16;
-					if ((FilePos % line_chars) == 0)
-						FilePos -= line_chars * (Y2 - Y1 + 1);
+					if ((FilePos % 16) == 0)
+						FilePos -= 16 * (Y2 - Y1 + 1);
 					else
-						FilePos -= (FilePos % line_chars) + line_chars * (Y2 - Y1);
+						FilePos -= (FilePos % 16) + 16 * (Y2 - Y1);
 					if (FilePos < 0)
 						FilePos = 0;
 				}
 				else
 				{
-					vseek(-1,SEEK_END);
-					WCHAR LastSym=0;
+					int ch_size = getCharSize(VM.CodePage);
+					wchar_t LastSym = L'\0';
 
-					if (vgetc(&LastSym) && LastSym!=L'\n' && LastSym!=L'\r')
+					vseek(0, SEEK_END);
+					FilePos = vtell() - 1;
+					FilePos -= FilePos % ch_size;
+					vseek(FilePos, SEEK_SET);
+					if (vgetc(&LastSym) && LastSym != L'\n' && LastSym != L'\r')
 						++max_counter;
 
 					FilePos=vtell();
-
-					for (int i=0; static_cast<unsigned int>(i)<max_counter; i++)
-						Up();
+					Up(max_counter);
 				}
 
 				Show();
@@ -1688,7 +1763,7 @@
 		{
 			while (IsMouseButtonPressed())
 			{
-//        _SVS(SysLog(L"Viewer/ KEY_DOWN= %i, %i",FilePos,FileSize));
+				//_SVS(SysLog(L"Viewer/ KEY_DOWN= %i, %i",FilePos,FileSize));
 				ProcessKey(KEY_DOWN);
 			}
 		}
@@ -1719,7 +1794,7 @@
 				else
 					Perc=ToPercent64(FilePos,FileSize);
 
-//_SVS(SysLog(L"Viewer/ ToPercent()=%i, %I64d, %I64d, Mouse=[%d:%d]",Perc,FilePos,FileSize,MsX,MsY));
+				//_SVS(SysLog(L"Viewer/ ToPercent()=%i, %I64d, %I64d, Mouse=[%d:%d]",Perc,FilePos,FileSize,MsX,MsY));
 				if (Perc == 100)
 					ProcessKey(KEY_CTRLEND);
 				else if (!Perc)
@@ -1796,140 +1871,270 @@
 	return TRUE;
 }
 
-void Viewer::Up()
+
+void Viewer::CacheLine( __int64 start, int length, bool have_eol )
 {
-	if (!ViewFile.Opened())
+	assert(start >= 0 && length >= 0);
+	if (!length) // empty lines beyond EOF
 		return;
 
-	LastPage = 0;
+	if ( lcache_ready
+	 && (lcache_wrap != VM.Wrap || lcache_wwrap != VM.WordWrap || lcache_width != Width)
+	){
+		lcache_ready = false;
+	}
 
-	if (FilePos <= 0)
-		return;
-
-	if (VM.Hex)
+	if (!lcache_ready || start > lcache_last || start+length < lcache_first)
 	{
-		FilePos -= IsUnicodeCodePage(VM.CodePage) ? 8 : 16;
-		if (FilePos < 0)
-			FilePos = 0;
-		return;
-	}
+		lcache_first = start;
+		lcache_last = start + length;
 
-	int Skipped = 0, I = -1, BufSize = MAX_VIEWLINE + 2;
-	wchar_t *Buf = Up_buffer, *tBuf = Up_buffer, *tBuf2 = nullptr;
+		lcache_count = 2;
+		lcache_base = 0;
+		lcache_lines[0] = (have_eol ? -start : +start);
+		lcache_lines[1] = start + length;
 
-	if (!VM.Hex && VM.CodePage == CP_UTF8)
-	{
-		BufSize = Up_buffer_size / 2; // = 3*MAXLINE + 2;
-		tBuf = tBuf2 = Up_buffer + BufSize;
+		lcache_wrap = VM.Wrap; lcache_wwrap = VM.WordWrap; lcache_width = Width;
+		lcache_ready = true;
 	}
-
-	if (FilePos < (__int64)BufSize)
-		BufSize = (int)FilePos;
-
-	if (BufSize >= 512) // first try to find newline in first 256 bytes
+	else if (start == lcache_last)
 	{
-		vseek(FilePos-256, SEEK_SET);
-		int nr = vread(Buf, 256, tBuf2);
-
-		if (nr > 0 && Buf[nr-1] == L'\n')
+		int i = (lcache_base + lcache_count - 1) % lcache_size;
+		lcache_lines[i] = (have_eol ? -start : +start);
+		i = (i + 1) % lcache_size;
+		lcache_lines[i]	= lcache_last = start + length;
+		if (lcache_count < lcache_size)
+			++lcache_count;
+		else
 		{
-			--nr; ++Skipped;
+			lcache_base = (lcache_base + 1) % lcache_size; // ++start
+			lcache_first = _abs64(lcache_lines[lcache_base]);
 		}
-		if (nr > 0 && Buf[nr-1] == L'\r')
+	}
+	else if (start+length == lcache_first)
+	{
+	   lcache_base = (lcache_base + lcache_size - 1) % lcache_size; // --start
+      lcache_lines[lcache_base] = (have_eol ? -start : +start);
+		lcache_first = start;
+		if (lcache_count < lcache_size)
+			++lcache_count;
+		else
 		{
-			--nr; ++Skipped;
+			int i = (lcache_base + lcache_size - 1) % lcache_size; // i = start - 1
+			lcache_last = _abs64(lcache_lines[i]);
 		}
+	}
+	else
+	{
+#if defined(_DEBUG) && 1 // it is legal case if file changed...
+		assert(start >= lcache_first && start+length <= lcache_last);
+		int i = CacheFindUp(start+length);
+		assert(i >= 0 && _abs64(lcache_lines[i]) == start);
+#endif
+		lcache_first = start;
+		lcache_last = start + length;
 
-		for (I = nr-1; I >= 0; I--)
-		{
-			if (Buf[I] == L'\n' || Buf[I] == L'\r')
-			{
-				BufSize = nr;
-				break;
-			}
-		}
+		lcache_count = 2;
+		lcache_base = 0;
+		lcache_lines[0] = (have_eol ? -start : +start);
+		lcache_lines[1] = start + length;
 	}
+}
 
-	if (I < 0) // full portion read
+int Viewer::CacheFindUp( __int64 start )
+{
+	if ( lcache_ready
+		&& (lcache_wrap != VM.Wrap || lcache_wwrap != VM.WordWrap || lcache_width != Width)
+	){
+		lcache_ready = false;
+	}
+	if ( !lcache_ready || start <= lcache_first || start > lcache_last )
+		return -1;
+
+	int i, j, i1 = 0, i2 = lcache_count - 1;
+   for (;;)
 	{
-		vseek(FilePos - BufSize, SEEK_SET);
-		BufSize = vread(Buf, BufSize, tBuf2);
-		Skipped=0;
+		if ( i1+1 >= i2 )
+			return (lcache_base + i1) % lcache_size;
 
-		if (BufSize > 0 && Buf[BufSize-1]==L'\n')
-		{
-			--BufSize; ++Skipped;
-		}
-		if (BufSize > 0 && Buf[BufSize-1] == L'\r')
-		{
-			--BufSize; ++Skipped;
-		}
-
-		int I0 = BufSize > MAX_VIEWLINE ? BufSize - MAX_VIEWLINE : 0;
-		for (I = BufSize-1; I >= I0; I--)
-		{
-			if (Buf[I] == L'\n' || Buf[I] == L'\r')
-				break;
-		}
+		i = (i1 + i2) / 2;
+		j = (lcache_base + i) % lcache_size;
+		if (_abs64(lcache_lines[j]) < start)
+			i1 = i;
+		else
+			i2 = i;
 	}
+}
 
-	if (!VM.Wrap)
+void Viewer::Up( int nlines )
+{
+	assert( nlines > 0 );
+
+	if (!ViewFile.Opened())
+		return;
+
+   LastPage = 0;
+
+	if (FilePos <= 0)
+		return;
+
+	if (VM.Hex)
 	{
-		FilePos -= GetStrBytesNum(tBuf + (I+1), BufSize-(I+1)) + Skipped;
+		FilePos = FilePos > 16*nlines ? FilePos-16*nlines : 0;
+		return;
 	}
-	else
+
+	__int64 fpos, fpos1;
+
+	int i = CacheFindUp(fpos = FilePos);
+	if ( i >= 0 )
 	{
-		wchar_t *line = tBuf + I+1;
-		int len, line_size = BufSize - (I+1);
 		for (;;)
 		{
-			len = WrappedLength(line, line_size);
-			if ( len >= line_size )
+			fpos = _abs64(lcache_lines[i]);
+			if (--nlines == 0)
+			{
+				FilePos = fpos;
+				return;
+			}
+			if (i == lcache_base)
 				break;
-			line += len;
-			line_size -= len;
+			i = (i + lcache_size - 1) % lcache_size;
 		}
-		FilePos -= GetStrBytesNum(line, line_size + Skipped);
 	}
-	return;
-}
 
-int Viewer::WrappedLength(wchar_t *str, int line_size)
-{
-	int width = 0, off = 0, wrap_pos = -1, wrap_ch, tab_size = ViOpt.TabSize;
-	bool wwrap = VM.Wrap && VM.WordWrap;
+	static const int portion_size = 256;
+	static const int max_backward = 32*1024;
 
-	for (;;)
+	union {
+		char c1[portion_size];
+		wchar_t c2[portion_size/(int)sizeof(wchar_t)];
+	} buff;
+
+	int j, buff_size, nr, ch_size = getCharSize(VM.CodePage);
+	int len[max_backward/40]; // suppose minimum line with is 80 chars...
+
+	while ( nlines > 0 )
 	{
-		if (off >= line_size)
-			return off;
+		if ( fpos <= 0 )
+		{
+			FilePos = 0;
+			return;
+		}
 
-		wchar_t ch = str[off++];
-		width += L'\t' != ch ? 1 : tab_size - width % tab_size;
+		fpos1 = fpos;
 
-		if (width >= Width)
+		// backward CR-LF search
+		//
+		for ( j = 0; j < max_backward/portion_size; ++j )
 		{
-			if (!wwrap || wrap_pos <= 0)
-				return off;
-			while (wrap_pos < line_size && is_space_or_nul(str[wrap_pos]))
-				++wrap_pos;
-			return wrap_pos;
+			buff_size = (fpos > (__int64)portion_size ? portion_size : (int)fpos);
+			fpos -= buff_size;
+			vseek(fpos, SEEK_SET);
+
+			if ( ch_size <= 1 )
+			{
+				DWORD nread = 0;
+				Reader.Read(buff.c1, static_cast<DWORD>(buff_size), &nread);
+				if ((nr = (int)nread) != buff_size)
+				{
+					return; //??? error handling
+				}
+				if ( 0 == j )
+				{
+					if ( nr > 0 && '\n' == buff.c1[nr-1] )
+					{
+						--nr;
+					}
+					if ( nr > 0 && '\r' == buff.c1[nr-1] )
+					{
+						--nr;
+					}
+				}
+				for ( i = nr-1; i >= 0; --i )
+				{
+					if ( '\n' == buff.c1[i] || '\r' == buff.c1[i] )
+						break;
+				}
+				if ( i >= 0 )
+				{
+					fpos += i + 1;
+					break;
+				}
+			}
+			else
+			{
+				nr = vread(buff.c2, buff_size);
+				if ( nr != buff_size / ch_size )
+				{
+					return; //??? error handling
+				}
+				if ( 0 == j )
+				{
+					if ( nr > 0 && L'\n' == buff.c2[nr-1] )
+					{
+						--nr;
+					}
+					if ( nr > 0 && L'\r' == buff.c2[nr-1] )
+					{
+						--nr;
+					}
+				}
+				for ( i = nr-1; i >= 0; --i )
+				{
+					if ( L'\n' == buff.c2[i] || L'\r' == buff.c2[i] )
+						break;
+				}
+				if ( i >= 0 )
+				{
+					fpos += ch_size * (i + 1);
+					break;
+				}
+			}
 		}
 
-		if (!wwrap)
-			continue;
+		// split read portion 
+		//
+		vseek(vString.nFilePos = fpos, SEEK_SET);
+		for (i = 0; i < (int)ARRAYSIZE(len); ++i)
+		{
+			ReadString(&vString, -1, false);
+			len[i] = (vString.have_eol ? -1 : +1) * vString.linesize;
+			if ((vString.nFilePos += vString.linesize) >= fpos1)
+			{
+				fpos1 = vString.nFilePos;
+				break;
+			}
+		}
+		assert(i < (int)ARRAYSIZE(len));
+		if (i >= (int)ARRAYSIZE(len))
+			--i;
 
-		if ((wrap_ch = wrapped_char(ch)) >= 0)
-			wrap_pos = off - 1 + wrap_ch;
+		while ( i >= 0 )
+		{
+			int l = len[i--];
+			bool eol = false;
+			if (l < 0)
+			{
+				eol = true;
+				l = -l;
+			}
+			fpos1 -= l;
+			CacheLine(fpos1, l, eol);
+			if (--nlines == 0)
+				FilePos = fpos1;
+		}
 	}
 }
 
-int Viewer::GetStrBytesNum(const wchar_t *Str, int Length)
+
+int Viewer::GetStrBytesNum( const wchar_t *Str, int Length )
 {
-	if (IsUnicodeCodePage(VM.CodePage))
-		return Length;
+	int ch_size = getCharSize(VM.CodePage);
+	if (ch_size > 0)
+		return Length * ch_size;
 	else
-		return WideCharToMultiByte(VM.CodePage, 0, Str, Length, nullptr, 0, nullptr, nullptr);
+		return WideCharToMultiByte(VM.CodePage,0, Str,Length, nullptr,0, nullptr,nullptr);
 }
 
 void Viewer::SetViewKeyBar(KeyBar *ViewKeyBar)
@@ -1938,7 +2143,6 @@
 	ChangeViewKeyBar();
 }
 
-
 void Viewer::ChangeViewKeyBar()
 {
 	if (ViewKeyBar)
@@ -1971,6 +2175,7 @@
 	CtrlObject->Plugins.CurViewer=this; //HostFileViewer;
 }
 
+
 enum SEARCHDLG
 {
 	SD_DOUBLEBOX,
@@ -1991,9 +2196,18 @@
 
 enum
 {
- DM_SDSETVISIBILITY = DM_USER+1,
+	DM_SDSETVISIBILITY = DM_USER + 1,
+	DM_SDREXVISIBILITY = DM_USER + 2
 };
 
+struct MyDialogData
+{
+   Viewer      *viewer;
+	bool edit_autofocus;
+   bool       hex_mode;
+	bool      recursive;
+};
+
 INT_PTR WINAPI ViewerSearchDlgProc(HANDLE hDlg,int Msg,int Param1,void* Param2)
 {
 	switch (Msg)
@@ -2010,42 +2224,133 @@
 			SendDlgMessage(hDlg,DM_SHOWITEM,SD_EDIT_TEXT,ToPtr(!Param1));
 			SendDlgMessage(hDlg,DM_SHOWITEM,SD_EDIT_HEX,ToPtr(Param1));
 			SendDlgMessage(hDlg,DM_ENABLE,SD_CHECKBOX_CASE,ToPtr(!Param1));
-			SendDlgMessage(hDlg,DM_ENABLE,SD_CHECKBOX_WORDS,ToPtr(!Param1));
-			//SendDlgMessage(hDlg,DM_ENABLE,SD_CHECKBOX_REGEXP,!Param1);
+			int re = SendDlgMessage(hDlg, DM_GETCHECK,SD_CHECKBOX_REGEXP,0) == BSTATE_CHECKED;
+			int ww = !Param1 && !re;
+			SendDlgMessage(hDlg,DM_ENABLE,SD_CHECKBOX_WORDS,ToPtr(ww));
+			SendDlgMessage(hDlg,DM_ENABLE,SD_CHECKBOX_REGEXP,ToPtr(!Param1));
+			SendDlgMessage(hDlg, DM_SDREXVISIBILITY, re && !Param1, 0);
 			return TRUE;
 		}
+		case DM_SDREXVISIBILITY:
+		{
+		   int show = 1;
+			if ( Param1 )
+			{
+				int tlen = (int)SendDlgMessage(hDlg, DM_GETTEXTPTR, SD_EDIT_TEXT, 0);
+				show = 0;
+				if ( tlen > 0 )
+				{
+					RegExp re;
+					wchar_t t[128], *tmp = t;
+					if (tlen > (int)ARRAYSIZE(t))
+						tmp = new wchar_t[tlen];
+					SendDlgMessage(hDlg, DM_GETTEXTPTR, SD_EDIT_TEXT, tmp);
+					string sre = tmp;
+					InsertRegexpQuote(sre);
+					show = re.Compile(sre.CPtr(), OP_PERLSTYLE);
+					if (tmp != t)
+						delete[] tmp;;
+				}
+			}
+			SendDlgMessage(hDlg, DM_ENABLE, SD_TEXT_SEARCH, ToPtr(show));
+			SendDlgMessage(hDlg, DM_ENABLE, SD_BUTTON_OK,   ToPtr(show));
+			return TRUE;
+		}
+		case DN_KILLFOCUS:
+		{
+			if ( SD_EDIT_TEXT == Param1 || SD_EDIT_HEX == Param1 )
+			{
+				MyDialogData *my = (MyDialogData *)SendDlgMessage(hDlg, DM_GETITEMDATA, SD_EDIT_TEXT, 0);
+				my->hex_mode = (SD_EDIT_HEX == Param1);
+			}
+			break;
+		}
 		case DN_BTNCLICK:
 		{
+			bool need_focus = false;
+			MyDialogData *my = (MyDialogData *)SendDlgMessage(hDlg, DM_GETITEMDATA, SD_EDIT_TEXT, 0);
+			int cradio = (my->hex_mode ? SD_RADIO_HEX : SD_RADIO_TEXT);
+
 			if ((Param1 == SD_RADIO_TEXT || Param1 == SD_RADIO_HEX) && Param2)
 			{
-				SendDlgMessage(hDlg, DM_ENABLEREDRAW, FALSE, 0);
-				Viewer *viewer = (Viewer *)SendDlgMessage(hDlg, DM_GETITEMDATA, SD_EDIT_TEXT, 0);
+				need_focus = true;
+				if ( Param1 != cradio)
+				{
+					bool new_hex = (Param1 == SD_RADIO_HEX);
 
-				int sd_dst = Param1 == SD_RADIO_HEX  ? SD_EDIT_HEX : SD_EDIT_TEXT;
-				int sd_src = Param1 == SD_RADIO_TEXT ? SD_EDIT_HEX : SD_EDIT_TEXT;
+					SendDlgMessage(hDlg, DM_ENABLEREDRAW, FALSE, 0);
 
-				const wchar_t *ps = (const wchar_t *)SendDlgMessage(hDlg, DM_GETCONSTTEXTPTR, sd_src, 0);
-				string strTo;
-				viewer->SearchTextTransform(strTo, ps, Param1 == SD_RADIO_TEXT);
+					int sd_dst = new_hex ? SD_EDIT_HEX : SD_EDIT_TEXT;
+					int sd_src = new_hex ? SD_EDIT_TEXT : SD_EDIT_HEX;
 
-				SendDlgMessage(hDlg, DM_SETTEXTPTR, sd_dst, const_cast<wchar_t*>(strTo.CPtr()));
-				SendDlgMessage(hDlg, DM_SDSETVISIBILITY, Param1 == SD_RADIO_HEX, 0);
+					EditorSetPosition esp;
+					esp.CurPos = -1;
+					SendDlgMessage(hDlg, DM_GETEDITPOSITION, sd_src, &esp);
+					const wchar_t *ps = (const wchar_t *)SendDlgMessage(hDlg, DM_GETCONSTTEXTPTR, sd_src, 0);
+					string strTo;
+					my->viewer->SearchTextTransform(strTo, ps, !new_hex, esp.CurPos);
 
-				if (!strTo.IsEmpty())
-				{
-					int changed = (int)SendDlgMessage(hDlg, DM_EDITUNCHANGEDFLAG, sd_src, ToPtr(-1));
-					SendDlgMessage(hDlg, DM_EDITUNCHANGEDFLAG, sd_dst, ToPtr(changed));
+					SendDlgMessage(hDlg, DM_SETTEXTPTR, sd_dst, ToPtr((INT_PTR)strTo.CPtr()));
+					SendDlgMessage(hDlg, DM_SDSETVISIBILITY, new_hex, 0);
+					if (esp.CurPos >= 0)
+					{
+						int p = esp.CurPos;
+						if (SendDlgMessage(hDlg, DM_GETEDITPOSITION, sd_dst, &esp))
+						{
+							esp.CurPos = esp.CurTabPos = p;
+							esp.LeftPos = 0;
+							SendDlgMessage(hDlg, DM_SETEDITPOSITION, sd_dst, &esp);
+						}
+					}
+
+					if (!strTo.IsEmpty())
+					{
+						int changed = (int)SendDlgMessage(hDlg, DM_EDITUNCHANGEDFLAG, sd_src, ToPtr(-1));
+						SendDlgMessage(hDlg, DM_EDITUNCHANGEDFLAG, sd_dst, ToPtr(changed));
+					}
+
+					SendDlgMessage(hDlg,DM_ENABLEREDRAW,TRUE,0);
+					my->hex_mode = new_hex;
+					if ( !my->edit_autofocus )
+						return TRUE;
 				}
+			}
+			else if ( Param1 == SD_CHECKBOX_REGEXP )
+			{
+				SendDlgMessage(hDlg, DM_SDSETVISIBILITY, my->hex_mode, 0);
+			}
 
-				SendDlgMessage(hDlg,DM_ENABLEREDRAW,TRUE,0);
+			if ( my->edit_autofocus && !my->recursive )
+			{
+				if ( need_focus
+				  || Param1 == SD_CHECKBOX_CASE
+				  || Param1 == SD_CHECKBOX_WORDS
+				  || Param1 == SD_CHECKBOX_REVERSE
+				  || Param1 == SD_CHECKBOX_REGEXP
+				){
+					my->recursive = true;
+					SendDlgMessage(hDlg, DM_SETFOCUS, my->hex_mode ? SD_EDIT_HEX : SD_EDIT_TEXT, 0);
+					my->recursive = false;
+				}
+			}
+
+			if (need_focus)
 				return TRUE;
-			}
+			else
+				break;
 		}
+		case DN_EDITCHANGE:
+		{
+			if ( Param1 == SD_EDIT_TEXT && SendDlgMessage(hDlg,DM_GETCHECK,SD_CHECKBOX_REGEXP,0) == BSTATE_CHECKED )
+				SendDlgMessage(hDlg, DM_SDREXVISIBILITY, 1, 0);
+			break;
+		}
 		case DN_HOTKEY:
 		{
 			if (Param1==SD_TEXT_SEARCH)
 			{
-				SendDlgMessage(hDlg,DM_SETFOCUS,(SendDlgMessage(hDlg,DM_GETCHECK,SD_RADIO_HEX,0) == BSTATE_CHECKED)?SD_EDIT_HEX:SD_EDIT_TEXT,0);
+				MyDialogData *my = (MyDialogData *)SendDlgMessage(hDlg, DM_GETITEMDATA, SD_EDIT_TEXT, 0);
+				SendDlgMessage(hDlg, DM_SETFOCUS, (my->hex_mode ? SD_EDIT_HEX : SD_EDIT_TEXT), 0);
 				return FALSE;
 			}
 		}
@@ -2059,10 +2364,13 @@
 static void PR_ViewerSearchMsg()
 {
 	PreRedrawItem preRedrawItem=PreRedraw.Peek();
-	ViewerSearchMsg((const wchar_t*)preRedrawItem.Param.Param1,(int)(INT_PTR)preRedrawItem.Param.Param2);
+	const wchar_t *name = (const wchar_t*)preRedrawItem.Param.Param1;
+	int percent = (int)(INT_PTR)preRedrawItem.Param.Param2;
+	int search_hex = (int)(INT_PTR)preRedrawItem.Param.Param3;
+	ViewerSearchMsg(name, percent, search_hex);
 }
 
-void ViewerSearchMsg(const wchar_t *MsgStr,int Percent)
+void ViewerSearchMsg(const wchar_t *MsgStr, int Percent, int SearchHex)
 {
 	string strProgress;
 
@@ -2093,6 +2401,7 @@
 	PreRedrawItem preRedrawItem=PreRedraw.Peek();
 	preRedrawItem.Param.Param1=(void*)MsgStr;
 	preRedrawItem.Param.Param2=(LPVOID)(INT_PTR)Percent;
+	preRedrawItem.Param.Param3=(LPVOID)(INT_PTR)SearchHex;
 	PreRedraw.SetParam(preRedrawItem.Param);
 }
 
@@ -2109,15 +2418,17 @@
 	}
 }
 
-static int hex2ss(const wchar_t *from, char *c1, int mb)
+static int hex2ss(const wchar_t *from, char *c1, int mb, int *pos = 0)
 {
-	int nb, i, v, sub = 0;
+	int nb, i, v, sub = 0, ps = 0, p0 = (pos ? *pos : -1), p1 = -1;
 	wchar_t ch;
 
 	nb = i = v = 0;
 	for (;;)
 	{
 		ch = *from++;
+		++ps;
+
 		if      (ch >= L'0' && ch <= L'9') sub = L'0';
 		else if (ch >= L'A' && ch <= L'F') sub = L'A' - 10;
 		else if (ch >= L'a' && ch <= L'f') sub = L'a' - 10;
@@ -2125,6 +2436,8 @@
 		{
 			if (i > 0)
 			{
+				if ( p0 >= 0 && ps > p0 && p1 < 0 )
+					p1 = nb;
 				c1[nb++] = (char)v;
 				i = 0;
 			}
@@ -2136,6 +2449,8 @@
 		v = v*16 + ((int)ch - sub);
 		if (++i >= 2)
 		{
+			if ( p0 >= 0 && ps > p0 && p1 < 0 )
+				p1 = nb;
 			c1[nb++] = (char)v;
 			i = 0;
 			if (nb >= mb)
@@ -2145,10 +2460,12 @@
 #ifdef _DEBUG
 	if (nb < mb) c1[nb] = '\0';
 #endif
+	if ( p1 >= 0 && pos )
+		*pos = p1;
 	return nb;
 }
 
-void Viewer::SearchTextTransform( UnicodeString &to, const wchar_t *from, bool hex2text )
+void Viewer::SearchTextTransform( UnicodeString &to, const wchar_t *from, bool hex2text, int &pos )
 {
 	int nb, i, v;
 	char c1[128];
@@ -2156,7 +2473,7 @@
 
 	if (hex2text)
 	{
-		nb = hex2ss(from, c1, ARRAYSIZE(c1));
+		nb = hex2ss(from, c1, ARRAYSIZE(c1), &pos);
 
 		if (IsUnicodeCodePage(VM.CodePage))
 		{
@@ -2171,10 +2488,16 @@
 					ch = 0xffff; // encode L'\0'
 				to += ch;
 			}
+			if ( pos >= 0 )
+				pos /= 2;
 		}
 		else
 		{
 			int nw = MultiByteToWideChar(VM.CodePage,0, c1,nb, ss,ARRAYSIZE(ss));
+			if ( pos >= 0 )
+			{
+				pos = MultiByteToWideChar(VM.CodePage,0, c1,pos, NULL,0);
+			}
 			for (i=0; i < nw; ++i)
 				if (!ss[i])
 					ss[i] = 0xffff;
@@ -2184,8 +2507,12 @@
 	}
 	else // text2hex
 	{
+		int ps = 0, pd = 0, p0 = pos, p1 = -1;
 		while (*from)
 		{
+			if ( ps == p0 )
+				p1 = pd;
+			++ps;
 			ch = *from++;
 			if (0xffff == ch)	// 0xffff - invalid unicode char
 				ch = 0x0000;   // it used to transfer '\0' in zero terminated string
@@ -2199,398 +2526,737 @@
 					nb = 2; c1[0] = (char)HIBYTE(ch); c1[1] = (char)LOBYTE(ch);
 				break;
 				default:
-					nb = WideCharToMultiByte(VM.CodePage,0, &ch, 1, c1, 4, nullptr, nullptr);
+					nb = WideCharToMultiByte(VM.CodePage,0, &ch,1, c1,4, NULL,NULL);
 				break;
 			}
 
 			ss2hex(to, c1, nb, L'\0');
+			pd += nb * 3;
 		}
+		pos = p1;
 	}
 }
 
-/* $ 27.01.2003 VVM
-   + �������� Next ����� �������� ������:
-   0 - ���� �����
-   1 - ��������� ����� �� ������� ������
-   2 - ��������� ����� � ����� �����
-*/
+struct Viewer::search_data
+{
+	INT64 CurPos;
+	INT64 MatchPos;
+	const char *search_bytes;
+	const wchar_t *search_text;
+	int search_len;
+	int  ch_size;
+	bool is_utf8;
+   bool first_Rex;
+	RegExp *pRex;
 
-void Viewer::Search(int Next,int FirstChar)
-{
-	const wchar_t *TextHistoryName=L"SearchText";
-	const wchar_t *HexMask=L"HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH ";
-	FarDialogItem SearchDlgData[]=
+	search_data()
 	{
-		{DI_DOUBLEBOX,3,1,72,11,0,nullptr,nullptr,0,MSG(MViewSearchTitle)},
-		{DI_TEXT,5,2,0,2,0,nullptr,nullptr,0,MSG(MViewSearchFor)},
-		{DI_EDIT,5,3,70,3,0,TextHistoryName,nullptr,DIF_FOCUS|DIF_HISTORY|DIF_USELASTHISTORY,L""},
-		{DI_FIXEDIT,5,3,70,3,0,nullptr,HexMask,DIF_MASKEDIT,L""},
-		{DI_TEXT,3,4,0,4,0,nullptr,nullptr,DIF_SEPARATOR,L""},
-		{DI_RADIOBUTTON,5,5,0,5,1,nullptr,nullptr,DIF_GROUP,MSG(MViewSearchForText)},
-		{DI_RADIOBUTTON,5,6,0,6,0,nullptr,nullptr,0,MSG(MViewSearchForHex)},
-		{DI_CHECKBOX,40,5,0,5,0,nullptr,nullptr,0,MSG(MViewSearchCase)},
-		{DI_CHECKBOX,40,6,0,6,0,nullptr,nullptr,0,MSG(MViewSearchWholeWords)},
-		{DI_CHECKBOX,40,7,0,7,0,nullptr,nullptr,0,MSG(MViewSearchReverse)},
-		{DI_CHECKBOX,40,8,0,8,0,nullptr,nullptr,DIF_DISABLE,MSG(MViewSearchRegexp)},
-		{DI_TEXT,3,9,0,9,0,nullptr,nullptr,DIF_SEPARATOR,L""},
-		{DI_BUTTON,0,10,0,10,0,nullptr,nullptr,DIF_DEFAULTBUTTON|DIF_CENTERGROUP,MSG(MViewSearchSearch)},
-		{DI_BUTTON,0,10,0,10,0,nullptr,nullptr,DIF_CENTERGROUP,MSG(MViewSearchCancel)},
-	};
-	MakeDialogItemsEx(SearchDlgData,SearchDlg);
-	string strSearchStr;
-	char search_bytes[128];
-	string strMsgStr;
-	__int64 MatchPos=0;
-	int SearchLength,Case,WholeWords,ReverseSearch,Match,SearchRegexp;
+		CurPos = MatchPos = -1;
+		search_bytes = nullptr;
+		search_text  = nullptr;
+		search_len = ch_size = 0;
+		is_utf8 = false;
+		first_Rex = true;
+		pRex = nullptr;
+	}
 
-	if (!ViewFile.Opened() || (Next && strLastSearchStr.IsEmpty()))
-		return;
+	~search_data()
+	{
+		 delete pRex;
+	}
+};
 
-	if (!strLastSearchStr.IsEmpty())
-		strSearchStr = strLastSearchStr;
+int Viewer::search_hex_forward( search_data* sd )
+{
+	char *buff = (char *)Search_buffer;
+	const char *search_str = sd->search_bytes;
+	int bsize = Search_buffer_size * static_cast<int>(sizeof(wchar_t)), slen = sd->search_len;
+	INT64 to, cpos = sd->CurPos;
+
+	bool up_half = cpos >= StartSearchPos;
+	if ( up_half )
+	{
+		if ( (to = FileSize) - cpos <= bsize )
+			SetFileSize();
+		to = FileSize;
+	}
 	else
-		strSearchStr.Clear();
+	{
+		to = StartSearchPos + slen - 1;
+		if ( to > FileSize )
+			to = FileSize;
+	}
 
-	SearchDlg[SD_RADIO_TEXT].Selected=!LastSearchHex;
-	SearchDlg[SD_RADIO_HEX].Selected=LastSearchHex;
-	SearchDlg[SD_CHECKBOX_CASE].Selected=LastSearchCase;
-	SearchDlg[SD_CHECKBOX_WORDS].Selected=LastSearchWholeWords;
-	SearchDlg[SD_CHECKBOX_REVERSE].Selected=LastSearchReverse;
-	SearchDlg[SD_CHECKBOX_REGEXP].Selected=LastSearchRegexp;
+	int nb = (to - cpos < bsize ? static_cast<int>(to - cpos) : bsize);
+	vseek(cpos, SEEK_SET);
+	DWORD nr = 0;
+	Reader.Read(buff, static_cast<DWORD>(nb), &nr);
+	int n1 = static_cast<int>(nr);
+	if ( n1 != nb )
+		SetFileSize();
 
-	if (SearchFlags.Check(REVERSE_SEARCH))
-		SearchDlg[SD_CHECKBOX_REVERSE].Selected=!SearchDlg[SD_CHECKBOX_REVERSE].Selected;
+	if ( n1 < slen )
+	{
+		if ( !up_half || StartSearchPos <= 0 )
+			return -1;
+		sd->CurPos = 0;
+		return 0;
+	}
 
-	if (SearchDlg[SD_RADIO_HEX].Selected)
+	char *ps = buff;
+	while ( nullptr != (ps = static_cast<char *>(memchr(ps, search_str[0], n1-slen+1))) )
 	{
-		int nb = hex2ss(strSearchStr.CPtr(), search_bytes, ARRAYSIZE(search_bytes));
-		strSearchStr.Clear();
-		ss2hex(strSearchStr, search_bytes, nb, L'\0');
-		SearchDlg[SD_EDIT_HEX].strData = strSearchStr;
+		if ( slen <= 1 || 0 == memcmp(ps+1, search_str+1, slen-1) )
+		{
+			sd->MatchPos = cpos + static_cast<INT64>(ps - buff);
+			return +1; // found
+		}
+		++ps;
+		n1 = static_cast<int>(nr - (ps - buff)); 
 	}
+
+	sd->CurPos = cpos + nr - slen + 1;
+	return (up_half || sd->CurPos < StartSearchPos ? 0 : -1);
+}
+
+int Viewer::search_hex_backward( search_data* sd )
+{
+	char *buff = (char *)Search_buffer;
+	const char *search_str = sd->search_bytes;
+	int bsize = Search_buffer_size * static_cast<int>(sizeof(wchar_t)), slen = sd->search_len;
+	INT64 to, cpos = sd->CurPos;
+
+	bool lo_half = cpos <= StartSearchPos;
+	if ( lo_half )
+	{
+		to = 0;
+	}
 	else
-		SearchDlg[SD_EDIT_TEXT].strData = strSearchStr;
+	{
+		to = StartSearchPos - slen + 1;
+		if ( to < 0 )
+			to = 0;
+	}
 
-	SearchDlg[SD_EDIT_TEXT].UserData = this;
+	int nb = (cpos - to < bsize ? static_cast<int>(cpos - to) : bsize);
+	vseek(cpos-nb, SEEK_SET);
+	DWORD nr = 0;
+	Reader.Read(buff, static_cast<DWORD>(nb), &nr);
+	int n1 = static_cast<int>(nr);
+	if ( n1 != nb )
+	{
+		SetFileSize();
+		cpos = vtell();
+	}
 
-	if (!Next)
+	if ( n1 < slen )
 	{
-		SearchFlags.Flags = 0;
-		Dialog Dlg(SearchDlg,ARRAYSIZE(SearchDlg),ViewerSearchDlgProc);
-		Dlg.SetPosition(-1,-1,76,13);
-		Dlg.SetHelp(L"ViewerSearch");
+		if ( !lo_half )
+			return -1;
+		SetFileSize();
+		if ( StartSearchPos >= FileSize )
+			return -1;
+		sd->CurPos = FileSize;
+		return 0;
+	}
 
-		if (FirstChar)
+	for ( char *ps = buff + n1 - 1; ps >= buff + slen - 1; --ps )
+	{
+		if ( *ps == search_str[slen-1] )
 		{
-			Dlg.InitDialog();
-			Dlg.Show();
-			Dlg.ProcessKey(FirstChar);
+			if ( slen <= 1 || 0 == memcmp(ps-slen+1, search_str, slen-1) )
+			{
+				sd->MatchPos = static_cast<INT64>(cpos-n1+(ps-slen+1-buff));
+				return +1;
+			}
 		}
+	}
 
-		Dlg.Process();
+	sd->CurPos = cpos - nr + slen - 1;
+	return (lo_half || sd->CurPos > StartSearchPos ? 0 : -1);
+}
 
-		if (Dlg.GetExitCode()!=SD_BUTTON_OK)
-			return;
+int Viewer::search_text_forward( search_data* sd )
+{
+	int bsize = 8192, slen = sd->search_len, ww = (LastSearchWholeWords ? 1 : 0);
+	wchar_t prev_char = L'\0', *buff = Search_buffer, *t_buff = (sd->is_utf8 ? buff + bsize : nullptr);
+	const wchar_t *search_str = sd->search_text;
+	INT64 to1, to, cpos = sd->CurPos;
+
+	vseek(cpos, SEEK_SET);
+	if ( ww )
+		prev_char = vgetc_prev();
+
+	bool up_half = cpos >= StartSearchPos;
+	if ( up_half )
+	{
+		if ( (to = FileSize) - cpos <= bsize )
+			SetFileSize();
+		to = FileSize;
 	}
+	else
+	{
+		to = StartSearchPos;
+		if ( to > FileSize )
+			to = FileSize;
+	}
 
-	SearchHex=SearchDlg[SD_RADIO_HEX].Selected;
-	Case=SearchDlg[SD_CHECKBOX_CASE].Selected;
-	WholeWords=SearchDlg[SD_CHECKBOX_WORDS].Selected;
-	ReverseSearch=SearchDlg[SD_CHECKBOX_REVERSE].Selected;
-	SearchRegexp=SearchDlg[SD_CHECKBOX_REGEXP].Selected;
+   int nb = (to - cpos > bsize ? bsize : static_cast<int>(to - cpos));
+	int nw = vread(buff, nb, t_buff);
+	to1 = vtell();
+	if ( !up_half && nb + 3*(slen+ww) < bsize && !veof() )
+	{
+		int nw1 = vread(buff+nw, 3*(slen+ww), t_buff ? t_buff+nw : nullptr);
+		nw1 = (nw1 > slen+ww-1 ? slen+ww-1 : nw1);
+		nw += nw1;
+		to1 = to + (t_buff ? GetStrBytesNum(t_buff, nw1) : sd->ch_size * nw1);
+	}
 
-	bool t_utf8 = !SearchHex && VM.CodePage == CP_UTF8;
+	int is_eof = (to1 >= FileSize ? 1 : 0), iLast = nw - slen - ww + is_eof;
+	if ( !LastSearchCase )
+		CharUpperBuff(buff, nw);
 
-	if (SearchHex)
+	for ( int i = 0; i <= iLast; ++i )
 	{
-		strSearchStr = SearchDlg[SD_EDIT_HEX].strData;
-		SearchLength = hex2ss(strSearchStr.CPtr(), search_bytes, ARRAYSIZE(search_bytes));
-		strSearchStr.Clear();
-		ss2hex(strSearchStr, search_bytes, SearchLength, L' ');
+		if ( ww )
+		{
+			if ( !is_word_div(i > 0 ? buff[i-1] : prev_char))
+				continue;
+			if ( !(i == iLast && is_eof) && !is_word_div(buff[i+slen]) )
+				continue;
+		}
+		if ( buff[i] != search_str[0]
+		 || (slen > 1 && buff[i+1] != search_str[1])
+		 || (slen > 2 && 0 != wmemcmp(buff+i+2, search_str+2, slen-2))
+		) continue;
+
+		sd->MatchPos = cpos + GetStrBytesNum(t_buff, i);
+		sd->search_len = GetStrBytesNum(t_buff+i, slen);
+		return +1;
 	}
+
+	if ( up_half && is_eof )
+	{
+		sd->CurPos = 0;
+		return (StartSearchPos > 0 ? 0 : -1);
+	}
 	else
 	{
-		strSearchStr = SearchDlg[SD_EDIT_TEXT].strData;
-		SearchLength = (int)strSearchStr.GetLength();
-		size_t pos = 0;
-		while (strSearchStr.Pos(pos, 0xffff))
-			strSearchStr.Replace(pos, 1, L'\0');
+		if ( iLast < 0 || (!up_half && to1 > StartSearchPos) )
+			return -1;
+		sd->CurPos = to1;
+		return 0;
 	}
+}
 
-	strLastSearchStr = strSearchStr;
-	LastSearchHex=SearchHex;
-	LastSearchCase=Case;
-	LastSearchWholeWords=WholeWords;
+int Viewer::search_text_backward( search_data* sd )
+{
+	int bsize = 8192, slen = sd->search_len, ww = (LastSearchWholeWords ? 1 : 0);
+	wchar_t *buff = Search_buffer, *t_buff = (sd->is_utf8 ? buff + bsize : nullptr);
+	const wchar_t *search_str = sd->search_text;
+	INT64 to1, to, cpos = sd->CurPos;
 
-	if (!SearchFlags.Check(REVERSE_SEARCH))
-		LastSearchReverse=ReverseSearch;
+	bool up_half = cpos > StartSearchPos;
+	to = (up_half ? StartSearchPos : 0);
 
-	LastSearchRegexp=SearchRegexp;
+	int nb = (cpos - to > bsize ? bsize : static_cast<int>(cpos- to));
+	if ( up_half && nb + 3*(slen+ww) < bsize && cpos > nb )
+	{
+		if ( sd->ch_size > 0 )
+		{
+			nb += sd->ch_size * (slen + ww - 1);
+			if (cpos < nb)
+				nb = static_cast<int>(cpos);
+		}
+		else
+		{
+			to1 = cpos - nb - 3*(slen + ww - 1);
+			if (to1 < 0)
+				to1 = 0;
+			int nb1 = static_cast<int>(cpos - nb - to1);
+			vseek(to1, SEEK_SET);
+			int nw1 = vread(buff, nb1, t_buff);
+			if ( nw1 > slen + ww - 1 )
+				nb1 = GetStrBytesNum(t_buff + nw1 - (slen + ww - 1), slen + ww - 1);
+			nb += nb1;
+		}
+	}
 
-	if (!SearchLength)
-		return;
+	cpos -= nb;
+	vseek(cpos, SEEK_SET);
+	int nw = vread(buff, nb, t_buff);
+	if ( !LastSearchCase )
+		CharUpperBuff(buff, nw);
 
+	int is_eof = (veof() ? 1 : 0), iFirst = ww * (cpos > 0 ? 1 : 0), iLast = nw - slen - ww + ww*is_eof;
+	for ( int i = iLast; i >= iFirst; --i )
 	{
-		TPreRedrawFuncGuard preRedrawFuncGuard(PR_ViewerSearchMsg);
-		//SaveScreen SaveScr;
-		SetCursorType(FALSE,0);
-		strMsgStr = strSearchStr;
+		if ( ww )
+		{
+			if ( i > 0 && !is_word_div(buff[i-1]) )
+				continue;
+			if ( !(i == iLast && is_eof) && !is_word_div(buff[i+slen]) )
+				continue;
+		}
+		if ( buff[i] != search_str[0]
+		|| (slen > 1 && buff[i+1] != search_str[1])
+		|| (slen > 2 && 0 != wmemcmp(buff+i+2, search_str+2, slen-2))
+		) continue;
 
-		if (strMsgStr.GetLength()+18 > static_cast<DWORD>(ObjWidth))
-			TruncStrFromEnd(strMsgStr, ObjWidth-18);
+		sd->MatchPos = cpos + GetStrBytesNum(t_buff, i);
+		sd->search_len = GetStrBytesNum(t_buff+i, slen);
+		return +1;
+	}
 
-		if (!SearchHex)
-			InsertQuote(strMsgStr);
+	int ret = 0;
+	if ( up_half )
+		ret = (cpos <= StartSearchPos ? -1 : 0);
+	else
+	{
+		if ( cpos <= 0 )
+		{
+			SetFileSize();
+			cpos = FileSize;
+			ret = (StartSearchPos >= FileSize ? -1 : 0);
+		}
+	}
+	sd->CurPos = cpos;
+   return ret;
+}
 
-		if (SearchHex)
-			Case = 1 + (WholeWords = 0);
+int Viewer::read_line(wchar_t *buf, wchar_t *tbuf, INT64 cpos, int adjust, INT64 &lpos, int &lsize)
+{
+	int llen = 0;
 
-		if (!Case && !SearchHex)
-			strSearchStr.Upper();
+	INT64 save_FilePos = FilePos, save_LastPage = LastPage;
+	int save_Hex = VM.Hex, save_Wrap = VM.Wrap, save_WordWrap = VM.WordWrap;
+	VM.Hex = VM.Wrap = VM.WordWrap = 0;
 
-		SelectSize = 0;
+	FilePos = cpos;
+	if ( adjust )
+	{
+		if ( adjust > 0 )
+			AdjustFilePos();
+		else
+			Up(1);
+	}
 
-		if (Next)
+	vseek(lpos = vString.nFilePos = FilePos, SEEK_SET);
+	vString.linesize = 0;
+	ReadString(&vString, -1, false); // read unwrapped text line
+
+	vseek(FilePos, SEEK_SET);
+	llen = vread(buf, lsize = vString.linesize, tbuf);
+	if ( llen > 0 && vString.have_eol ) // remove eol-s
+	{
+		--llen;
+		if ( llen > 0 && (buf[llen-1] == L'\r' || buf[llen-1] == L'\n') )
+			--llen;
+	}
+	buf[llen >= 0 ? llen : 0] = L'\0';
+
+   VM.Hex = save_Hex; VM.Wrap = save_Wrap; VM.WordWrap = save_WordWrap;
+	FilePos = save_FilePos; LastPage = save_LastPage;
+	return llen;
+}
+
+int Viewer::search_regex_forward( search_data* sd )
+{
+	assert(sd->pRex);
+	assert(Search_buffer_size >= 2*MAX_VIEWLINEB);
+
+	wchar_t *line = Search_buffer, *t_line = sd->is_utf8 ? Search_buffer + MAX_VIEWLINEB : nullptr;
+	INT64 cpos = sd->CurPos, bpos = 0;
+
+	int first = (sd->first_Rex ? +1 : 0);
+	sd->first_Rex = false;
+	bool up_half = cpos >= StartSearchPos;
+
+	int lsize = 0, nw = read_line(line, t_line, cpos, first, bpos, lsize);
+   if ( lsize <= 0 )
+	{
+		sd->CurPos = 0;
+		return (up_half && StartSearchPos > 0 ? 0 : -1);
+	}
+
+	int off = 0;
+	for (;;)
+	{
+		if ( off > nw )
+			break;
+
+		SMatch m[1];
+		int n = static_cast<int>(ARRAYSIZE(m));
+		if ( !sd->pRex->SearchEx(line, line+off, line+nw, m, n) )  // doesn't match
+			break;
+
+		INT64 fpos = bpos + GetStrBytesNum(t_line, m[0].start);
+		if ( fpos < cpos )
 		{
-			if (Next == 2)
-			{
-				SearchFlags.Set(SEARCH_MODE2);
-				LastSelPos = ReverseSearch?FileSize:0;
-			}
-			else
-			{
-				LastSelPos = SelectPos + (ReverseSearch?-1:1);
-			}
+			off = m[0].start + 1; // skip
+			continue;
 		}
-		else
+		else if ( !up_half && fpos >= StartSearchPos )
 		{
-			LastSelPos = FilePos;
+			break; // done - not found
+		}
+		else // found
+		{
+			sd->MatchPos = fpos;
+			sd->search_len = GetStrBytesNum(t_line+off, m[0].end - m[0].start);
+			return +1;
+		}
+	}
 
-			if (!LastSelPos || LastSelPos == FileSize)
-				SearchFlags.Set(SEARCH_MODE2);
+	sd->CurPos = vtell();
+	if ( up_half )
+	{
+		if ( veof() )
+		{
+			sd->CurPos = 0;
+			return (StartSearchPos > 0 ? 0 : -1);
 		}
+		return 0;
+	}
+	else
+	{
+		return (sd->CurPos >= StartSearchPos ? -1 : 0);
+	}
+}
 
-		vseek(LastSelPos,SEEK_SET);
-		Match = 0;
+int Viewer::search_regex_backward( search_data* sd )
+{
+	assert(sd->pRex);
+	assert(Search_buffer_size >= 2*MAX_VIEWLINEB);
 
-		if (SearchLength > 0 && (!ReverseSearch || LastSelPos > 0))
+	wchar_t *line = Search_buffer, *t_line = sd->is_utf8 ? Search_buffer + MAX_VIEWLINEB : nullptr;
+	INT64 cpos = sd->CurPos, bpos = 0, prev_pos = -1;
+
+	bool up_half = cpos > StartSearchPos;
+
+	int off=0, lsize=0, nw, prev_len = -1, flen;
+	nw = read_line(line, t_line, cpos, -1, bpos, lsize);
+	for (;;)
+	{
+		if ( lsize <= 0 || off > nw )
+			break;
+
+		SMatch m[1];
+		int n = static_cast<int>(ARRAYSIZE(m));
+		if ( !sd->pRex->SearchEx(line, line+off, line+nw, m, n) )
+			break;
+
+		INT64 fpos = bpos + GetStrBytesNum(t_line, m[0].start);
+		flen = GetStrBytesNum(t_line + m[0].start, m[0].end - m[0].start);
+		if ( fpos+flen > cpos )
+			break;
+
+		if ( !(up_half && fpos+flen <= StartSearchPos) )
 		{
-			wchar_t *Buf = new wchar_t[8192 + 8192], *t_Buf = t_utf8 ? Buf + 8192 : nullptr;
-			int buf_size = 8192, seek_size = 1;
-			char *byte_buff = nullptr;
-			if (SearchHex)
-			{
-				byte_buff = (char *)Buf;
-				buf_size *= 2*(int)sizeof(wchar_t);
-				seek_size = IsUnicodeCodePage(VM.CodePage) ? 2 : 1;
-			}
+			prev_pos = fpos;
+			prev_len = flen;
+		}
 
-			__int64 CurPos=LastSelPos;
-			int BufSize = buf_size;
+		off = m[0].start + 1; // skip
+		continue;
+	}
 
-			if (ReverseSearch)
-			{
-				/* $ 01.08.2000 KM
-				   ������� �������� CurPos � ���� Whole words
-				*/
-				if (WholeWords)
-					CurPos -= BufSize/seek_size - SearchLength/seek_size + 1;
-				else
-					CurPos -= BufSize/seek_size - SearchLength/seek_size;
+	if ( prev_len >= 0 )
+	{
+		sd->MatchPos = prev_pos;
+		sd->search_len = prev_len;
+		return +1;
+	}
+	
+	if ( (sd->CurPos = bpos) <= 0 )
+	{
+		SetFileSize();
+		sd->CurPos = FileSize;
+		return (StartSearchPos >= FileSize ? -1 : 0);
+	}
+   return (up_half && bpos <= StartSearchPos ? -1 : 0);
+}
 
-				if (CurPos<0)
-					BufSize += (int)CurPos * seek_size;
-			}
 
-			int ReadSize;
-			TaskBar TB;
-			wakeful W;
-			INT64 StartPos = vtell();
-			DWORD StartTime = GetTickCount();
+/*
+ + �������� Next ����� �������� ������:
+ 0 - ���� �����
+ 1 - ��������� ����� �� ������� ������
+-1 - ��������� ����� �� ������� ������ � ��������������� �����������
+ 2 - ��������� ����� � �����/���� �����
+*/
+void Viewer::Search(int Next,int FirstChar)
+{
+	if (!ViewFile.Opened() || (Next && strLastSearchStr.IsEmpty()))
+		return;
 
-			while (!Match)
-			{
-				/* $ 01.08.2000 KM
-				   �������� ������ if (ReverseSearch && CurPos<0) �� if (CurPos<0),
-				   ��� ��� ��� ����� ����� � LastSelPos=0xFFFFFFFF, �����
-				   ���������� ��� � �� ������.
-				*/
-				if (CurPos < 0)
-					CurPos = 0;
+	string strSearchStr, strMsgStr;
+	char search_bytes[128];
+	int Case,WholeWords,ReverseSearch,SearchRegexp,SearchHex;
 
-				vseek(CurPos, SEEK_SET);
+	SearchHex = LastSearchHex;
+	Case = LastSearchCase;
+	WholeWords = LastSearchWholeWords;
+	ReverseSearch = LastSearchReverse;
+	SearchRegexp = LastSearchRegexp;
+	strSearchStr.Clear();
+	if (!strLastSearchStr.IsEmpty())
+		strSearchStr = strLastSearchStr;
 
-				if (SearchHex)
-				{
-					DWORD nr = 0;
-					Reader.Read(byte_buff, (DWORD)BufSize, &nr);
-					ReadSize = (int)nr;
-				}
-				else
-				{
-					ReadSize = vread(Buf, BufSize, t_Buf);
-				}
-				if (ReadSize <= 0)
-					break;
+	search_data sd;
+	int (Viewer::* searcher)( Viewer::search_data *p_sd ) = nullptr;
 
-				DWORD CurTime=GetTickCount();
+	if ( !Next )
+	{
+		const wchar_t *TextHistoryName=L"SearchText";
+		const wchar_t *HexMask = L"HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH " ;
+		FarDialogItem SearchDlgData[]=
+		{
+			{DI_DOUBLEBOX,3,1,72,11,0,nullptr,nullptr,0,MSG(MViewSearchTitle)},
+			{DI_TEXT,5,2,0,2,0,nullptr,nullptr,0,MSG(MViewSearchFor)},
+			{DI_EDIT,5,3,70,3,0,TextHistoryName,nullptr,DIF_FOCUS|DIF_HISTORY|DIF_USELASTHISTORY,L""},
+			{DI_FIXEDIT,5,3,70,3,0,nullptr,HexMask,DIF_MASKEDIT,L""},
+			{DI_TEXT,3,4,0,4,0,nullptr,nullptr,DIF_SEPARATOR,L""},
+			{DI_RADIOBUTTON,5,5,0,5,1,nullptr,nullptr,DIF_GROUP,MSG(MViewSearchForText)},
+			{DI_RADIOBUTTON,5,6,0,6,0,nullptr,nullptr,0,MSG(MViewSearchForHex)},
+			{DI_CHECKBOX,40,5,0,5,0,nullptr,nullptr,0,MSG(MViewSearchCase)},
+			{DI_CHECKBOX,40,6,0,6,0,nullptr,nullptr,0,MSG(MViewSearchWholeWords)},
+			{DI_CHECKBOX,40,7,0,7,0,nullptr,nullptr,0,MSG(MViewSearchReverse)},
+			{DI_CHECKBOX,40,8,0,8,0,nullptr,nullptr,DIF_DISABLE,MSG(MViewSearchRegexp)},
+			{DI_TEXT,3,9,0,9,0,nullptr,nullptr,DIF_SEPARATOR,L""},
+			{DI_BUTTON,0,10,0,10,0,nullptr,nullptr,DIF_DEFAULTBUTTON|DIF_CENTERGROUP,MSG(MViewSearchSearch)},
+			{DI_BUTTON,0,10,0,10,0,nullptr,nullptr,DIF_CENTERGROUP,MSG(MViewSearchCancel)},
+		};
+		MakeDialogItemsEx(SearchDlgData,SearchDlg);
 
-				if (CurTime-StartTime>RedrawTimeout)
-				{
-					StartTime=CurTime;
+		SearchDlg[SD_RADIO_TEXT].Selected=!LastSearchHex;
+		SearchDlg[SD_RADIO_HEX].Selected=LastSearchHex;
+		SearchDlg[SD_CHECKBOX_CASE].Selected=LastSearchCase;
+		SearchDlg[SD_CHECKBOX_WORDS].Selected=LastSearchWholeWords;
+		SearchDlg[SD_CHECKBOX_REVERSE].Selected=LastSearchReverse;
+		SearchDlg[SD_CHECKBOX_REGEXP].Selected=LastSearchRegexp;
 
-					if (CheckForEscSilent())
-					{
-						if (ConfirmAbortOp())
-						{
-							Redraw();
-							return;
-						}
-					}
+		if (SearchDlg[SD_RADIO_HEX].Selected)
+		{
+			int nb = hex2ss(strSearchStr.CPtr(), search_bytes, ARRAYSIZE(search_bytes));
+			strSearchStr.Clear();
+			ss2hex(strSearchStr, search_bytes, nb, L'\0');
+			SearchDlg[SD_EDIT_HEX].strData = strSearchStr;
+		}
+		else
+			SearchDlg[SD_EDIT_TEXT].strData = strSearchStr;
 
-					INT64 Total=ReverseSearch?StartPos:FileSize-StartPos;
-					INT64 Current=_abs64(CurPos-StartPos);
-					int Percent=Total>0?static_cast<int>(Current*100/Total):-1;
-					// � ����� ���� ���� ����������� �������, �� ���������
-					// �������� ����� �� ���� 100. ����������� ��� ������.
-					if (Percent>100)
-					{
-						SetFileSize();
-						Total=FileSize-StartPos;
-						Percent=Total>0?static_cast<int>(Current*100/Total):-1;
-						if (Percent>100)
-						{
-							Percent=100;
-						}
-					}
-					ViewerSearchMsg(strMsgStr,Percent);
-				}
+		MyDialogData my;
+		//
+		my.viewer = this;
+		my.edit_autofocus = (ViOpt.SearchEditFocus != 0);
+		my.hex_mode = (LastSearchHex != 0);
+		my.recursive = false;
+		//
+		SearchDlg[SD_EDIT_TEXT].UserData = &my;
 
-				/* $ 01.08.2000 KM
-				   ������� ����� �������� �� Case sensitive � Hex
-				   � ���� ���, ����� Buf ��������� � ������� ��������
-				*/
-				if (!Case && !SearchHex)
-					CharUpperBuff(Buf, ReadSize);
+		Dialog Dlg(SearchDlg,ARRAYSIZE(SearchDlg),ViewerSearchDlgProc);
+		Dlg.SetPosition(-1,-1,76,13);
+		Dlg.SetHelp(L"ViewerSearch");
 
-				/* $ 01.08.2000 KM
-				   ����� ����� ������ ����� ��������� ��������� ������
-				   � Buf � ������� ��������, ���� ����� �� ����������������
-				   ��� �� ���� Hex-������ � � ���� � ���� ����������� ��� ������
-				*/
-				int MaxSize=ReadSize-SearchLength+1;
-				int Increment=ReverseSearch ? -seek_size : +seek_size;
+		if (FirstChar)
+		{
+			Dlg.InitDialog();
+			Dlg.Show();
+			Dlg.ProcessKey(FirstChar);
+		}
 
-				for (int I=ReverseSearch ? MaxSize-1:0; I<MaxSize && I>=0; I+=Increment)
-				{
-					/* $ 01.08.2000 KM
-					   ��������� ������ "Whole words"
-					*/
-					int locResultLeft=FALSE;
-					int locResultRight=FALSE;
+		Dlg.Process();
 
-					if (WholeWords)
-					{
-						if (I)
-						{
-							if (IsSpace(Buf[I-1]) || IsEol(Buf[I-1]) ||
-							        (wcschr(Opt.strWordDiv,Buf[I-1])))
-								locResultLeft=TRUE;
-						}
-						else
-						{
-							locResultLeft=TRUE;
-						}
+		if (Dlg.GetExitCode()!=SD_BUTTON_OK)
+			return;
 
-						if (ReadSize!=BufSize && I+SearchLength>=ReadSize)
-							locResultRight=TRUE;
-						else if (I+SearchLength<ReadSize &&
-						         (IsSpace(Buf[I+SearchLength]) || IsEol(Buf[I+SearchLength]) ||
-						          (wcschr(Opt.strWordDiv,Buf[I+SearchLength]))))
-							locResultRight=TRUE;
-					}
-					else
-					{
-						locResultLeft=TRUE;
-						locResultRight=TRUE;
-					}
+		SearchHex=SearchDlg[SD_RADIO_HEX].Selected;
+		Case=SearchDlg[SD_CHECKBOX_CASE].Selected;
+		WholeWords=SearchDlg[SD_CHECKBOX_WORDS].Selected;
+		ReverseSearch=SearchDlg[SD_CHECKBOX_REVERSE].Selected;
+		SearchRegexp=SearchDlg[SD_CHECKBOX_REGEXP].Selected;
 
-					if (!SearchHex)
-					{
-						Match = locResultLeft && locResultRight && strSearchStr.At(0) == Buf[I] &&
-							( SearchLength==1 ||
-								( strSearchStr.At(1) == Buf[I+1] &&
-									(SearchLength==2 || !memcmp(strSearchStr.CPtr()+2,&Buf[I+2],(SearchLength-2)*sizeof(wchar_t)))
-								)
-							)
-						;
-					}
-					else
-					{
-						if ((Match = search_bytes[0] == byte_buff[I+0]) && SearchLength > 1)
-							if ((Match = search_bytes[1] == byte_buff[I+1]) && SearchLength > 2)
-								if ((Match = search_bytes[2] == byte_buff[I+2]) && SearchLength > 3)
-									if ((Match = search_bytes[3] == byte_buff[I+3]) && SearchLength > 4)
-										Match = !memcmp(search_bytes+4, byte_buff+I+4, SearchLength-4);
-					}
+		if (SearchHex)
+		{
+			strSearchStr = SearchDlg[SD_EDIT_HEX].strData;
+			int len = hex2ss(strSearchStr.CPtr(), search_bytes, ARRAYSIZE(search_bytes));
+			strSearchStr.Clear();
+			ss2hex(strSearchStr, search_bytes, len, L' ');
+		}
+		else
+		{
+			strSearchStr = SearchDlg[SD_EDIT_TEXT].strData;
+			size_t pos = 0;
+			while (strSearchStr.Pos(pos, 0xffff))
+				strSearchStr.Replace(pos, 1, L'\0');
+		}
+	}
 
-					if (Match)
+	LastSearchCase = Case;
+	LastSearchWholeWords = WholeWords;
+	LastSearchReverse = ReverseSearch;
+	LastSearchRegexp = SearchRegexp;
+
+	if (Next == -1)
+		ReverseSearch = !ReverseSearch;
+
+	strMsgStr = strLastSearchStr = strSearchStr;
+	if (strMsgStr.GetLength()+18 > static_cast<DWORD>(ObjWidth))
+		TruncStrFromEnd(strMsgStr, ObjWidth-18);
+
+	sd.search_len = (int)strSearchStr.GetLength();
+	if (0 != (LastSearchHex = SearchHex))
+	{
+		sd.search_len = hex2ss(strSearchStr.CPtr(), search_bytes, ARRAYSIZE(search_bytes));
+		sd.search_bytes = search_bytes;
+		sd.ch_size = 1;
+		Case = 1 + (WholeWords = SearchRegexp = 0);
+		searcher = (ReverseSearch ? &Viewer::search_hex_backward : &Viewer::search_hex_forward);
+	}
+	else
+	{
+		sd.is_utf8 = VM.CodePage == CP_UTF8;
+		sd.ch_size = getCharSize(VM.CodePage);
+		sd.search_text = strSearchStr.CPtr();
+
+		if ( SearchRegexp )
+		{
+			WholeWords = 0;
+			searcher = (ReverseSearch ? &Viewer::search_regex_backward : &Viewer::search_regex_forward);
+			InsertRegexpQuote(strMsgStr);
+			sd.pRex = new RegExp;
+			string strSlash = strSearchStr;
+			InsertRegexpQuote(strSlash);
+			if ( !sd.pRex->Compile(strSlash, OP_PERLSTYLE | OP_OPTIMIZE | (Case ? 0 : OP_IGNORECASE)) )
+				return; // wrong regular expression...
+		}
+		else
+		{
+			searcher = (ReverseSearch ? &Viewer::search_text_backward : &Viewer::search_text_forward);
+			InsertQuote(strMsgStr);
+		}
+	}
+		
+	if (!Case && !SearchRegexp)
+	{
+		strSearchStr.Upper();
+		sd.search_text = strSearchStr.CPtr();
+	}
+
+	int found = 0;
+
+	int search_direction = ReverseSearch ? -1 : +1;
+	switch (Next)
+	{
+		case 2:
+			StartSearchPos = LastSelectPos = (ReverseSearch ? FileSize : 0);
+		break;
+		case +1: case -1:
+			if ( SelectPos >= 0 && LastSelectSize >= 0 )
+			{
+				if (sd.ch_size >= 1)
+					LastSelectPos = SelectPos + (ReverseSearch ? LastSelectSize-sd.ch_size : sd.ch_size);
+				else
+				{
+					INT64 prev_pos = SelectPos;
+					vseek(SelectPos, SEEK_SET);
+					for (;;)
 					{
-						MatchPos = CurPos + I / seek_size;
-						if (t_utf8)
+						wchar_t ch;
+						bool ok_getc = vgetc(&ch);
+						LastSelectPos = vtell();
+						if (!ReverseSearch || !ok_getc)
+							break;
+						if ( LastSelectPos >= SelectPos + LastSelectSize )
 						{
-							MatchPos = CurPos + GetStrBytesNum(t_Buf, I);
-							SearchLength = GetStrBytesNum(t_Buf+I, SearchLength);
+							LastSelectPos = prev_pos;
+							break;
 						}
-						break;
+						prev_pos = LastSelectPos;
 					}
 				}
+				if (search_direction != LastSearchDirection)
+					StartSearchPos = LastSelectPos;
+				else if ( LastSelectPos == StartSearchPos ) // ������ �
+					found = -1;									  // ������������
 
-				if ((ReverseSearch && CurPos <= 0) || (!ReverseSearch && veof()))
-					break;
+				break;
+			} // else pass to case 0 (below)
+		case 0:
+			LastSelectSize = SelectSize = -1;
+			StartSearchPos = LastSelectPos = (ReverseSearch ? EndOfScreen(0) : BegOfScreen());
+		break;
+	}
+	LastSearchDirection = search_direction;
 
-				int asz = buf_size;
-				if (t_utf8)
-					asz = GetStrBytesNum(t_Buf, ReadSize);
+	if (!sd.search_len || (__int64)sd.search_len > FileSize)
+		return;
 
-				if (ReverseSearch)
+	sd.CurPos = LastSelectPos;
+	if ( !found )
+	{
+		TPreRedrawFuncGuard preRedrawFuncGuard(PR_ViewerSearchMsg);
+		SetCursorType(FALSE,0);
+
+		DWORD start_time = GetTickCount();
+		for (;;)
+		{
+			found = (this->*searcher)(&sd);
+			if ( found != 0 )
+				break;
+
+			DWORD cur_time = GetTickCount();
+			if ( cur_time - start_time > RedrawTimeout )
+			{
+				start_time = cur_time;
+
+				if (CheckForEscSilent())
 				{
-					/* $ 01.08.2000 KM
-					   ������� �������� CurPos � ���� Whole words
-					*/
-					if (WholeWords)
-						CurPos -= asz/seek_size - SearchLength/seek_size + 1;
-					else
-						CurPos -= asz/seek_size - SearchLength/seek_size;
+					if (ConfirmAbortOp())
+					{
+						Redraw();
+						return;
+					}
 				}
-				else
+
+				int percent = -1;
+				INT64 done, total = FileSize;
+				if ( total > 0 )
 				{
-					if (WholeWords)
-						CurPos += asz/seek_size - SearchLength/seek_size + 1;
+					if ( !ReverseSearch )
+					{
+						if ( sd.CurPos >= StartSearchPos )
+							done = sd.CurPos - StartSearchPos;
+						else
+							done = (FileSize - StartSearchPos) + sd.CurPos;
+					}
 					else
-						CurPos += asz/seek_size - SearchLength/seek_size;
+					{
+						if ( sd.CurPos <= StartSearchPos )
+							done = StartSearchPos - sd.CurPos;
+						else
+							done = StartSearchPos + (FileSize - sd.CurPos);
+					}
+					percent = static_cast<int>(done*100/total);
 				}
+				ViewerSearchMsg(strMsgStr, percent, SearchHex);
 			}
-
-			delete[] Buf;
 		}
 	}
 
-	if (Match)
+	if ( sd.MatchPos >= 0 )
 	{
-		/* $ 24.01.2003 KM
-		   ! �� �������� ������ �������� �� ���� ������ ��
-		     ���� ������������ ����.
-		*/
-		SelectText(MatchPos,SearchLength,ReverseSearch?0x2:0);
-		// ������� ��������� �� ��������� ����� ������ �� ����.
+		SelectText(sd.MatchPos, sd.search_len, ReverseSearch?0x2:0);
+		LastSelectSize = SelectSize;
+
+		// ������� ��������� �� ��������� �������� ������ �� ����.
 		int FromTop=(ScrY-(Opt.ViOpt.ShowKeyBar?2:1))/4;
 
 		if (FromTop<0 || FromTop>ScrY)
 			FromTop=0;
 
-		for (int i=0; i<FromTop; i++)
-			Up();
+		Up(FromTop);
 
 		AdjustSelPosition = TRUE;
 		Show();
@@ -2598,24 +3264,13 @@
 	}
 	else
 	{
-		//Show();
-		/* $ 27.01.2003 VVM
-		   + ����� ������� ������ ������� � ������� ������ � �����/���� */
-		if (SearchFlags.Check(SEARCH_MODE2))
-		{
-			Message(MSG_WARNING,1,MSG(MViewSearchTitle),
-			        (SearchHex?MSG(MViewSearchCannotFindHex):MSG(MViewSearchCannotFind)),strMsgStr,MSG(MOk));
-		}
-		else
-		{
-			if (!Message(MSG_WARNING,2,MSG(MViewSearchTitle),
-			            (SearchHex?MSG(MViewSearchCannotFindHex):MSG(MViewSearchCannotFind)),strMsgStr,
-			            (ReverseSearch?MSG(MViewSearchFromEnd):MSG(MViewSearchFromBegin)),
-			            MSG(MHYes),MSG(MHNo)))
-			{
-				Search(2,0);
-			}
-		}
+		Message(
+			MSG_WARNING, 1,
+			MSG(MViewSearchTitle),
+			(SearchHex ? MSG(MViewSearchCannotFindHex) : MSG(MViewSearchCannotFind)),
+			strMsgStr,
+			MSG(MOk)
+		);
 	}
 }
 
@@ -2625,37 +3280,31 @@
 	return(VM.Wrap);
 }
 
-
 void Viewer::SetWrapMode(int Wrap)
 {
 	Viewer::VM.Wrap=Wrap;
 }
 
-
 void Viewer::EnableHideCursor(int HideCursor)
 {
 	Viewer::HideCursor=HideCursor;
 }
 
-
 int Viewer::GetWrapType()
 {
 	return(VM.WordWrap);
 }
 
-
 void Viewer::SetWrapType(int TypeWrap)
 {
 	Viewer::VM.WordWrap=TypeWrap;
 }
 
-
 void Viewer::GetFileName(string &strName)
 {
 	strName = strFullFileName;
 }
 
-
 void Viewer::ShowConsoleTitle()
 {
 	string strTitle;
@@ -2663,7 +3312,6 @@
 	ConsoleTitle::SetFarTitle(strTitle);
 }
 
-
 void Viewer::SetTempViewName(const wchar_t *Name, BOOL DeleteFolder)
 {
 	if (Name && *Name)
@@ -2677,7 +3325,6 @@
 	Viewer::DeleteFolder=DeleteFolder;
 }
 
-
 void Viewer::SetTitle(const wchar_t *Title)
 {
 	if (!Title)
@@ -2686,19 +3333,16 @@
 		strTitle = Title;
 }
 
-
 void Viewer::SetFilePos(__int64 Pos)
 {
 	FilePos=Pos;
 };
 
-
 void Viewer::SetPluginData(const wchar_t *PluginData)
 {
 	Viewer::strPluginData = NullToEmpty(PluginData);
 }
 
-
 void Viewer::SetNamesList(NamesList *List)
 {
 	if (List)
@@ -2825,10 +3469,15 @@
 
 	if (IsUnicodeCodePage(VM.CodePage))
 	{
-		Reader.Read(Buf, Count*2, &ReadSize);
+		int rev = (CP_REVERSEBOM == VM.CodePage ? 1 : 0);
+
+		Reader.Read(Buf, Count, &ReadSize);
 		if (0 != (ReadSize & 1))
-			((char *)Buf)[ReadSize++] = '\0';
-
+		{
+			((char *)Buf)[ReadSize-1+rev] = (char)(REPLACE_CHAR & 0xff);
+			((char *)Buf)[ReadSize-0-rev] = (char)(REPLACE_CHAR >> 8);
+			++ReadSize;
+		}
 		if (CP_REVERSEBOM == VM.CodePage)
 		{
 			for (DWORD i=0; i<ReadSize; i+=2)
@@ -2838,7 +3487,6 @@
 				((char *)Buf)[i+1] = t;
 			}
 		}
-
 		ReadSize /= 2;
 	}
 	else
@@ -2871,15 +3519,11 @@
 
 bool Viewer::vseek(__int64 Offset, int Whence)
 {
-	int seek_size = IsUnicodeCodePage(VM.CodePage) ? 2 : 1;
-	Offset *= seek_size;
-
 	if (FILE_CURRENT == Whence)
 	{
 		if (vgetc_ready)
 		{
 			int tail = vgetc_cb - vgetc_ib;
-			tail += (seek_size - 1) * (tail & 1);
 			Offset += tail;
 			vgetc_ready = false;
 		}
@@ -2898,9 +3542,6 @@
 	if (vgetc_ready)
 		Ptr -= (vgetc_cb - vgetc_ib);
 
-	if (IsUnicodeCodePage(VM.CodePage))
-		Ptr=(Ptr+(Ptr&1))/2;
-
 	return Ptr;
 }
 
@@ -2927,9 +3568,6 @@
 		DWORD nr = 0;
 		Reader.Read(vgetc_buffer + vgetc_cb, (DWORD)(ARRAYSIZE(vgetc_buffer)-vgetc_cb), &nr);
 		vgetc_cb += (int)nr;
-
-		if (0 != (vgetc_cb & 1) && IsUnicodeCodePage(VM.CodePage))
-			vgetc_buffer[vgetc_cb++] = 0;
 	}
 
 	vgetc_ready = true;
@@ -2951,11 +3589,17 @@
 	switch (VM.CodePage)
 	{
 		case CP_REVERSEBOM:
-			*pCh = (wchar_t)((vgetc_buffer[vgetc_ib] << 8) | vgetc_buffer[vgetc_ib+1]);
+			if (vgetc_ib == vgetc_cb-1)
+				*pCh = REPLACE_CHAR;
+			else
+				*pCh = (wchar_t)((vgetc_buffer[vgetc_ib] << 8) | vgetc_buffer[vgetc_ib+1]);
 			vgetc_ib += 2;
 		break;
 		case CP_UNICODE:
-			*pCh = (wchar_t)((vgetc_buffer[vgetc_ib+1] << 8) | vgetc_buffer[vgetc_ib]);
+			if (vgetc_ib == vgetc_cb-1)
+				*pCh = REPLACE_CHAR;
+			else
+				*pCh = (wchar_t)((vgetc_buffer[vgetc_ib+1] << 8) | vgetc_buffer[vgetc_ib]);
 			vgetc_ib += 2;
 		break;
 		case CP_UTF8:
@@ -2978,6 +3622,54 @@
 	return true;
 }
 
+wchar_t Viewer::vgetc_prev()
+{
+	INT64 pos = vtell();
+	if ( pos <= 0 )
+		return L'\0';
+
+	int ch_size = getCharSize(VM.CodePage);
+	if ( pos < ch_size )
+		return REPLACE_CHAR;
+
+	int nb = (ch_size >= 1 ? ch_size : (pos > 4 ? 4 : static_cast<int>(pos)));
+	DWORD nr = 0;
+
+	char ss[4];
+	if ( vseek(-nb, SEEK_CUR) )
+		 Reader.Read(ss, static_cast<DWORD>(nb), &nr);
+
+	vseek(pos, SEEK_SET);
+	
+	wchar_t ch = REPLACE_CHAR;
+	if ( static_cast<int>(nr) == nb )
+	{
+		switch ( VM.CodePage )
+		{
+			case CP_REVERSEBOM:
+				ch = MAKEWORD(ss[1], ss[0]);
+			break;
+			case CP_UNICODE:
+				ch = MAKEWORD(ss[0], ss[1]);
+			break;
+			case CP_UTF8:
+			{
+				int tail = 0;
+				wchar_t w[4];
+				int nw = utf8_to_WideChar(ss, nb, w,nullptr, 4,tail);
+				if ( !tail && nw > 0 )
+					ch = w[nw-1];
+				break;
+			}
+			default:
+				MultiByteToWideChar(VM.CodePage, 0, (LPCSTR)ss,1, &ch,1);
+			break;
+		}
+	}
+	return ch;
+}
+
+
 #define RB_PRC 3
 #define RB_HEX 4
 #define RB_DEC 5
@@ -3060,9 +3752,6 @@
 				//  Percent=0;
 				Offset=FileSize/100*Percent;
 
-				if (IsUnicodeCodePage(VM.CodePage))
-					Offset*=2;
-
 				while (ToPercent64(Offset,FileSize)<Percent)
 					Offset++;
 			}
@@ -3094,31 +3783,16 @@
 				//  Percent=0;
 				Offset=FileSize/100*Percent;
 
-				if (IsUnicodeCodePage(VM.CodePage))
-					Offset*=2;
-
 				while (ToPercent64(Offset,FileSize)<Percent)
 					Offset++;
 			}
 		}
 
-		if (Relative)
-		{
-			if (Relative==-1 && Offset>FilePos)   // ���� ���, if (FilePos<0) �� ������� - FilePos � ��� unsigned long
-				FilePos=0;
-			else
-				FilePos=IsUnicodeCodePage(VM.CodePage)? FilePos+Offset*Relative/2 : FilePos+Offset*Relative;
-		}
-		else
-			FilePos=IsUnicodeCodePage(VM.CodePage) ? Offset/2:Offset;
-
-		if (FilePos>FileSize || FilePos<0)     // � ���� ��� �����?
-			FilePos=FileSize;     // ��� ��� ����� ����� ����
+		FilePos = (Relative ? FilePos + Offset*Relative : Offset);
+		FilePos = (FilePos < 0 ? 0 : (FilePos > FileSize ? FileSize : FilePos));
 	}
-	// �������
 	AdjustFilePos();
 
-//  LastSelPos=FilePos;
 	if (!(Flags&VSP_NOREDRAW))
 		Show();
 }
@@ -3129,6 +3803,9 @@
 
 	if (!VM.Hex)
 	{
+		int ch_size = getCharSize(VM.CodePage);
+		FilePos -= FilePos % ch_size;
+
 		vseek(FilePos, SEEK_SET);
 		if (VM.CodePage != CP_UTF8)
 		{
@@ -3140,7 +3817,7 @@
 			if (vgetc_ib < vgetc_cb)
 			{
 				if (0x80 == (vgetc_buffer[vgetc_ib] & 0xC0))
-		{
+				{
 					if (++vgetc_ib < vgetc_cb)
 					{
 						if (0x80 == (vgetc_buffer[vgetc_ib] & 0xC0))
@@ -3152,18 +3829,18 @@
 							}
 						}
 					}
-		}
+				}
 				else
-		{
+				{
 					vgetc(&ch);
 				}
 			}
 		}
 
-		   FilePos = vtell();
-			Up();
-		}
+		FilePos = vtell();
+		Up(1);
 	}
+}
 
 void Viewer::SetFileSize()
 {
@@ -3173,13 +3850,6 @@
 	UINT64 uFileSize=0; // BUGBUG, sign
 	ViewFile.GetSize(uFileSize);
 	FileSize=uFileSize;
-
-	/* $ 20.02.2003 IS
-	   ����� ���������� FilePos � FileSize, FilePos �� ������� ������
-	   �������� � ��� ����, ������ FileSize ���� ���� ������
-	*/
-	if (IsUnicodeCodePage(VM.CodePage))
-		FileSize=(FileSize+(FileSize&1))/2;
 }
 
 
@@ -3190,85 +3860,50 @@
 	Flags=SelectFlags;
 }
 
-/* $ 19.01.2001 SVS
-   �������� - � ������� ������������� ������.
-   Flags=0x01 - �������� (����� Show())
-         0x02 - "������� �����" ?
-*/
-void Viewer::SelectText(const __int64 &MatchPos,const __int64 &SearchLength, const DWORD Flags)
+// Flags=0x01 - �������� [����� Show()]
+//       0x02 - "������� �����" ?
+//
+void Viewer::SelectText(const __int64 &match_pos,const __int64 &search_len, const DWORD flags)
 {
 	if (!ViewFile.Opened())
 		return;
 
-	int buff_size = 1024;
-	wchar_t *Buf = Up_buffer, *tBuf = (!VM.Hex && VM.CodePage == CP_UTF8) ? Buf + buff_size : nullptr;
-	__int64 StartLinePos = -1, SearchLinePos = MatchPos - buff_size;
-	if (SearchLinePos < 0)
-	{
-		buff_size += (int)SearchLinePos;
-		SearchLinePos = 0;
-	}
+	SelectPos = match_pos;
+	SelectSize = search_len;
+	SelectFlags = flags;
+	if ( SelectSize < 0 )
+		return;
 
-	vseek(SearchLinePos, SEEK_SET);
-	int ReadSize = vread(Buf, buff_size, tBuf);
-
-	for (int I=ReadSize-1; I >= 0; I--)
-	if (Buf[I]==L'\n' || Buf[I]==L'\r')
+	if ( VM.Hex )
 	{
-		if (tBuf)
-			StartLinePos = MatchPos - GetStrBytesNum(tBuf+I, ReadSize-I);
-		else
-			StartLinePos = SearchLinePos+I;
-		break;
+		 FilePos = (FilePos & 0xf) | (SelectPos & ~0xf);
+		 FilePos = (FilePos < SelectPos ? FilePos : (FilePos > 16 ? FilePos-16 : 0));
 	}
-
-	vseek(MatchPos+1, SEEK_SET);
-	SelectPos = FilePos=MatchPos;
-	SelectSize = SearchLength;
-	SelectFlags = Flags;
-
-//  LastSelPos=SelectPos+((Flags&0x2) ? -1:1);
-	if (VM.Hex)
-	{
-		FilePos&=~(IsUnicodeCodePage(VM.CodePage) ? 0x7:0xf);
-	}
 	else
 	{
-		if (SelectPos != StartLinePos)
+		FilePos = SelectPos;
+		Up(1);
+		LeftPos = 0;
+
+		if ( !VM.Wrap )
 		{
-			Up();
-			Show();  //update OutStr
-		}
+			vseek(vString.nFilePos = FilePos, SEEK_SET);
+			vString.lpData[0] = L'\0';
+			ReadString(&vString, (int)(SelectPos-FilePos));
 
-		/* $ 13.03.2001 IS
-		   ���� ��������� ����������� � ����� ������ ������ ���������� ����� � ����
-		   ����� � ����� fffe ��� feff, �� �� ����� ���������� �������, ���
-		   ����� ����� ������ �� ������ (��-�� ����, ��� ������ ������ ��
-		   ����������)
-		*/
-		SelectPosOffSet=(IsUnicodeOrUtfCodePage(VM.CodePage) && Signature
-		                 && (MatchPos+SelectSize<=ObjWidth && MatchPos<(__int64)StrLength(Strings[0]->lpData)))?1:0;
-		SelectPos-=SelectPosOffSet;
-		__int64 Length=SelectPos-StartLinePos-1;
+			wchar_t first_found_char = L'\0';
+			vgetc(&first_found_char);
 
-		if (VM.Wrap)
-			Length%=Width+1; //??
-
-		if (Length<=Width)
-			LeftPos=0;
-
-		if (Length-LeftPos>Width || Length<LeftPos)
-		{
-			LeftPos=Length;
-
-			if (LeftPos>(MAX_VIEWLINE-1) || LeftPos<0)
-				LeftPos=0;
-			else if (LeftPos>10)
-				LeftPos-=10;
+			if ( L'\r' != first_found_char && L'\n' != first_found_char )
+			{
+				int found_offset = (int)wcslen(vString.lpData);
+				if ( found_offset > Width-10 )
+					LeftPos = (Width <= 10 ? found_offset : found_offset + 10 - Width);
+			}
 		}
 	}
 
-	if (Flags&1)
+	if (0 != (flags & 1))
 	{
 		AdjustSelPosition = TRUE;
 		Show();
@@ -3276,6 +3911,7 @@
 	}
 }
 
+
 int Viewer::ViewerControl(int Command,void *Param)
 {
 	switch (Command)
@@ -3365,7 +4001,7 @@
 			}
 			else
 			{
-				SelectSize = 0;
+				SelectSize = -1;
 				Show();
 			}
 
@@ -3476,7 +4112,6 @@
 		Show();
 	}
 
-// LastSelPos=FilePos;
 	return oldHex;
 }
 
@@ -3488,7 +4123,7 @@
 	if (VM.Wrap)
 		LeftPos = 0;
 
-	if (!VM.Wrap)
+	if (!VM.Hex)
 		AdjustFilePos();
 
 	if (isRedraw)
@@ -3498,7 +4133,6 @@
 	}
 
 	Opt.ViOpt.ViewerIsWrap=VM.Wrap;
-//  LastSelPos=FilePos;
 	return oldWrap;
 }
 
@@ -3513,6 +4147,9 @@
 		LeftPos = 0;
 	}
 
+	if (!VM.Hex)
+		AdjustFilePos();
+
 	if (isRedraw)
 	{
 		ChangeViewKeyBar();
@@ -3520,6 +4157,5 @@
 	}
 
 	Opt.ViOpt.ViewerWrap=VM.WordWrap;
-//LastSelPos=FilePos;
 	return oldTypeWrap;
 }
Index: config.hpp
===================================================================
--- config.hpp	(revision 6286)
+++ config.hpp	(working copy)
@@ -314,6 +314,8 @@
 	int AnsiCodePageAsDefault;
 	int ShowTitleBar;
 	int SearchRegexp;
+	int MaxLineSize; // 80..16384, default=2048
+	int SearchEditFocus; // auto-focus on edit text/hex window
 };
 
 // "�����"
Index: config.cpp
===================================================================
--- config.cpp	(revision 6286)
+++ config.cpp	(working copy)
@@ -595,6 +595,9 @@
 	{1, GeneralConfig::TYPE_INTEGER, NKeyViewer,L"PersistentBlocks",&Opt.ViOpt.PersistentBlocks,0, 0},
 	{1, GeneralConfig::TYPE_INTEGER, NKeyViewer,L"AnsiCodePageAsDefault",&Opt.ViOpt.AnsiCodePageAsDefault,1, 0},
 
+	{1, GeneralConfig::TYPE_INTEGER, NKeyViewer,L"MaxLineSize",&Opt.ViOpt.MaxLineSize,2048, 0},
+	{1, GeneralConfig::TYPE_INTEGER, NKeyViewer,L"SearchEditFocus",&Opt.ViOpt.SearchEditFocus,0, 0},
+
 	{1, GeneralConfig::TYPE_INTEGER, NKeyDialog, L"EditHistory",&Opt.Dialogs.EditHistory,1, 0},
 	{1, GeneralConfig::TYPE_INTEGER, NKeyDialog, L"EditBlock",&Opt.Dialogs.EditBlock,0, 0},
 	{1, GeneralConfig::TYPE_INTEGER, NKeyDialog, L"AutoComplete",&Opt.Dialogs.AutoComplete,1, 0},
@@ -920,8 +923,15 @@
 		}
 	}
 
-	Opt.ViOpt.ViewerIsWrap&=1;
-	Opt.ViOpt.ViewerWrap&=1;
+	Opt.ViOpt.ViewerIsWrap &= 1;
+	Opt.ViOpt.ViewerWrap &= 1;
+	if (!Opt.ViOpt.MaxLineSize)
+		Opt.ViOpt.MaxLineSize = 2048;
+	else if (Opt.ViOpt.MaxLineSize < 80)
+		Opt.ViOpt.MaxLineSize = 80;
+	else if (Opt.ViOpt.MaxLineSize > 16*1024)
+		Opt.ViOpt.MaxLineSize = 16*1024;
+	Opt.ViOpt.SearchEditFocus &= 1;
 
 	// ������� �������� �������� ������������ ;-)
 	if (Opt.strWordDiv.IsEmpty())
NewViewer3.diff (93,452 bytes)   

vskirdin

2011-05-26 14:18

administrator   bugnote:0007069

а че за исправления?
+ куда то пропала строка
#include "RegExp.hpp"

vskirdin

2011-05-26 14:21

administrator   bugnote:0007070

http://skirda-file.googlecode.com/files/Viewer-new.3.7z

vskirdin

2011-05-26 14:24

administrator   bugnote:0007072

Так, если все устраивает тестовый вариант, то перед коммитом хотелось бы увидеть строки для changelog - что значит "новый вьювер" :-)

2useven10

2011-05-26 14:43

developer   bugnote:0007074

скобка была не на месте.

новый...
Внутренности:
полностью переделан поиск
добавлен механизм кеширования переводов строк
юникодные файлы теперь как все прочие (нет размеру в символах!)
переделан HexView
переработан диалог поиска

Функции:
добавлен поиск регулярных выражений (построчный в обе стороны)
повторный поиск всегда зациклен до места начала поиска или смены направления
добавлена возможность изменения максимальной длины строки (параметр)

ну и порядка 30% кода переписано...

vskirdin

2011-05-26 15:11

administrator   bugnote:0007075

> добавлена возможность изменения максимальной длины строки (параметр)
Это где?
Опция доступна простым смертным? (из интерфейса, или она столь специфичная, что для большинства случаев хватит 2048?)
Если нет, то в FARConfig нужно нули выставить для "MaxLineSize" и "SearchEditFocus":

{0, GeneralConfig::TYPE_INTEGER, NKeyViewer,L"MaxLineSize",&Opt.ViOpt.MaxLineSize,2048, 0},
{0, GeneralConfig::TYPE_INTEGER, NKeyViewer,L"SearchEditFocus",&Opt.ViOpt.SearchEditFocus,0, 0},

2useven10

2011-05-26 15:29

developer   bugnote:0007076

в интерфейсе нет. 2048 - текущее значение. при нуле тоже будет 2048. в конфиг добавлен код:
    if (!Opt.ViOpt.MaxLineSize)
        Opt.ViOpt.MaxLineSize = 2048;
    else if (Opt.ViOpt.MaxLineSize < 80)
        Opt.ViOpt.MaxLineSize = 80;
    else if (Opt.ViOpt.MaxLineSize > 16*1024)
        Opt.ViOpt.MaxLineSize = 16*1024;

vskirdin

2011-05-26 15:50

administrator   bugnote:0007077

build 2054

DrKnS

2011-05-27 06:46

administrator   bugnote:0007083

> 2048 - текущее значение

А почему бы не сделать дефолтное значение побольше, например раз в 10?

2useven10

2011-05-27 07:36

developer   bugnote:0007086

пока ограничено 16k (16k*2 - это половина размера кэша CacheRead, можно увеличить и то и другое). если длинные строки нужны всем - не проблема, я просто по дефолту оставлял текущее поведение.

2useven10

2011-06-01 11:25

developer  

ViewerLongLineFix.diff (7,088 bytes)   
Index: config.hpp
===================================================================
--- config.hpp	(revision 6306)
+++ config.hpp	(working copy)
@@ -300,6 +300,13 @@
 */
 struct ViewerOptions
 {
+	enum EViewerLineSize
+	{
+		eMinLineSize = 100,
+		eDefLineSize = 10*1000,
+		eMaxLineSize = 100*1000
+	};
+
 	int TabSize;
 	int AutoDetectCodePage;
 	int ShowScrollbar;     // $ 18.07.2000 tran ���� �������� �� viewer
@@ -314,7 +321,7 @@
 	int AnsiCodePageAsDefault;
 	int ShowTitleBar;
 	int SearchRegexp;
-	int MaxLineSize; // 80..16384, default=2048
+	int MaxLineSize; // 100..100000, default=10000
 	int SearchEditFocus; // auto-focus on edit text/hex window
 };
 
Index: config.cpp
===================================================================
--- config.cpp	(revision 6306)
+++ config.cpp	(working copy)
@@ -595,7 +595,7 @@
 	{1, GeneralConfig::TYPE_INTEGER, NKeyViewer,L"PersistentBlocks",&Opt.ViOpt.PersistentBlocks,0, 0},
 	{1, GeneralConfig::TYPE_INTEGER, NKeyViewer,L"AnsiCodePageAsDefault",&Opt.ViOpt.AnsiCodePageAsDefault,1, 0},
 
-	{0, GeneralConfig::TYPE_INTEGER, NKeyViewer,L"MaxLineSize",&Opt.ViOpt.MaxLineSize,2048, 0},
+	{0, GeneralConfig::TYPE_INTEGER, NKeyViewer,L"MaxLineSize",&Opt.ViOpt.MaxLineSize,ViewerOptions::eDefLineSize, 0},
 	{0, GeneralConfig::TYPE_INTEGER, NKeyViewer,L"SearchEditFocus",&Opt.ViOpt.SearchEditFocus,0, 0},
 
 	{1, GeneralConfig::TYPE_INTEGER, NKeyDialog, L"EditHistory",&Opt.Dialogs.EditHistory,1, 0},
@@ -926,11 +926,11 @@
 	Opt.ViOpt.ViewerIsWrap &= 1;
 	Opt.ViOpt.ViewerWrap &= 1;
 	if (!Opt.ViOpt.MaxLineSize)
-		Opt.ViOpt.MaxLineSize = 2048;
-	else if (Opt.ViOpt.MaxLineSize < 80)
-		Opt.ViOpt.MaxLineSize = 80;
-	else if (Opt.ViOpt.MaxLineSize > 16*1024)
-		Opt.ViOpt.MaxLineSize = 16*1024;
+		Opt.ViOpt.MaxLineSize = ViewerOptions::eDefLineSize;
+	else if (Opt.ViOpt.MaxLineSize < ViewerOptions::eMinLineSize)
+		Opt.ViOpt.MaxLineSize = ViewerOptions::eMinLineSize;
+	else if (Opt.ViOpt.MaxLineSize > ViewerOptions::eMaxLineSize)
+		Opt.ViOpt.MaxLineSize = ViewerOptions::eMaxLineSize;
 	Opt.ViOpt.SearchEditFocus &= 1;
 
 	// ������� �������� �������� ������������ ;-)
Index: cache.hpp
===================================================================
--- cache.hpp	(revision 6306)
+++ cache.hpp	(working copy)
@@ -35,7 +35,7 @@
 class CachedRead
 {
 public:
-	CachedRead(File& file);
+	CachedRead(File& file, DWORD buffer_size=0);
 	~CachedRead();
 	bool AdjustAlignment(); // file have to be opened already
 	bool Read(LPVOID Data, DWORD DataSize, LPDWORD BytesRead);
Index: cache.cpp
===================================================================
--- cache.cpp	(revision 6306)
+++ cache.cpp	(working copy)
@@ -35,22 +35,21 @@
 
 #include "cache.hpp"
 
-CachedRead::CachedRead(File& file):
-	Buffer(static_cast<LPBYTE>(xf_malloc(DefaultBufferSize))),
+CachedRead::CachedRead(File& file, DWORD buffer_size):
 	file(file),
 	ReadSize(0),
 	BytesLeft(0),
 	LastPtr(0),
 	BufferSize(DefaultBufferSize),
 	Alignment(512)
-{}
+{
+	BufferSize = (buffer_size ? (buffer_size+Alignment-1) & ~(Alignment-1) : DefaultBufferSize);
+	Buffer = static_cast<LPBYTE>(xf_malloc(BufferSize));
+}
 
 CachedRead::~CachedRead()
 {
-	if (Buffer)
-	{
-		xf_free(Buffer);
-	}
+	xf_free(Buffer);
 }
 
 bool CachedRead::AdjustAlignment()
Index: viewer.hpp
===================================================================
--- viewer.hpp	(revision 6306)
+++ viewer.hpp	(working copy)
@@ -159,6 +159,10 @@
 		int      lcache_wwrap;
 		int      lcache_width;
 
+      int		max_backward_size;
+		int      llengths_size;
+		int     *llengths;
+
 		wchar_t *Search_buffer;
 		int Search_buffer_size;
 
Index: viewer.cpp
===================================================================
--- viewer.cpp	(revision 6306)
+++ viewer.cpp	(working copy)
@@ -82,7 +82,7 @@
 
 Viewer::Viewer(bool bQuickView, UINT aCodePage):
 	ViOpt(Opt.ViOpt),
-	Reader(ViewFile),
+	Reader(ViewFile, (Opt.ViOpt.MaxLineSize*2*64 > 64*1024 ? Opt.ViOpt.MaxLineSize*2*64 : 64*1024)),
 	m_bQuickView(bQuickView)
 {
 	_OT(SysLog(L"[%p] Viewer::Viewer()", this));
@@ -135,15 +135,23 @@
 	vgetc_cb = vgetc_ib = 0;
 	vgetc_composite = L'\0';
 
-	vread_buffer = new char[vread_buffer_size = 8192];
+	vread_buffer_size = (MAX_VIEWLINEB < 8192 ? 8192 : MAX_VIEWLINEB);
+	vread_buffer = new char[vread_buffer_size];
 
 	lcache_first = lcache_last = -1;
-	lcache_lines = new INT64[lcache_size = 8*1000];
+	lcache_lines = new INT64[lcache_size = 16*1000];
 	lcache_count = 0;
 	lcache_base = 0;
 	lcache_ready = false;
 	lcache_wrap = lcache_wwrap = lcache_width = -1;
 
+   int cached_buffer_size = (Opt.ViOpt.MaxLineSize*2*64 > 64*1024 ? Opt.ViOpt.MaxLineSize*2*64 : 64*1024);
+	max_backward_size = ViewerOptions::eMaxLineSize*3;
+	if ( max_backward_size > cached_buffer_size/2 )
+		max_backward_size = cached_buffer_size / 2;
+	llengths_size = max_backward_size / 40;
+	llengths = new int[llengths_size];
+
 	Search_buffer_size = 3 * (MAX_VIEWLINEB < 8000 ? 8000 : MAX_VIEWLINEB);
 	Search_buffer = new wchar_t[Search_buffer_size];
 
@@ -183,6 +191,7 @@
 
 	delete[] vString.lpData;
 	delete[] Search_buffer;
+	delete[] llengths;
 	delete[] lcache_lines;
 	delete[] vread_buffer;
 
@@ -2003,8 +2012,7 @@
 		}
 	}
 
-	static const int portion_size = 256;
-	static const int max_backward = 32*1024;
+	const int portion_size = 256;
 
 	union {
 		char c1[portion_size];
@@ -2012,7 +2020,6 @@
 	} buff;
 
 	int j, buff_size, nr, ch_size = getCharSize(VM.CodePage);
-	int len[max_backward/40]; // suppose minimum line with is 80 chars...
 
 	while ( nlines > 0 )
 	{
@@ -2026,7 +2033,7 @@
 
 		// backward CR-LF search
 		//
-		for ( j = 0; j < max_backward/portion_size; ++j )
+		for ( j = 0; j < max_backward_size/portion_size; ++j )
 		{
 			buff_size = (fpos > (__int64)portion_size ? portion_size : (int)fpos);
 			fpos -= buff_size;
@@ -2096,23 +2103,23 @@
 		// split read portion
 		//
 		vseek(vString.nFilePos = fpos, SEEK_SET);
-		for (i = 0; i < (int)ARRAYSIZE(len); ++i)
+		for (i = 0; i < llengths_size; ++i)
 		{
 			ReadString(&vString, -1, false);
-			len[i] = (vString.have_eol ? -1 : +1) * vString.linesize;
+			llengths[i] = (vString.have_eol ? -1 : +1) * vString.linesize;
 			if ((vString.nFilePos += vString.linesize) >= fpos1)
 			{
 				fpos1 = vString.nFilePos;
 				break;
 			}
 		}
-		assert(i < (int)ARRAYSIZE(len));
-		if (i >= (int)ARRAYSIZE(len))
+		assert(i < llengths_size);
+		if (i >= llengths_size)
 			--i;
 
 		while ( i >= 0 )
 		{
-			int l = len[i--];
+			int l = llengths[i--];
 			bool eol = false;
 			if (l < 0)
 			{
@@ -3889,7 +3896,7 @@
 		{
 			vseek(vString.nFilePos = FilePos, SEEK_SET);
 			vString.lpData[0] = L'\0';
-			ReadString(&vString, (int)(SelectPos-FilePos));
+			ReadString(&vString, (int)(SelectPos-FilePos), false);
 
 			wchar_t first_found_char = L'\0';
 			vgetc(&first_found_char);
ViewerLongLineFix.diff (7,088 bytes)   

2useven10

2011-06-01 11:27

developer   bugnote:0007116

fix для длинных строк (поправлены ошибки + MaxLineSize: Min=100, Def=10000, Max=100000)

vskirdin

2011-06-03 06:16

administrator   bugnote:0007121

build 2061

2useven10

2011-06-28 14:47

developer  

NewViewer-fix2.diff (831 bytes)   
Index: viewer.cpp
===================================================================
--- viewer.cpp	(revision 6365)
+++ viewer.cpp	(working copy)
@@ -254,6 +254,7 @@
 
 	ViewFile.Close();
 	Reader.Clear();
+	vgetc_ready = lcache_ready = false;
 
 	SelectSize = -1; // ������� ��������
 	strFileName = Name;
@@ -3965,12 +3966,7 @@
 				if ((LeftPos=vsp->LeftPos) < 0)
 					LeftPos=0;
 
-				/* $ 20.01.2003 IS
-				     ���� ��������� - ������, �� ��������� �������, ��������� �
-				     2 ����. ������ ������� StartPos � 2 ����, �.�. �����
-				     GoTo ��������� ������ � _�����_.
-				*/
-				GoTo(FALSE, vsp->StartPos*(IsUnicodeCodePage(VM.CodePage)?2:1), vsp->Flags);
+				GoTo(FALSE, vsp->StartPos, vsp->Flags);
 
 				if (isReShow && !(vsp->Flags&VSP_NOREDRAW))
 					ScrBuf.Flush();
NewViewer-fix2.diff (831 bytes)   

2useven10

2011-06-28 14:50

developer   bugnote:0007219

2 небольшие правки:
1) сброс кэша переводов строк при переключении файлов по +/-
2) удаление остатков 'символьного' размера юникодных файлов

DrKnS

2011-06-28 20:11

administrator   bugnote:0007220

2083

2useven10

2011-08-11 20:34

developer  

ViewerAutoscrollAgain.diff (612 bytes)   
Index: viewer.cpp
===================================================================
--- viewer.cpp	(revision 6548)
+++ viewer.cpp	(working copy)
@@ -1266,6 +1266,8 @@
 				if (!apiGetFindDataEx(strFullFileName, NewViewFindData))
 					return TRUE;
 
+				ViewFile.GetSize(NewViewFindData.nFileSize); // Required! -- thanks Dzirt2005
+
 				if (ViewFindData.ftLastWriteTime.dwLowDateTime!=NewViewFindData.ftLastWriteTime.dwLowDateTime
 				 || ViewFindData.ftLastWriteTime.dwHighDateTime!=NewViewFindData.ftLastWriteTime.dwHighDateTime
 				 || ViewFindData.nFileSize != NewViewFindData.nFileSize)
ViewerAutoscrollAgain.diff (612 bytes)   

2useven10

2011-08-11 20:37

developer   bugnote:0007441

Ёще одна поправка для автоскролла.
'Лишний' код оказался нужным.
Спасибо Dzirt2005 за диагностику.
Проявлялось, если вывод батника перенаправить в просматриваемый файл.

vskirdin

2011-08-12 06:33

administrator   bugnote:0007443

build 2143

DrKnS

2011-08-26 06:29

administrator   bugnote:0007539

А давайте во вьювер режим дампа добавим?
Т.е. без обработки переносов строк и табуляций.

2useven10

2011-08-26 08:18

developer   bugnote:0007542

а подробнее...
0) зачем?
1) что делать с длинными строками?
2) управление (вкл-выкл) ?

max rusov

2011-08-26 08:31

reporter   bugnote:0007543

Мне тоже часто хочется такое. Типа HEX'а, но без HEX'а. Просто чтобы на экран влезал кусок дампа побольше.

DrKnS

2011-08-26 08:34

administrator   bugnote:0007544

0) иногда надо смотреть/искать строковые данные в бинарном файле, внезапные переносы при этом бессмысленны, а 16-символьная колонка в hex - сильно узко.
1) как сейчас
2) F4, циклично: text - hex - dump

2useven10

2011-08-26 08:37

developer   bugnote:0007545

<CR>, <LF> - обычные (видимые) символы, или переход на новую строку?

DrKnS

2011-08-26 08:39

administrator   bugnote:0007546

>> 1) что делать с длинными строками?
> 1) как сейчас

хотя, наверное, в этом режиме лучше просто всегда сворачивать по размеру окна.


> <CR>, <LF> - обычные (видимые) символы, или переход на новую строку?

видимые, всё как в текстовой колонке hex-режима.

2useven10

2011-08-26 08:44

developer   bugnote:0007547

можно сделать, несложно.
imho лучше конфигурить доступность этого режима.
но я смогу этим заняться только недели через 2, сейчас совсем нет времени.

2useven10

2011-08-26 20:26

developer  

AutoscrollOptimize.diff (2,262 bytes)   
Index: viewer.cpp
===================================================================
--- viewer.cpp	(revision 6635)
+++ viewer.cpp	(working copy)
@@ -1265,36 +1265,45 @@
 				if (!apiGetFindDataEx(strFullFileName, NewViewFindData))
 					return TRUE;
 
-				ViewFile.GetSize(NewViewFindData.nFileSize); // Required! -- thanks Dzirt2005
-
-				if (ViewFindData.ftLastWriteTime.dwLowDateTime!=NewViewFindData.ftLastWriteTime.dwLowDateTime
-				 || ViewFindData.ftLastWriteTime.dwHighDateTime!=NewViewFindData.ftLastWriteTime.dwHighDateTime
-				 || ViewFindData.nFileSize != NewViewFindData.nFileSize)
+				// Smart file change check -- thanks Dzirt2005
+				//
+				bool changed = (
+					ViewFindData.ftLastWriteTime.dwLowDateTime!=NewViewFindData.ftLastWriteTime.dwLowDateTime ||
+					ViewFindData.ftLastWriteTime.dwHighDateTime!=NewViewFindData.ftLastWriteTime.dwHighDateTime ||
+					ViewFindData.nFileSize != NewViewFindData.nFileSize
+				);
+				if ( changed )
+					ViewFindData = NewViewFindData;
+				else {
+					if ( !ViewFile.GetSize(NewViewFindData.nFileSize) || FileSize == static_cast<__int64>(NewViewFindData.nFileSize) )
+						return TRUE;
+					changed = FileSize > static_cast<__int64>(NewViewFindData.nFileSize); // true if file shrank
+				}
+				
+				SetFileSize();
+				if ( changed ) // do not reset caches if file just enlarged [make sense on Win7, doesn't matter on XP]
 				{
-					ViewFindData = NewViewFindData;
-					SetFileSize();
-
 					Reader.Clear(); // ���� ���� �� ��� ����?
 					ViewFile.FlushBuffers();
 					vseek(0, SEEK_CUR); // reset vgetc state
 					lcache_ready = false; // reset start-lines cache
+				}
 
-					if (FilePos>FileSize)
+				if (FilePos > FileSize)
+				{
+					ProcessKey(KEY_CTRLEND);
+				}
+				else
+				{
+					__int64 PrevLastPage=LastPage;
+					LastPage = 0;
+					Show();
+
+					if (PrevLastPage && !LastPage)
 					{
 						ProcessKey(KEY_CTRLEND);
+						LastPage=TRUE;
 					}
-					else
-					{
-						__int64 PrevLastPage=LastPage;
-						LastPage = 0;
-						Show();
-
-						if (PrevLastPage && !LastPage)
-						{
-							ProcessKey(KEY_CTRLEND);
-							LastPage=TRUE;
-						}
-					}
 				}
 			}
 
AutoscrollOptimize.diff (2,262 bytes)   

2useven10

2011-08-26 20:29

developer   bugnote:0007553

Last edited: 2011-08-27 05:46

Оптимизация автоскролла для Win7.
Не сбрасываем кеш, если файл просто растёт.
На XP эффект почти нулевой.

vskirdin

2011-08-28 12:27

administrator   bugnote:0007559

build 2178

samlyukov

2011-09-02 14:24

reporter   bugnote:0007604

Last edited: 2011-09-02 14:25

стал замечать, что вьювер последних версий стал некорректно отображать первые строки при включенной опции [x] Save view and edit history.
замечаю на файле changelog, т.е. смотрю его для текущей ревизии, через несколько дней делаю "svn up", открываем на просмотр файл changelog - первые строки на экране непонятно как плавают. делаем Home и переоткрытие - становится потребно.

2useven10

2011-09-02 20:03

developer  

AdjustFilePos.diff (301 bytes)   
Index: viewer.cpp
===================================================================
--- viewer.cpp	(revision 6675)
+++ viewer.cpp	(working copy)
@@ -330,6 +330,8 @@
 			CachedCodePage = 0;
 		LastSelectPos=FilePos=NewFilePos;
 		LeftPos=NewLeftPos;
+
+		AdjustFilePos();
 	}
 	else
 	{
AdjustFilePos.diff (301 bytes)   

2useven10

2011-09-02 20:04

developer   bugnote:0007606

AdjustFilePos.diff

samlyukov

2011-09-10 07:54

reporter   bugnote:0007632

гораздо лучше.

vskirdin

2011-09-12 06:49

administrator   bugnote:0007653

build 2195

2useven10

2011-09-26 20:33

developer  

AdjustFilePos2.diff (598 bytes)   
Index: viewer.cpp
===================================================================
--- viewer.cpp	(revision 6734)
+++ viewer.cpp	(working copy)
@@ -330,8 +330,6 @@
 			CachedCodePage = 0;
 		LastSelectPos=FilePos=NewFilePos;
 		LeftPos=NewLeftPos;
-
-		AdjustFilePos();
 	}
 	else
 	{
@@ -362,6 +360,7 @@
 			if (Detect)
 			{
 				VM.CodePage=CodePage;
+				//??? CodePageChangedByUser=TRUE;
 			}
 
 			if (CachedCodePage)
@@ -384,6 +383,8 @@
 
 	if (FilePos > FileSize)
 		FilePos=0;
+	if ( FilePos )
+		AdjustFilePos();
 
 	ChangeViewKeyBar();
 	AdjustWidth();
AdjustFilePos2.diff (598 bytes)   

2useven10

2011-09-26 20:37

developer   bugnote:0007749

Last edited: 2011-09-26 20:44

AdjustFilePos2 - поправка к предыдущему фиксу.
Позицию надо корректировать после установки кодовой страницы.
Иначе при просмотре юникода у вьювера может поехать крыша :)
10x Maximus: http://forum.farmanager.com/viewtopic.php?f=9&t=667&start=15#p85644

vskirdin

2011-09-29 06:53

administrator   bugnote:0007755

build 2206

2useven10

2011-10-04 04:53

developer  

AdjustFilePos3.diff (1,005 bytes)   
Index: viewer.cpp
===================================================================
--- viewer.cpp	(revision 6747)
+++ viewer.cpp	(working copy)
@@ -1432,6 +1432,7 @@
 		{
 			VM.CodePage = VM.CodePage==GetOEMCP() ? GetACP() : GetOEMCP();
 			lcache_ready = false;
+			AdjustFilePos();
 			ChangeViewKeyBar();
 			Show();
 			CodePageChangedByUser=TRUE;
@@ -1440,8 +1441,6 @@
 		case KEY_SHIFTF8:
 		{
 			UINT nCodePage = SelectCodePage(VM.CodePage, true, true, false, true);
-			lcache_ready = false;
-
 			if (nCodePage != static_cast<UINT>(-1))
 			{
 				if (nCodePage == (CP_AUTODETECT & 0xffff))
@@ -1454,7 +1453,8 @@
 				}
 				CodePageChangedByUser=TRUE;
 				VM.CodePage=nCodePage;
-				SetFileSize();
+				lcache_ready = false;
+				AdjustFilePos();
 				ChangeViewKeyBar();
 				Show();
 			}
@@ -3382,6 +3382,7 @@
 void Viewer::SetFilePos(__int64 Pos)
 {
 	FilePos=Pos;
+	AdjustFilePos();
 };
 
 void Viewer::SetPluginData(const wchar_t *PluginData)
AdjustFilePos3.diff (1,005 bytes)   

2useven10

2011-10-04 04:57

developer   bugnote:0007822

Ещё пара мест где надо корректировать позицию - при смене кодировки и при выставлении позиции через интерфейс.
Спасибо .1st за диагностику.

vskirdin

2011-10-04 07:11

administrator   bugnote:0007824

build 2210

2useven10

2011-10-05 17:38

developer   bugnote:0007830

версию с дамповым режимом просмотра можно попробовать:
http://sharapov-16.narod.ru/far/index.html

2useven10

2011-10-07 14:20

developer   bugnote:0007842

ну так кому-нибудь такой режим ещё нужен?
или в морг?

DrKnS

2011-10-07 14:27

administrator   bugnote:0007843

Нужен.

Denis

2011-10-07 14:35

reporter   bugnote:0007844

Last edited: 2011-10-07 14:35

Пригодится!

samlyukov

2011-10-08 09:21

reporter   bugnote:0007845

как по мне, так дополнительная опция и ненужна, сделать по-умолчанию: текст-хекс-дамп

2useven10

2011-10-08 11:19

developer   bugnote:0007846

Last edited: 2011-10-08 11:20

Нужны мнения...
В текущей версии вьювер при переключении в текстовый режим автоматически позиционируется на начало текущей строки. Некоторые считают это багом.
Для бинарных файлов для этого есть некоторые основания. В связи с этим хочу ввести небольшой интеллект: для бинарных файлов по F4 переключаем хекс-дамп, для текстовых текст-хекс. По Shift-F4 все режимы по очереди.

max rusov

2011-10-08 22:48

reporter   bugnote:0007850

Может, по Shift-F4 - менюшку, со списком режимов? Тогда можно будет намакросить любой вариант переключений...

2useven10

2011-10-10 22:21

developer  

WWrap.diff (1,811 bytes)   
Index: viewer.cpp
===================================================================
--- viewer.cpp	(revision 6761)
+++ viewer.cpp	(working copy)
@@ -962,29 +962,40 @@
 		}
 
 		pString->lpData[OutPtr++] = ch ? ch : L' ';
-		if (!VM.Wrap)
+		if ( !VM.Wrap )
 			continue;
 
-		if (OutPtr >= Width)
+		if ( VM.WordWrap && OutPtr <= Width && wrapped_char(ch) >= 0 )
 		{
-			if (VM.WordWrap && wrap_out > 0)
-			{
-				OutPtr = wrap_out;
-				vseek(wrap_pos, SEEK_SET);
-				while (OutPtr > 0 && is_space_or_nul(pString->lpData[OutPtr-1]))
-					--OutPtr;
-				skip_space = true;
-			}
-			break;
+			wrap_out = OutPtr;
+			wrap_pos = fpos1;
 		}
 
-		if (!VM.WordWrap)
+		if ( OutPtr < Width )
 			continue;
+		if ( !VM.WordWrap )
+			break;
 
-		if (wrapped_char(ch) >= 0)
+		if ( OutPtr > Width )
 		{
-			wrap_out = OutPtr;
-			wrap_pos = vtell();
+			if ( wrap_out <= 0 || is_space_or_nul(ch) )
+			{
+				wrap_out = OutPtr - 1;
+				wrap_pos = fpos;
+			}
+
+			OutPtr = wrap_out;
+			vseek(wrap_pos, SEEK_SET);
+			while (OutPtr > 0 && is_space_or_nul(pString->lpData[OutPtr-1]))
+				--OutPtr;
+
+			if ( bSelEndFound && pString->nSelEnd > OutPtr )
+				pString->nSelEnd = OutPtr;
+			if ( bSelStartFound && pString->nSelStart >= OutPtr )
+				bSelStartFound = bSelEndFound = false;
+
+			skip_space = true;
+			break;
 		}
 	}
 
@@ -2248,9 +2259,9 @@
 
 struct MyDialogData
 {
-   Viewer      *viewer;
+	Viewer      *viewer;
 	bool edit_autofocus;
-   bool       hex_mode;
+	bool       hex_mode;
 	bool      recursive;
 };
 
@@ -2279,7 +2290,7 @@
 		}
 		case DM_SDREXVISIBILITY:
 		{
-		   int show = 1;
+			int show = 1;
 			if ( Param1 )
 			{
 				int tlen = (int)SendDlgMessage(hDlg, DM_GETTEXTPTR, SD_EDIT_TEXT, 0);
WWrap.diff (1,811 bytes)   

2useven10

2011-10-10 22:24

developer   bugnote:0007860

WWrap.diff - убираем артефакты при попадании найденного текста в переносимую часть строки при WWrap. Более точно определяем границу переноса (раньше переносилось 'с запасом').

vskirdin

2011-10-11 10:57

administrator   bugnote:0007861

Last edited: 2011-10-11 11:00

2useven10: давай так поступим.
я переписываю этот Инц на тебя, а дальше сам :-)
ты уже можешь сюда не аттачить дифы.
в этом Инце ты ставишь "нужен отклик" и пишешь номер билда, где ЭТО исправлено.
тот, что проверяет (хмм.. в этом случае ты :-)) ставит статус "угу, исправлено"...

Я так думаю, что этот Инц имеет смысл закрыть, т.к. свою роль он уже сыграл, но начинает обрастать прочими хотелкобагофичами.
Те, кто/кому/etc. что-то надо от вьювера - пусть заводят новые Инцы.

2useven10

2011-10-11 11:17

developer   bugnote:0007862

build 2217

Issue History

Date Modified Username Field Change
2011-05-12 17:45 2useven10 New Issue
2011-05-12 19:58 2useven10 Note Added: 0006882
2011-05-12 19:59 2useven10 Note Edited: 0006882
2011-05-12 20:34 max rusov Note Added: 0006883
2011-05-12 21:18 2useven10 Note Added: 0006884
2011-05-12 22:29 max rusov Note Added: 0006886
2011-05-13 12:45 2useven10 Note Added: 0006887
2011-05-13 12:46 2useven10 Note Edited: 0006887
2011-05-18 07:53 2useven10 File Added: viewer-diff-8.7z
2011-05-18 07:55 2useven10 Note Added: 0006890
2011-05-18 14:51 vskirdin Note Added: 0006893
2011-05-18 16:07 vskirdin Note Added: 0006894
2011-05-18 16:22 2useven10 Note Added: 0006895
2011-05-18 16:30 2useven10 Note Edited: 0006895
2011-05-18 16:31 2useven10 Note Edited: 0006895
2011-05-18 20:52 vskirdin Note Added: 0006896
2011-05-18 21:27 2useven10 Note Added: 0006897
2011-05-20 19:11 2useven10 Note Edited: 0006897
2011-05-23 14:29 2useven10 Note Edited: 0006897
2011-05-25 12:24 vskirdin Note Added: 0007033
2011-05-25 13:43 2useven10 Note Added: 0007038
2011-05-25 13:58 2useven10 Note Edited: 0007038
2011-05-26 11:10 2useven10 File Added: NewViewer.diff
2011-05-26 11:10 2useven10 Note Added: 0007063
2011-05-26 11:59 vskirdin Note Added: 0007065
2011-05-26 11:59 vskirdin Assigned To => vskirdin
2011-05-26 11:59 vskirdin Status new => assigned
2011-05-26 14:04 2useven10 File Added: NewViewer2.diff
2011-05-26 14:05 2useven10 Note Added: 0007067
2011-05-26 14:13 2useven10 File Added: NewViewer3.diff
2011-05-26 14:13 2useven10 Note Edited: 0007067
2011-05-26 14:18 vskirdin Note Added: 0007069
2011-05-26 14:21 vskirdin Note Added: 0007070
2011-05-26 14:24 vskirdin Note Added: 0007072
2011-05-26 14:43 2useven10 Note Added: 0007074
2011-05-26 15:11 vskirdin Note Added: 0007075
2011-05-26 15:29 2useven10 Note Added: 0007076
2011-05-26 15:50 vskirdin Note Added: 0007077
2011-05-26 15:50 vskirdin Status assigned => feedback
2011-05-27 06:46 DrKnS Note Added: 0007083
2011-05-27 07:36 2useven10 Note Added: 0007086
2011-05-27 07:36 2useven10 Status feedback => assigned
2011-06-01 11:25 2useven10 File Added: ViewerLongLineFix.diff
2011-06-01 11:27 2useven10 Note Added: 0007116
2011-06-03 06:16 vskirdin Note Added: 0007121
2011-06-03 06:16 vskirdin Status assigned => feedback
2011-06-28 14:47 2useven10 File Added: NewViewer-fix2.diff
2011-06-28 14:50 2useven10 Note Added: 0007219
2011-06-28 14:50 2useven10 Status feedback => assigned
2011-06-28 20:11 DrKnS Note Added: 0007220
2011-06-28 20:11 DrKnS Status assigned => feedback
2011-08-11 20:34 2useven10 File Added: ViewerAutoscrollAgain.diff
2011-08-11 20:37 2useven10 Note Added: 0007441
2011-08-11 20:37 2useven10 Status feedback => assigned
2011-08-12 06:33 vskirdin Note Added: 0007443
2011-08-12 06:33 vskirdin Status assigned => feedback
2011-08-26 06:29 DrKnS Note Added: 0007539
2011-08-26 08:18 2useven10 Note Added: 0007542
2011-08-26 08:18 2useven10 Status feedback => assigned
2011-08-26 08:31 max rusov Note Added: 0007543
2011-08-26 08:34 DrKnS Note Added: 0007544
2011-08-26 08:37 2useven10 Note Added: 0007545
2011-08-26 08:39 DrKnS Note Added: 0007546
2011-08-26 08:44 2useven10 Note Added: 0007547
2011-08-26 20:26 2useven10 File Added: AutoscrollOptimize.diff
2011-08-26 20:29 2useven10 Note Added: 0007553
2011-08-27 05:46 2useven10 Note Edited: 0007553
2011-08-28 12:27 vskirdin Note Added: 0007559
2011-08-28 12:27 vskirdin Status assigned => feedback
2011-09-02 14:24 samlyukov Note Added: 0007604
2011-09-02 14:25 samlyukov Note Edited: 0007604
2011-09-02 20:03 2useven10 File Added: AdjustFilePos.diff
2011-09-02 20:04 2useven10 Note Added: 0007606
2011-09-02 20:04 2useven10 Status feedback => assigned
2011-09-10 07:54 samlyukov Note Added: 0007632
2011-09-12 06:49 vskirdin Note Added: 0007653
2011-09-12 06:49 vskirdin Status assigned => feedback
2011-09-26 20:33 2useven10 File Added: AdjustFilePos2.diff
2011-09-26 20:37 2useven10 Note Added: 0007749
2011-09-26 20:37 2useven10 Status feedback => assigned
2011-09-26 20:44 2useven10 Note Edited: 0007749
2011-09-29 06:53 vskirdin Note Added: 0007755
2011-09-29 06:53 vskirdin Status assigned => feedback
2011-10-04 04:53 2useven10 File Added: AdjustFilePos3.diff
2011-10-04 04:57 2useven10 Note Added: 0007822
2011-10-04 04:57 2useven10 Status feedback => assigned
2011-10-04 07:11 vskirdin Note Added: 0007824
2011-10-04 07:11 vskirdin Status assigned => feedback
2011-10-05 17:38 2useven10 Note Added: 0007830
2011-10-05 17:38 2useven10 Status feedback => assigned
2011-10-07 14:20 2useven10 Note Added: 0007842
2011-10-07 14:27 DrKnS Note Added: 0007843
2011-10-07 14:35 Denis Note Added: 0007844
2011-10-07 14:35 Denis Note Edited: 0007844
2011-10-08 09:21 samlyukov Note Added: 0007845
2011-10-08 11:19 2useven10 Note Added: 0007846
2011-10-08 11:20 2useven10 Note Edited: 0007846
2011-10-08 22:48 max rusov Note Added: 0007850
2011-10-10 22:21 2useven10 File Added: WWrap.diff
2011-10-10 22:24 2useven10 Note Added: 0007860
2011-10-11 10:57 vskirdin Note Added: 0007861
2011-10-11 10:57 vskirdin Assigned To vskirdin => 2useven10
2011-10-11 11:00 vskirdin Note Edited: 0007861
2011-10-11 11:17 2useven10 Note Added: 0007862
2011-10-11 11:18 2useven10 Status assigned => feedback
2011-10-11 11:18 2useven10 Status feedback => resolved
2011-10-11 11:20 2useven10 Build => 2217
2011-10-11 11:20 2useven10 Status resolved => closed
2011-10-11 11:20 2useven10 Resolution open => fixed
2011-10-11 11:20 2useven10 Fixed in Version => 3.0