support full-width/composite characters and true color palette in terminal (#1386)
[far2l.git] / far2l / src / viewer.cpp
blob30180d2ad93d9e9a0474ffd12c8ea6bc923b56d1
1 /*
2 viewer.cpp
4 Internal viewer
5 */
6 /*
7 Copyright (c) 1996 Eugene Roshal
8 Copyright (c) 2000 Far Group
9 All rights reserved.
11 Redistribution and use in source and binary forms, with or without
12 modification, are permitted provided that the following conditions
13 are met:
14 1. Redistributions of source code must retain the above copyright
15 notice, this list of conditions and the following disclaimer.
16 2. Redistributions in binary form must reproduce the above copyright
17 notice, this list of conditions and the following disclaimer in the
18 documentation and/or other materials provided with the distribution.
19 3. The name of the authors may not be used to endorse or promote products
20 derived from this software without specific prior written permission.
22 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 #include "headers.hpp"
36 #include <ctype.h>
37 #include "viewer.hpp"
38 #include "codepage.hpp"
39 #include "macroopcode.hpp"
40 #include "keyboard.hpp"
41 #include "lang.hpp"
42 #include "colors.hpp"
43 #include "keys.hpp"
44 #include "help.hpp"
45 #include "dialog.hpp"
46 #include "panel.hpp"
47 #include "filepanels.hpp"
48 #include "fileview.hpp"
49 #include "savescr.hpp"
50 #include "ctrlobj.hpp"
51 #include "scrbuf.hpp"
52 #include "TPreRedrawFunc.hpp"
53 #include "syslog.hpp"
54 #include "cddrv.hpp"
55 #include "interf.hpp"
56 #include "message.hpp"
57 #include "clipboard.hpp"
58 #include "delete.hpp"
59 #include "dirmix.hpp"
60 #include "pathmix.hpp"
61 #include "filestr.hpp"
62 #include "mix.hpp"
63 #include "execute.hpp"
64 #include "constitle.hpp"
65 #include "console.hpp"
66 #include "AnsiEsc.hpp"
67 #include "wakeful.hpp"
68 #include "WideMB.h"
69 #include "UtfConvert.hpp"
71 static void PR_ViewerSearchMsg();
72 static void ViewerSearchMsg(const wchar_t *Name,int Percent);
74 static int InitHex=FALSE,SearchHex=FALSE;
76 static int NextViewerID=0;
79 static int CalcByteDistance(UINT CodePage, const wchar_t* begin, const wchar_t* end)
81 if (begin > end)
82 return -1;
83 #if (__WCHAR_MAX__ > 0xffff)
84 if ((CodePage == CP_UTF32LE) || (CodePage == CP_UTF32BE)) {
85 return (end - begin) * 4;
88 int distance;
90 if ((CodePage == CP_UTF16LE) || (CodePage == CP_UTF16BE)) {
91 distance = UtfCalcSpace<wchar_t, uint16_t>(begin, end - begin, false);
92 distance*= sizeof(uint16_t);
94 } else if (CodePage == CP_UTF8) {
95 distance = UtfCalcSpace<wchar_t, uint8_t>(begin, end - begin, false);
97 } else {// one-byte code page?
98 distance = end - begin;
101 #else
102 if ((CodePage == CP_UTF16LE) || (CodePage == CP_UTF16BE)) {
103 return (end - begin) * 2;
106 int distance;
108 if (CodePage == CP_UTF8) {
109 distance = UtfCalcSpace<wchar_t, uint8_t>(begin, end - begin, false);
111 } else {// one-byte code page?
112 distance = end - begin;
115 #endif
117 return distance;
120 static int CalcCodeUnitsDistance(UINT CodePage, const wchar_t* begin, const wchar_t* end)
122 int distance = CalcByteDistance(CodePage, begin, end);
123 if (distance > 0) switch (CodePage) {
124 case CP_UTF32LE: case CP_UTF32BE: distance/= 4; break;
125 case CP_UTF16LE: case CP_UTF16BE: distance/= 2; break;
127 return distance;
131 Viewer::Viewer(bool bQuickView, UINT aCodePage):
132 ViOpt(Opt.ViOpt),
133 m_bQuickView(bQuickView)
135 _OT(SysLog(L"[%p] Viewer::Viewer()", this));
137 for (int i=0; i<=MAXSCRY; i++)
139 Strings[i] = new ViewerString();
140 Strings[i]->lpData[MAX_VIEWLINEB] = 0;
143 strLastSearchStr = strGlobalSearchString;
144 LastSearchCase=GlobalSearchCase;
145 LastSearchRegexp=Opt.ViOpt.SearchRegexp;
146 LastSearchWholeWords=GlobalSearchWholeWords;
147 LastSearchReverse=GlobalSearchReverse;
148 LastSearchHex=GlobalSearchHex;
149 VM.CodePage=DefCodePage=aCodePage;
150 // Вспомним тип врапа
151 VM.Wrap=Opt.ViOpt.ViewerIsWrap;
152 VM.WordWrap=Opt.ViOpt.ViewerWrap;
153 VM.Hex=InitHex;
154 VM.Processed=0;
155 ViewKeyBar=nullptr;
156 FilePos=0;
157 LeftPos=0;
158 SecondPos=0;
159 FileSize=0;
160 LastPage=0;
161 SelectPos=SelectSize=0;
162 LastSelPos=0;
163 SetStatusMode(TRUE);
164 HideCursor=TRUE;
165 CodePageChangedByUser=FALSE;
166 memset(&BMSavePos,0xff,sizeof(BMSavePos));
167 memset(UndoData,0xff,sizeof(UndoData));
168 LastKeyUndo=FALSE;
169 InternalKey=FALSE;
170 ViewerID=::NextViewerID++;
171 CtrlObject->Plugins.CurViewer=this;
172 OpenFailed=false;
173 HostFileViewer=nullptr;
174 bVE_READ_Sent = false;
177 FARString Viewer::ComposeCacheName()
179 // FARString strCacheName=strPluginData.IsEmpty()?strFileName:strPluginData+PointToName(strFileName);
180 FARString strCacheName=strPluginData.IsEmpty()?strFullFileName:strPluginData+PointToName(strFileName);
181 if (VM.Processed)
183 strCacheName+= L":PROCESSED";
186 return strCacheName;
189 void Viewer::SavePosCache()
191 if (Opt.ViOpt.SavePos && Opt.OnlyEditorViewerUsed != Options::ONLY_VIEWER_ON_CMDOUT)
193 FARString strCacheName=ComposeCacheName();
195 UINT CodePage=0;
197 if (CodePageChangedByUser)
199 CodePage=VM.CodePage;
202 PosCache poscache={};
203 poscache.Param[0]=FilePos;
204 poscache.Param[1]=LeftPos;
205 poscache.Param[2]=VM.Hex;
206 //=poscache.Param[3];
207 poscache.Param[4]=CodePage;
209 if (Opt.ViOpt.SaveShortPos)
211 poscache.Position[0]=BMSavePos.SavePosAddr;
212 poscache.Position[1]=BMSavePos.SavePosLeft;
213 //poscache.Position[2]=;
214 //poscache.Position[3]=;
217 CtrlObject->ViewerPosCache->AddPosition(strCacheName,poscache);
221 Viewer::~Viewer()
223 KeepInitParameters();
225 if (ViewFile.Opened())
227 ViewFile.Close();
228 SavePosCache();
231 _tran(SysLog(L"[%p] Viewer::~Viewer, TempViewName=[%ls]",this,TempViewName));
232 /* $ 11.10.2001 IS
233 Удаляем файл только, если нет открытых фреймов с таким именем.
236 if (!strProcessedViewName.IsEmpty())
238 unlink(strProcessedViewName.GetMB().c_str());
239 CutToSlash(strProcessedViewName);
240 if (!strProcessedViewName.IsEmpty())
242 rmdir(strProcessedViewName.GetMB().c_str());
243 strProcessedViewName.Clear();
247 for (int i=0; i<=MAXSCRY; i++)
249 delete Strings[i];
252 if (!OpenFailed && bVE_READ_Sent)
254 CtrlObject->Plugins.CurViewer=this; //HostFileViewer;
255 CtrlObject->Plugins.ProcessViewerEvent(VE_CLOSE,&ViewerID);
260 void Viewer::KeepInitParameters()
262 strGlobalSearchString = strLastSearchStr;
263 GlobalSearchCase=LastSearchCase;
264 GlobalSearchWholeWords=LastSearchWholeWords;
265 GlobalSearchReverse=LastSearchReverse;
266 GlobalSearchHex=LastSearchHex;
267 Opt.ViOpt.ViewerIsWrap=VM.Wrap;
268 Opt.ViOpt.ViewerWrap=VM.WordWrap;
269 Opt.ViOpt.SearchRegexp=LastSearchRegexp;
270 InitHex=VM.Hex;
274 int Viewer::OpenFile(const wchar_t *Name,int warning)
276 VM.CodePage=DefCodePage;
277 DefCodePage=CP_AUTODETECT;
278 OpenFailed=false;
280 ViewFile.Close();
282 DWORD FileAttr = apiGetFileAttributes(Name);
283 if (FileAttr!=INVALID_FILE_ATTRIBUTES && (FileAttr&FILE_ATTRIBUTE_DEVICE)!=0) {//avoid stuck
284 OpenFailed=TRUE;
285 return FALSE;
288 SelectSize = 0; // Сбросим выделение
289 strFileName = Name;
291 // Processed mode:
292 // Renders ANSI ESC coloring sequences
293 // For all files beside *.ansi/*.ans runs view.sh
294 // that doing 'processing' of file and writes output
295 // into temporary filename. That temporary file then
296 // viewed instead of original one's.
297 const wchar_t *ext = wcsrchr(Name, L'.');
298 if (ext && (wcscasecmp(ext, L".ansi") == 0 || wcscasecmp(ext, L".ans") == 0))
300 VM.Processed = 1;
301 VM.Wrap = 0;
302 if (VM.CodePage == CP_AUTODETECT)
304 VM.CodePage = 437;
307 else if (VM.Processed)
309 if (strProcessedViewName.IsEmpty())
311 if (FarMkTempEx(strProcessedViewName, L"view"))
313 strProcessedViewName+= PointToName(strFileName);
315 std::string cmd = GetMyScriptQuoted("view.sh");
316 std::string strFile = strFileName.GetMB();
318 QuoteCmdArgIfNeed(strFile);
319 cmd+= ' ';
320 cmd+= strFile;
322 strFile = strProcessedViewName.GetMB();
323 QuoteCmdArgIfNeed(strFile);
324 cmd+= ' ';
325 cmd+= strFile;
326 int r = farExecuteA(cmd.c_str(), 0);
327 if (r == 0) {
328 Name = strProcessedViewName.CPtr();
329 } else {
330 unlink(strProcessedViewName.GetMB().c_str());
331 strProcessedViewName.Clear();
334 } else {
335 Name = strProcessedViewName.CPtr();
339 ViewFile.Open(Wide2MB(Name));//strFileName.GetMB()
341 if (!ViewFile.Opened())
343 /* $ 04.07.2000 tran
344 + 'warning' flag processing, in QuickView it is FALSE
345 so don't show red message box */
346 if (warning)
347 Message(MSG_WARNING|MSG_ERRORTYPE,1,Msg::ViewerTitle,Msg::ViewerCannotOpenFile,strFileName,Msg::Ok);
349 OpenFailed=true;
350 return FALSE;
353 CodePageChangedByUser=FALSE;
355 ConvertNameToFull(strFileName,strFullFileName);
356 apiGetFindDataForExactPathName(strFileName, ViewFindData);
357 UINT CachedCodePage=0;
359 if (Opt.ViOpt.SavePos)
361 int64_t NewLeftPos,NewFilePos;
362 FARString strCacheName=ComposeCacheName();
363 memset(&BMSavePos,0xff,sizeof(BMSavePos)); //заполним с -1
364 PosCache poscache={};
366 if (Opt.ViOpt.SaveShortPos)
368 poscache.Position[0]=BMSavePos.SavePosAddr;
369 poscache.Position[1]=BMSavePos.SavePosLeft;
370 //poscache.Position[2]=;
371 //poscache.Position[3]=;
374 CtrlObject->ViewerPosCache->GetPosition(strCacheName,poscache);
375 NewFilePos=poscache.Param[0];
376 NewLeftPos=poscache.Param[1];
377 VM.Hex=(int)poscache.Param[2];
378 //=poscache.Param[3];
379 CachedCodePage=(UINT)poscache.Param[4];
381 // Проверяем поддерживается или нет загруженная из кэша кодовая страница
382 if (CachedCodePage && !IsCodePageSupported(CachedCodePage))
383 CachedCodePage = 0;
384 LastSelPos=FilePos=NewFilePos;
385 LeftPos=NewLeftPos;
387 else
389 FilePos=0;
392 /* $ 26.07.2002 IS
393 Автоопределение Unicode не должно зависеть от опции
394 "Автоопределение таблицы символов", т.к. Unicode не есть
395 _таблица символов_ для перекодировки.
397 //if(ViOpt.AutoDetectTable)
399 bool Detect=false;
400 UINT CodePage=0;
402 if (VM.CodePage == CP_AUTODETECT || IsUnicodeOrUtfCodePage(VM.CodePage))
404 Detect = GetFileFormat2(strFileName,CodePage,nullptr,Opt.ViOpt.AutoDetectCodePage!=0,true);
407 if (VM.CodePage==CP_AUTODETECT)
409 if (Detect)
411 VM.CodePage=CodePage;
414 if (CachedCodePage)
416 VM.CodePage=CachedCodePage;
417 CodePageChangedByUser=TRUE;
420 if (VM.CodePage==CP_AUTODETECT)
421 VM.CodePage = Opt.ViOpt.DefaultCodePage;
423 else
425 CodePageChangedByUser=TRUE;
428 // BUGBUG
429 // пока что запретим переключать hex в UTF8/UTF32, ибо не работает.
430 if (VM.Hex && (VM.CodePage==CP_UTF8 || VM.CodePage==CP_UTF32LE || VM.CodePage==CP_UTF32BE))
432 VM.CodePage=WINPORT(GetACP)();
435 if (!IsUnicodeOrUtfCodePage(VM.CodePage))
437 ViewFile.SetPointer(0);
440 SetFileSize();
442 if (FilePos>FileSize)
443 FilePos=0;
445 SetCRSym();
446 // if (ViOpt.AutoDetectTable && !TableChangedByUser)
447 // {
448 // }
449 ChangeViewKeyBar();
450 AdjustWidth();
451 CtrlObject->Plugins.CurViewer=this; // HostFileViewer;
452 /* $ 15.09.2001 tran
453 пора легализироваться */
454 CtrlObject->Plugins.ProcessViewerEvent(VE_READ,nullptr);
455 bVE_READ_Sent = true;
456 return TRUE;
460 /* $ 27.04.2001 DJ
461 функция вычисления ширины в зависимости от наличия скроллбара
464 void Viewer::AdjustWidth()
466 Width=X2-X1+1;
467 XX2=X2;
469 if (ViOpt.ShowScrollbar && !m_bQuickView)
471 Width--;
472 XX2--;
476 void Viewer::SetCRSym()
478 if (!ViewFile.Opened())
479 return;
481 wchar_t Buf[2048];
482 int CRCount=0,LFCount=0;
483 int ReadSize,I;
484 vseek(0,SEEK_SET);
485 ReadSize=vread(Buf,ARRAYSIZE(Buf));
487 for (I=0; I<ReadSize; I++)
488 switch (Buf[I])
490 case L'\n':
491 LFCount++;
492 break;
493 case L'\r':
495 if (I+1>=ReadSize || Buf[I+1]!=L'\n')
496 CRCount++;
498 break;
501 if (LFCount<CRCount)
502 CRSym=L'\r';
503 else
504 CRSym=L'\n';
507 void Viewer::ShowPage(int nMode)
509 int I,Y;
510 AdjustWidth();
512 if (!ViewFile.Opened())
514 if (!strFileName.IsEmpty() && ((nMode == SHOW_RELOAD) || (nMode == SHOW_HEX)))
516 SetScreen(X1,Y1,X2,Y2,L' ',COL_VIEWERTEXT);
517 GotoXY(X1,Y1);
518 SetColor(COL_WARNDIALOGTEXT);
519 FS << fmt::Cells() << fmt::Truncate(XX2 - X1 + 1) << Msg::ViewerCannotOpenFile;
520 ShowStatus();
523 return;
526 if (HideCursor)
527 SetCursorType(0,10);
529 vseek(FilePos,SEEK_SET);
531 if (!SelectSize)
532 SelectPos=FilePos;
534 switch (nMode)
536 case SHOW_HEX:
537 CtrlObject->Plugins.CurViewer = this; //HostFileViewer;
538 ShowHex();
539 break;
540 case SHOW_RELOAD:
541 CtrlObject->Plugins.CurViewer = this; //HostFileViewer;
543 for (I=0,Y=Y1; Y<=Y2; Y++,I++)
545 Strings[I]->nFilePos = vtell();
547 if (Y==Y1+1 && !ViewFile.Eof())
548 SecondPos=vtell();
550 ReadString(Strings[I],-1,MAX_VIEWLINEB);
553 break;
554 case SHOW_UP:
556 for (I=Y2-Y1-1; I>=0; I--)
558 Strings[I+1]->nFilePos = Strings[I]->nFilePos;
559 Strings[I+1]->nSelStart = Strings[I]->nSelStart;
560 Strings[I+1]->nSelEnd = Strings[I]->nSelEnd;
561 Strings[I+1]->bSelection = Strings[I]->bSelection;
562 wcscpy(Strings[I+1]->lpData, Strings[I]->lpData);
565 Strings[0]->nFilePos = FilePos;
566 SecondPos = Strings[1]->nFilePos;
567 ReadString(Strings[0],(int)(SecondPos-FilePos),MAX_VIEWLINEB);
568 break;
569 case SHOW_DOWN:
571 for (I=0; I<Y2-Y1; I++)
573 Strings[I]->nFilePos = Strings[I+1]->nFilePos;
574 Strings[I]->nSelStart = Strings[I+1]->nSelStart;
575 Strings[I]->nSelEnd = Strings[I+1]->nSelEnd;
576 Strings[I]->bSelection = Strings[I+1]->bSelection;
577 wcscpy(Strings[I]->lpData, Strings[I+1]->lpData);
580 FilePos = Strings[0]->nFilePos;
581 SecondPos = Strings[1]->nFilePos;
582 vseek(Strings[Y2-Y1]->nFilePos, SEEK_SET);
583 ReadString(Strings[Y2-Y1],-1,MAX_VIEWLINEB);
584 Strings[Y2-Y1]->nFilePos = vtell();
585 ReadString(Strings[Y2-Y1],-1,MAX_VIEWLINEB);
586 break;
589 if (nMode != SHOW_HEX)
591 std::unique_ptr<ViewerPrinter> printer;
592 if (VM.Processed)
593 printer.reset(new AnsiEsc::Printer(B_BLACK | F_WHITE));
594 else
595 printer.reset(new PlainViewerPrinter(COL_VIEWERTEXT));
596 if (IsUnicodeOrUtfCodePage(VM.CodePage))
597 printer->EnableBOMSkip();
599 for (I=0,Y=Y1; Y<=Y2; Y++,I++)
601 int StrLen = printer->Length(Strings[I]->lpData);
602 GotoXY(X1,Y);
604 printer->Print(LeftPos, Width, Strings[I]->lpData);
606 if (SelectSize && Strings[I]->bSelection)
608 auto visualSelStart = printer->Length(Strings[I]->lpData, Strings[I]->nSelStart);
609 auto visualSelLength = printer->Length(&Strings[I]->lpData[Strings[I]->nSelStart],
610 Strings[I]->nSelEnd - Strings[I]->nSelStart);
612 if (!VM.Wrap && AdjustSelPosition &&
613 (visualSelStart < LeftPos || (visualSelStart > LeftPos
614 && visualSelStart + visualSelLength > LeftPos + XX2 - X1)))
616 LeftPos = visualSelStart > 1 ? visualSelStart - 1 : 0;
617 AdjustSelPosition = FALSE;
618 Show();
619 return;
622 int SelX1 = X1, SelSkip = 0;
623 if (visualSelStart > LeftPos)
624 SelX1+= visualSelStart - LeftPos;
625 else if (visualSelStart < LeftPos)
626 SelSkip = LeftPos - visualSelStart;
628 if (visualSelLength > SelSkip) {
629 GotoXY((int)SelX1, Y);
630 // PlainViewerPrinter selPrinter(COL_VIEWERSELECTEDTEXT);
631 // if (IsUnicodeOrUtfCodePage(VM.CodePage))
632 // selPrinter.EnableBOMSkip();
633 printer->SetSelection(true);
634 printer->Print(SelSkip,
635 visualSelLength - SelSkip, &Strings[I]->lpData[Strings[I]->nSelStart]);
636 printer->SetSelection(false);
640 if (StrLen > LeftPos + Width && ViOpt.ShowArrows)
642 GotoXY(XX2,Y);
643 SetColor(COL_VIEWERARROWS);
644 BoxText(0xbb);
647 if (LeftPos>0 && *Strings[I]->lpData && ViOpt.ShowArrows)
649 GotoXY(X1,Y);
650 SetColor(COL_VIEWERARROWS);
651 BoxText(0xab);
656 DrawScrollbar();
657 ShowStatus();
660 void Viewer::DisplayObject()
662 ShowPage(VM.Hex?SHOW_HEX:SHOW_RELOAD);
665 void Viewer::ShowHex()
667 wchar_t OutStr[MAX_VIEWLINE],TextStr[20];
668 int EndFile;
669 // int64_t SelSize;
670 WCHAR Ch;
671 int X,Y,TextPos;
672 int SelStart, SelEnd;
673 bool bSelStartFound = false, bSelEndFound = false;
674 int64_t HexLeftPos=((LeftPos>80-ObjWidth) ? Max(80-ObjWidth,0):LeftPos);
676 for (EndFile=0,Y=Y1; Y<=Y2; Y++)
678 bSelStartFound = false;
679 bSelEndFound = false;
680 // SelSize=0;
681 SetColor(COL_VIEWERTEXT);
682 GotoXY(X1,Y);
684 if (EndFile)
686 FS << fmt::Cells() << fmt::Expand(ObjWidth) << L"";
687 continue;
690 if (Y==Y1+1 && !ViewFile.Eof())
691 SecondPos=vtell();
693 INT64 Ptr=0;
694 ViewFile.GetPointer(Ptr);
695 swprintf(OutStr,ARRAYSIZE(OutStr),L"%010llX: ", Ptr);
696 TextPos=0;
697 int HexStrStart = (int)wcslen(OutStr);
698 SelStart = HexStrStart;
699 SelEnd = SelStart;
700 int64_t fpos = vtell();
702 if (fpos > SelectPos)
703 bSelStartFound = true;
705 if (fpos < SelectPos+SelectSize-1)
706 bSelEndFound = true;
708 if (!SelectSize)
709 bSelStartFound = bSelEndFound = false;
711 const wchar_t BorderLine[]={BoxSymbols[BS_V1],L' ',0};
713 if (VM.CodePage==CP_UTF16LE || VM.CodePage==CP_UTF16BE)
715 for (X=0; X<8; X++)
717 int64_t fpos = vtell();
719 if (SelectSize>0 && (SelectPos == fpos))
721 bSelStartFound = true;
722 SelStart = (int)wcslen(OutStr);
723 // SelSize=SelectSize;
724 /* $ 22.01.2001 IS
725 Внимание! Возможно, это не совсем верное решение проблемы
726 выделения из плагинов, но мне пока другого в голову не пришло.
727 Я приравниваю SelectSize нулю в Process*
729 //SelectSize=0;
732 if (SelectSize>0 && (fpos == (SelectPos+SelectSize-1)))
734 bSelEndFound = true;
735 SelEnd = (int)wcslen(OutStr)+3;
736 // SelSize=SelectSize;
739 if (!vgetc(Ch))
741 /* $ 28.06.2000 tran
742 убираем показ пустой строки, если длина
743 файла кратна 16 */
744 EndFile=1;
745 LastPage=1;
747 if (!X)
749 wcscpy(OutStr,L"");
750 break;
753 wcscat(OutStr,L" ");
754 TextStr[TextPos++]=L' ';
756 else
758 WCHAR OutChar = Ch;
760 if (VM.CodePage == CP_UTF16BE) {
761 OutChar = WideReverse(OutChar);
765 int OutStrLen=StrLength(OutStr);
766 swprintf(OutStr+OutStrLen,ARRAYSIZE(OutStr)-OutStrLen,L"%02X%02X ", (unsigned int)HIBYTE(OutChar), (unsigned int)LOBYTE(OutChar));
768 if (!Ch)
770 Ch=L' ';
773 TextStr[TextPos++]=Ch;
774 LastPage=0;
777 if (X==3)
778 wcscat(OutStr, BorderLine);
781 else
783 for (X=0; X<16; X++)
785 int64_t fpos = vtell();
787 if (SelectSize>0 && (SelectPos == fpos))
789 bSelStartFound = true;
790 SelStart = (int)wcslen(OutStr);
791 // SelSize=SelectSize;
792 /* $ 22.01.2001 IS
793 Внимание! Возможно, это не совсем верное решение проблемы
794 выделения из плагинов, но мне пока другого в голову не пришло.
795 Я приравниваю SelectSize нулю в Process*
797 //SelectSize=0;
800 if (SelectSize>0 && (fpos == (SelectPos+SelectSize-1)))
802 bSelEndFound = true;
803 SelEnd = (int)wcslen(OutStr)+1;
804 // SelSize=SelectSize;
807 if (!vgetc(Ch))
809 /* $ 28.06.2000 tran
810 убираем показ пустой строки, если длина
811 файла кратна 16 */
812 EndFile=1;
813 LastPage=1;
815 if (!X)
817 wcscpy(OutStr,L"");
818 break;
821 /* $ 03.07.2000 tran
822 - вместо 5 пробелов тут надо 3 */
823 wcscat(OutStr,L" ");
824 TextStr[TextPos++]=L' ';
826 else
828 char NewCh;
829 WINPORT(WideCharToMultiByte)(VM.CodePage, 0, &Ch,1, &NewCh,1," ",nullptr);
830 int OutStrLen=StrLength(OutStr);
831 swprintf(OutStr+OutStrLen,ARRAYSIZE(OutStr)-OutStrLen,L"%02X ", (unsigned int)(unsigned char)NewCh);
833 if (!Ch)
834 Ch=L' ';
836 TextStr[TextPos++]=Ch;
837 LastPage=0;
840 if (X==7)
841 wcscat(OutStr,BorderLine);
845 TextStr[TextPos]=0;
846 wcscat(TextStr,L" ");
848 if ((SelEnd <= SelStart) && bSelStartFound)
849 SelEnd = (int)wcslen(OutStr)-2;
851 wcscat(OutStr,L" ");
852 wcscat(OutStr,TextStr);
853 #if 0
855 for (size_t I=0; I < wcslen(OutStr); ++I)
856 if (OutStr[I] == (wchar_t)0xFFFF)
857 OutStr[I]=L'?';
859 #endif
861 if (StrLength(OutStr)>HexLeftPos)
863 FS << fmt::Cells() << fmt::LeftAlign() << fmt::Size(ObjWidth) << OutStr + static_cast<size_t>(HexLeftPos);
865 else
867 FS << fmt::Cells() << fmt::Expand(ObjWidth) << L"";
870 if (bSelStartFound && bSelEndFound)
872 SetColor(COL_VIEWERSELECTEDTEXT);
873 GotoXY((int)((int64_t)X1+SelStart-HexLeftPos),Y);
874 FS << fmt::Cells() << fmt::Truncate(SelEnd - SelStart + 1) << OutStr + static_cast<size_t>(SelStart);
875 // SelSize = 0;
880 /* $ 27.04.2001 DJ
881 отрисовка скроллбара - в отдельную функцию
884 void Viewer::DrawScrollbar()
886 if (ViOpt.ShowScrollbar)
888 if (m_bQuickView)
889 SetColor(COL_PANELSCROLLBAR);
890 else
891 SetColor(COL_VIEWERSCROLLBAR);
893 if (!VM.Hex)
895 ScrollBar(X2+(m_bQuickView?1:0),Y1,Y2-Y1+1,(LastPage ? (!FilePos?0:100):ToPercent64(FilePos,FileSize)),100);
897 else
899 UINT64 Total=FileSize/16+(FileSize%16?1:0);
900 UINT64 Top=FilePos/16+(FilePos%16?1:0);
901 ScrollBarEx(X2+(m_bQuickView?1:0),Y1,Y2-Y1+1,LastPage?Top?Total:0:Top,Total);
907 FARString &Viewer::GetTitle(FARString &strName,int,int)
909 if (!strTitle.IsEmpty())
911 strName = strTitle;
913 else
915 if (!IsAbsolutePath(strFileName))
917 FARString strPath;
918 ViewNamesList.GetCurDir(strPath);
919 AddEndSlash(strPath);
920 strName = strPath+strFileName;
922 else
924 strName = strFileName;
928 return strName;
931 void Viewer::ShowStatus()
933 if (HostFileViewer)
934 HostFileViewer->ShowStatus();
938 void Viewer::SetStatusMode(int Mode)
940 ShowStatusLine=Mode;
944 void Viewer::ReadString(ViewerString *pString, int MaxSize, int StrSize)
946 WCHAR Ch, Ch2;
947 int64_t OutPtr;
948 bool bSelStartFound = false, bSelEndFound = false;
949 pString->bSelection = false;
950 AdjustWidth();
951 OutPtr=0;
953 if (VM.Hex)
955 size_t len = 16;
956 // Alter-1: ::vread accepts number of codepoint units:
957 // 4-bytes for UTF32, 2-bytes for UTF16 and 1-bytes for everything else
958 // But we always display 16 bytes
959 switch (VM.CodePage) {
960 case CP_UTF32LE: case CP_UTF32BE: len/= 4; break;
961 case CP_UTF16LE: case CP_UTF16BE: len/= 2; break;
962 } // TODO: ???
964 OutPtr=vread(pString->lpData, len);
965 pString->lpData[len] = 0;
967 else
969 bool CRSkipped=false;
971 if (SelectSize && vtell() > SelectPos)
973 pString->nSelStart = 0;
974 bSelStartFound = true;
977 for (;;)
979 if (OutPtr>=StrSize-16)
980 break;
982 /* $ 12.07.2000 SVS
983 ! Wrap - трехпозиционный
985 if (VM.Wrap && OutPtr>XX2-X1)
987 /* $ 11.07.2000 tran
988 + warp are now WORD-WRAP */
989 int64_t SavePos=vtell();
990 WCHAR TmpChar=0;
992 if (vgetc(Ch) && Ch!=CRSym && (Ch!=L'\r' || (vgetc(TmpChar) && TmpChar!=CRSym)))
994 vseek(SavePos,SEEK_SET);
996 if (VM.WordWrap)
998 if (!IsSpace(Ch) && !IsSpace(pString->lpData[(int)OutPtr]))
1000 int64_t SavePtr=OutPtr;
1002 /* $ 18.07.2000 tran
1003 добавил в качестве wordwrap разделителей , ; > ) */
1004 while (OutPtr)
1006 Ch2=pString->lpData[(int)OutPtr];
1008 if (IsSpace(Ch2) || Ch2==L',' || Ch2==L';' || Ch2==L'>'|| Ch2==L')')
1009 break;
1011 OutPtr--;
1014 Ch2=pString->lpData[(int)OutPtr];
1016 if (Ch2==L',' || Ch2==L';' || Ch2==L')' || Ch2==L'>')
1017 OutPtr++;
1018 else
1019 while (IsSpace(pString->lpData[(int)OutPtr]) && OutPtr<=SavePtr)
1020 OutPtr++;
1022 if (OutPtr < SavePtr && OutPtr)
1024 vseek(-CalcCodeUnitsDistance(VM.CodePage,
1025 &pString->lpData[(size_t)OutPtr],
1026 &pString->lpData[(size_t)SavePtr]),
1027 SEEK_CUR);
1029 else
1030 OutPtr = SavePtr;
1033 /* $ 13.09.2000 tran
1034 remove space at WWrap */
1036 if (IsSpace(Ch)) {
1037 int64_t lastpos;
1038 for (;;) {
1039 lastpos = vtell();
1040 if (!vgetc(Ch) || !IsSpace(Ch)) break;
1042 vseek(lastpos, SEEK_SET);
1045 }// wwrap
1048 break;
1051 if (SelectSize > 0 && SelectPos==vtell())
1053 pString->nSelStart = OutPtr+(CRSkipped?1:0);;
1054 bSelStartFound = true;
1057 if (!(MaxSize--))
1058 break;
1060 if (!vgetc(Ch))
1061 break;
1063 if (Ch==CRSym)
1064 break;
1066 if (CRSkipped)
1068 CRSkipped=false;
1069 pString->lpData[(int)OutPtr++]=L'\r';
1072 if (Ch==L'\t')
1076 pString->lpData[(int)OutPtr++]=L' ';
1078 while ((OutPtr % ViOpt.TabSize) && ((int)OutPtr < (MAX_VIEWLINEB-1)));
1080 if (VM.Wrap && OutPtr>XX2-X1)
1081 pString->lpData[XX2-X1+1]=0;
1083 continue;
1086 /* $ 20.09.01 IS
1087 Баг: не учитывали левую границу при свертке
1089 if (Ch==L'\r')
1091 CRSkipped=true;
1093 if (OutPtr>=XX2-X1)
1095 int64_t SavePos=vtell();
1096 WCHAR nextCh=0;
1098 if (vgetc(nextCh) && nextCh!=CRSym)
1100 CRSkipped=false;
1103 vseek(SavePos,SEEK_SET);
1106 if (CRSkipped)
1107 continue;
1110 if (!Ch || Ch==L'\n')
1111 Ch=L' ';
1113 pString->lpData[(int)OutPtr++]=Ch;
1114 pString->lpData[(int)OutPtr]=0;
1116 if (SelectSize > 0 && (SelectPos+SelectSize)==vtell())
1118 pString->nSelEnd = OutPtr;
1119 bSelEndFound = true;
1124 pString->lpData[(int)OutPtr]=0;
1126 if (!bSelEndFound && SelectSize && vtell() < SelectPos+SelectSize)
1128 bSelEndFound = true;
1129 pString->nSelEnd = wcslen(pString->lpData);
1132 if (bSelStartFound)
1134 if (pString->nSelStart > (int64_t)wcslen(pString->lpData))
1135 bSelStartFound = false;
1137 if (bSelEndFound)
1138 if (pString->nSelStart > pString->nSelEnd)
1139 bSelStartFound = false;
1142 LastPage=ViewFile.Eof();
1144 if (bSelStartFound && bSelEndFound)
1145 pString->bSelection = true;
1149 int64_t Viewer::VMProcess(int OpCode,void *vParam,int64_t iParam)
1151 switch (OpCode)
1153 case MCODE_C_EMPTY:
1154 return (int64_t)!FileSize;
1155 case MCODE_C_SELECTED:
1156 return (int64_t)(SelectSize?TRUE:FALSE);
1157 case MCODE_C_EOF:
1158 return (int64_t)(LastPage || !ViewFile.Opened());
1159 case MCODE_C_BOF:
1160 return (int64_t)(!FilePos || !ViewFile.Opened());
1161 case MCODE_V_VIEWERSTATE:
1163 DWORD MacroViewerState=0;
1164 MacroViewerState|=VM.Wrap?0x00000008:0;
1165 MacroViewerState|=VM.WordWrap?0x00000010:0;
1166 MacroViewerState|=VM.Hex?0x00000020:0;
1167 MacroViewerState|=Opt.OnlyEditorViewerUsed?0x08000000|0x00000800:0;
1168 MacroViewerState|=HostFileViewer && !HostFileViewer->GetCanLoseFocus()?0x00000800:0;
1169 return (int64_t)MacroViewerState;
1171 case MCODE_V_ITEMCOUNT: // ItemCount - число элементов в текущем объекте
1172 return (int64_t)GetViewFileSize();
1173 case MCODE_V_CURPOS: // CurPos - текущий индекс в текущем объекте
1174 return (int64_t)(GetViewFilePos()+1);
1177 return 0;
1180 /* $ 28.01.2001
1181 - Путем проверки ViewFile на nullptr избавляемся от падения
1183 int Viewer::ProcessKey(int Key)
1185 // Pressing Alt together with PageDown/PageUp allows to smoothly
1186 // boost scrolling speed, releasing Alt while keeping PageDown/PageUp
1187 // will continue scrolling with selected speed. Pressing any other key
1188 // or releasing keys until KEY_IDLE event dismisses scroll speed boost.
1189 if (Key == KEY_ALTPGUP || Key == KEY_ALTPGDN)
1191 if (iBoostPg < 0x100000)
1192 ++iBoostPg;
1194 else if (Key != KEY_PGUP && Key != KEY_PGDN && Key != KEY_NONE && iBoostPg != 0)
1196 fprintf(stderr, "Dismiss iBoostPg=%u due to Key=0x%x\n", iBoostPg, Key);
1197 iBoostPg = 0;
1200 if (Key == KEY_SHIFTLEFT || Key == KEY_SHIFTRIGHT)
1202 if (SelectSize > 0 && (SelectPos > 0 || Key == KEY_SHIFTRIGHT))
1204 int64_t NewSelectSize = SelectSize + 1;
1205 int64_t NewSelectPos = SelectPos;
1206 if (Key == KEY_SHIFTLEFT)
1207 --NewSelectPos;
1208 fprintf(stderr, "SELECTIO CHANGE: [%ld +%ld)\n", (unsigned long)NewSelectPos, NewSelectSize);
1209 SelectText(NewSelectPos, NewSelectSize, SelectFlags);
1211 return TRUE;
1214 /* $ 22.01.2001 IS
1215 Происходят какие-то манипуляции -> снимем выделение
1217 if (!ViOpt.PersistentBlocks &&
1218 Key!=KEY_IDLE && Key!=KEY_NONE && !(Key==KEY_CTRLINS||Key==KEY_CTRLNUMPAD0) && Key!=KEY_CTRLC)
1219 SelectSize=0;
1221 if (!InternalKey && !LastKeyUndo && (FilePos!=UndoData[0].UndoAddr || LeftPos!=UndoData[0].UndoLeft))
1223 for (int i=ARRAYSIZE(UndoData)-1; i>0; i--)
1225 UndoData[i].UndoAddr=UndoData[i-1].UndoAddr;
1226 UndoData[i].UndoLeft=UndoData[i-1].UndoLeft;
1229 UndoData[0].UndoAddr=FilePos;
1230 UndoData[0].UndoLeft=LeftPos;
1233 if (Key!=KEY_ALTBS && Key!=KEY_CTRLZ && Key!=KEY_NONE && Key!=KEY_IDLE)
1234 LastKeyUndo=FALSE;
1236 if (Key>=KEY_CTRL0 && Key<=KEY_CTRL9)
1238 int Pos=Key-KEY_CTRL0;
1240 if (BMSavePos.SavePosAddr[Pos]!=POS_NONE)
1242 FilePos=BMSavePos.SavePosAddr[Pos];
1243 LeftPos=BMSavePos.SavePosLeft[Pos];
1244 // LastSelPos=FilePos;
1245 Show();
1248 return TRUE;
1251 if (Key>=KEY_CTRLSHIFT0 && Key<=KEY_CTRLSHIFT9)
1252 Key=Key-KEY_CTRLSHIFT0+KEY_RCTRL0;
1254 if (Key>=KEY_RCTRL0 && Key<=KEY_RCTRL9)
1256 int Pos=Key-KEY_RCTRL0;
1257 BMSavePos.SavePosAddr[Pos]=FilePos;
1258 BMSavePos.SavePosLeft[Pos]=LeftPos;
1259 return TRUE;
1262 switch (Key)
1264 case KEY_F1:
1266 Help Hlp(L"Viewer");
1267 return TRUE;
1269 case KEY_CTRLU:
1271 // if (SelectSize)
1273 SelectSize = 0;
1274 Show();
1276 return TRUE;
1278 case KEY_CTRLC:
1279 case KEY_CTRLINS: case KEY_CTRLNUMPAD0:
1281 if (SelectSize && ViewFile.Opened())
1283 wchar_t *SelData;
1284 size_t DataSize = (size_t)SelectSize;// + (IsFullWideCodePage(VM.CodePage) ? sizeof(wchar_t) : 1);
1285 switch (VM.CodePage) {
1286 case CP_UTF32LE: case CP_UTF32BE: DataSize+= 4; break;
1287 case CP_UTF16LE: case CP_UTF16BE: DataSize+= 2; break;
1288 default: DataSize++;
1290 int64_t CurFilePos=vtell();
1292 if ((SelData=(wchar_t*)malloc(DataSize*sizeof(wchar_t))) )
1294 wmemset(SelData, 0, DataSize);
1295 vseek(SelectPos,SEEK_SET);
1296 vread(SelData, (int)SelectSize);
1297 CopyToClipboard(SelData);
1298 free(SelData);
1299 vseek(CurFilePos,SEEK_SET);
1303 return TRUE;
1305 // включить/выключить скролбар
1306 case KEY_CTRLS:
1308 ViOpt.ShowScrollbar=!ViOpt.ShowScrollbar;
1309 Opt.ViOpt.ShowScrollbar=ViOpt.ShowScrollbar;
1311 if (m_bQuickView)
1312 CtrlObject->Cp()->ActivePanel->Redraw();
1314 Show();
1315 return (TRUE);
1317 case KEY_IDLE:
1319 if (ViewFile.Opened())
1321 // TODO: strFullFileName -> if (DriveType!=DRIVE_REMOVABLE && !IsDriveTypeCDROM(DriveType))
1323 FAR_FIND_DATA_EX NewViewFindData;
1325 if (!apiGetFindDataForExactPathName(strFullFileName, NewViewFindData))
1326 return TRUE;
1328 ViewFile.ActualizeFileSize();
1329 vseek(0,SEEK_END);
1330 int64_t CurFileSize=vtell();
1332 if (ViewFindData.ftLastWriteTime.dwLowDateTime!=NewViewFindData.ftLastWriteTime.dwLowDateTime ||
1333 ViewFindData.ftLastWriteTime.dwHighDateTime!=NewViewFindData.ftLastWriteTime.dwHighDateTime ||
1334 CurFileSize!=FileSize)
1336 ViewFindData=NewViewFindData;
1337 FileSize=CurFileSize;
1339 if (FilePos>FileSize)
1340 ProcessKey(KEY_CTRLEND);
1341 else
1343 int64_t PrevLastPage=LastPage;
1344 Show();
1346 if (PrevLastPage && !LastPage)
1348 ProcessKey(KEY_CTRLEND);
1349 LastPage=TRUE;
1356 if (Opt.ViewerEditorClock && HostFileViewer && HostFileViewer->IsFullScreen() && Opt.ViOpt.ShowTitleBar)
1357 ShowTime(FALSE);
1359 return TRUE;
1361 case KEY_ALTBS:
1362 case KEY_CTRLZ:
1364 for (size_t I=1; I<ARRAYSIZE(UndoData); I++)
1366 UndoData[I-1].UndoAddr=UndoData[I].UndoAddr;
1367 UndoData[I-1].UndoLeft=UndoData[I].UndoLeft;
1370 if (UndoData[0].UndoAddr!=-1)
1372 FilePos=UndoData[0].UndoAddr;
1373 LeftPos=UndoData[0].UndoLeft;
1374 UndoData[ARRAYSIZE(UndoData)-1].UndoAddr=-1;
1375 UndoData[ARRAYSIZE(UndoData)-1].UndoLeft=-1;
1376 Show();
1377 // LastSelPos=FilePos;
1380 return TRUE;
1382 case KEY_ADD:
1383 case KEY_SUBTRACT:
1385 if (!FileHolder) // if viewing observed (typically temporary) file - dont allow to switch to another file
1387 FARString strName;
1388 bool NextFileFound;
1390 if (Key==KEY_ADD)
1391 NextFileFound=ViewNamesList.GetNextName(strName);
1392 else
1393 NextFileFound=ViewNamesList.GetPrevName(strName);
1395 if (NextFileFound)
1397 if (Opt.ViOpt.SavePos)
1399 FARString strCacheName=ComposeCacheName();
1400 UINT CodePage=0;
1402 if (CodePageChangedByUser)
1403 CodePage=VM.CodePage;
1406 PosCache poscache={};
1407 poscache.Param[0]=FilePos;
1408 poscache.Param[1]=LeftPos;
1409 poscache.Param[2]=VM.Hex;
1410 //=poscache.Param[3];
1411 poscache.Param[4]=CodePage;
1413 if (Opt.ViOpt.SaveShortPos)
1415 poscache.Position[0]=BMSavePos.SavePosAddr;
1416 poscache.Position[1]=BMSavePos.SavePosLeft;
1417 //poscache.Position[2]=;
1418 //poscache.Position[3]=;
1421 CtrlObject->ViewerPosCache->AddPosition(strCacheName,poscache);
1422 memset(&BMSavePos,0xff,sizeof(BMSavePos)); //??!!??
1426 if (PointToName(strName) == strName)
1428 FARString strViewDir;
1429 ViewNamesList.GetCurDir(strViewDir);
1431 if (!strViewDir.IsEmpty())
1432 FarChDir(strViewDir);
1435 if (OpenFile(strName, TRUE))
1437 SecondPos=0;
1438 Show();
1441 ShowConsoleTitle();
1445 return TRUE;
1447 case KEY_SHIFTF2:
1449 ProcessTypeWrapMode(!VM.WordWrap);
1450 return TRUE;
1452 case KEY_F2:
1454 ProcessWrapMode(!VM.Wrap);
1455 return TRUE;
1457 case KEY_F4:
1459 ProcessHexMode(!VM.Hex);
1460 return TRUE;
1462 case KEY_F5:
1464 SavePosCache();
1465 VM.Processed = !VM.Processed;
1466 ChangeViewKeyBar();
1467 FARString reopenFileName = strFileName;
1468 if (VM.Processed || !strProcessedViewName.IsEmpty())
1470 DefCodePage=VM.CodePage;
1471 OpenFile(reopenFileName, TRUE);
1473 Show();
1474 return true;
1476 case KEY_F7:
1478 Search(0,0);
1479 return TRUE;
1481 case KEY_SHIFTF7:
1482 case KEY_SPACE:
1484 Search(1,0);
1485 return TRUE;
1487 case KEY_ALTF7:
1489 SearchFlags.Set(REVERSE_SEARCH);
1490 Search(1,0);
1491 SearchFlags.Clear(REVERSE_SEARCH);
1492 return TRUE;
1494 case KEY_F8:
1496 switch (VM.CodePage) {
1497 case CP_UTF32LE: case CP_UTF32BE:
1498 FilePos*= 4;
1499 SetFileSize();
1500 SelectPos = 0;
1501 SelectSize = 0;
1502 break;
1503 case CP_UTF16LE: case CP_UTF16BE:
1504 FilePos*= 2;
1505 SetFileSize();
1506 SelectPos = 0;
1507 SelectSize = 0;
1508 break;
1511 VM.CodePage = VM.CodePage==WINPORT(GetOEMCP)() ? WINPORT(GetACP)() : WINPORT(GetOEMCP)();
1512 ChangeViewKeyBar();
1513 Show();
1514 // LastSelPos=FilePos;
1515 CodePageChangedByUser=TRUE;
1516 return TRUE;
1518 case KEY_SHIFTF8:
1520 UINT nCodePage = SelectCodePage(VM.CodePage, true, true, false, true);
1521 if (nCodePage == CP_AUTODETECT)
1523 if (!GetFileFormat2(strFileName,nCodePage,nullptr,true,true))
1524 return TRUE;
1527 // BUGBUG
1528 // пока что запретим переключать hex в UTF8/UTF32, ибо не работает.
1529 if (VM.Hex && (nCodePage==CP_UTF8 || nCodePage==CP_UTF32LE || nCodePage==CP_UTF32BE))
1531 return TRUE;
1534 if (nCodePage!=(UINT)-1)
1536 CodePageChangedByUser=TRUE;
1538 if (IsFullWideCodePage(VM.CodePage) && !IsFullWideCodePage(nCodePage))
1540 FilePos*= sizeof(wchar_t);
1541 SelectPos = 0;
1542 SelectSize = 0;
1543 SetCRSym();
1545 else if (!IsFullWideCodePage(VM.CodePage) && IsFullWideCodePage(nCodePage))
1547 FilePos=(FilePos+(FilePos&3))/4; //????
1548 SelectPos = 0;
1549 SelectSize = 0;
1550 SetCRSym();
1553 VM.CodePage=nCodePage;
1554 SetFileSize();
1555 ChangeViewKeyBar();
1556 Show();
1557 // LastSelPos=FilePos;
1560 return TRUE;
1562 case KEY_ALTF8:
1564 if (ViewFile.Opened())
1565 GoTo();
1567 return TRUE;
1569 case KEY_F11:
1571 CtrlObject->Plugins.CommandsMenu(MODALTYPE_VIEWER,0,L"Viewer");
1572 Show();
1573 return TRUE;
1575 /* $ 27.06.2001 VVM
1576 + С альтом скролим по 1 */
1577 case KEY_MSWHEEL_UP:
1578 case(KEY_MSWHEEL_UP | KEY_ALT):
1580 int Roll = Key & KEY_ALT?1:Opt.MsWheelDeltaView;
1582 for (int i=0; i<Roll; i++)
1583 ProcessKey(KEY_UP);
1585 return TRUE;
1587 case KEY_MSWHEEL_DOWN:
1588 case(KEY_MSWHEEL_DOWN | KEY_ALT):
1590 int Roll = Key & KEY_ALT?1:Opt.MsWheelDeltaView;
1592 for (int i=0; i<Roll; i++)
1593 ProcessKey(KEY_DOWN);
1595 return TRUE;
1597 case KEY_MSWHEEL_LEFT:
1598 case(KEY_MSWHEEL_LEFT | KEY_ALT):
1600 int Roll = Key & KEY_ALT?1:Opt.MsHWheelDeltaView;
1602 for (int i=0; i<Roll; i++)
1603 ProcessKey(KEY_LEFT);
1605 return TRUE;
1607 case KEY_MSWHEEL_RIGHT:
1608 case(KEY_MSWHEEL_RIGHT | KEY_ALT):
1610 int Roll = Key & KEY_ALT?1:Opt.MsHWheelDeltaView;
1612 for (int i=0; i<Roll; i++)
1613 ProcessKey(KEY_RIGHT);
1615 return TRUE;
1617 case KEY_UP: case KEY_NUMPAD8: case KEY_SHIFTNUMPAD8:
1619 if (FilePos>0 && ViewFile.Opened())
1621 Up();
1623 if (VM.Hex)
1625 size_t len = 0x8;
1626 switch (VM.CodePage) {
1627 case CP_UTF32LE: case CP_UTF32BE: len*= 4; break;
1628 case CP_UTF16LE: case CP_UTF16BE: len*= 2; break;
1630 FilePos&=~ (len - 1);
1631 Show();
1633 else
1634 ShowPage(SHOW_UP);
1637 // LastSelPos=FilePos;
1638 return TRUE;
1640 case KEY_DOWN: case KEY_NUMPAD2: case KEY_SHIFTNUMPAD2:
1642 if (!LastPage && ViewFile.Opened())
1644 if (VM.Hex)
1646 FilePos=SecondPos;
1647 Show();
1649 else
1650 ShowPage(SHOW_DOWN);
1653 // LastSelPos=FilePos;
1654 return TRUE;
1657 case KEY_ALTPGUP: case KEY_PGUP: case KEY_NUMPAD9: case KEY_SHIFTNUMPAD9: case KEY_CTRLUP:
1659 if (ViewFile.Opened())
1661 const auto InitialFilePos = FilePos;
1662 for (unsigned boost = 0; boost <= iBoostPg; boost+= 4)
1664 for (int i=Y1; i<Y2; i++)
1665 Up();
1667 if ((FilePos - InitialFilePos) * 256 >= FileSize)
1668 { // limit speed boost by FileSize/256 per keypress
1669 // fprintf(stderr, "iBoostPg limited at %u\n", boost);
1670 iBoostPg = boost;
1671 break;
1675 Show();
1676 // LastSelPos=FilePos;
1679 return TRUE;
1682 case KEY_ALTPGDN: case KEY_PGDN: case KEY_NUMPAD3: case KEY_SHIFTNUMPAD3: case KEY_CTRLDOWN:
1684 ViewerString vString;
1685 vString.lpData[MAX_VIEWLINEB] = 0;
1686 const auto InitialFilePos = FilePos;
1687 for (unsigned boost = 0; boost <= iBoostPg; boost+= 4)
1689 if (LastPage || !ViewFile.Opened())
1691 return TRUE;
1694 vseek(FilePos,SEEK_SET);
1696 for (int i=Y1; i<Y2; i++)
1698 ReadString(&vString,-1, MAX_VIEWLINEB);
1700 if (LastPage)
1702 return TRUE;
1706 FilePos=vtell();
1708 for (int i=Y1; i<=Y2; i++)
1709 ReadString(&vString,-1, MAX_VIEWLINEB);
1711 /* $ 02.06.2003 VVM
1712 + Старое поведение оставим на Ctrl-Down */
1714 /* $ 21.05.2003 VVM
1715 + По PgDn листаем всегда по одной странице,
1716 даже если осталась всего одна строчка.
1717 Удобно тексты читать */
1718 if (LastPage && Key == KEY_CTRLDOWN)
1720 InternalKey++;
1721 ProcessKey(KEY_CTRLPGDN);
1722 InternalKey--;
1723 return TRUE;
1726 if ((FilePos - InitialFilePos) * 256 >= FileSize)
1727 { // limit speed boost by FileSize/256 per keypress
1728 // fprintf(stderr, "iBoostPg limited at %u\n", boost);
1729 iBoostPg = boost;
1730 break;
1733 Show();
1735 // LastSelPos=FilePos;
1736 return TRUE;
1738 case KEY_LEFT: case KEY_NUMPAD4: case KEY_SHIFTNUMPAD4:
1740 if (LeftPos>0 && ViewFile.Opened())
1742 if (VM.Hex && LeftPos>80-Width)
1743 LeftPos=Max(80-Width,1);
1745 LeftPos--;
1746 Show();
1749 // LastSelPos=FilePos;
1750 return TRUE;
1752 case KEY_RIGHT: case KEY_NUMPAD6: case KEY_SHIFTNUMPAD6:
1754 if (LeftPos<MAX_VIEWLINE && ViewFile.Opened() && !VM.Hex && !VM.Wrap)
1756 LeftPos++;
1757 Show();
1760 // LastSelPos=FilePos;
1761 return TRUE;
1763 case KEY_CTRLLEFT: case KEY_CTRLNUMPAD4:
1765 if (ViewFile.Opened())
1767 if (VM.Hex)
1769 FilePos--;
1771 if (FilePos<0)
1772 FilePos=0;
1774 else
1776 LeftPos-=20;
1778 if (LeftPos<0)
1779 LeftPos=0;
1782 Show();
1783 // LastSelPos=FilePos;
1786 return TRUE;
1788 case KEY_CTRLRIGHT: case KEY_CTRLNUMPAD6:
1790 if (ViewFile.Opened())
1792 if (VM.Hex)
1794 FilePos++;
1796 if (FilePos >= FileSize)
1797 FilePos=FileSize-1; //??
1799 else if (!VM.Wrap)
1801 LeftPos+=20;
1803 if (LeftPos>MAX_VIEWLINE)
1804 LeftPos=MAX_VIEWLINE;
1807 Show();
1808 // LastSelPos=FilePos;
1811 return TRUE;
1813 case KEY_CTRLSHIFTLEFT: case KEY_CTRLSHIFTNUMPAD4:
1815 // Перейти на начало строк
1816 if (ViewFile.Opened())
1818 LeftPos = 0;
1819 Show();
1822 return TRUE;
1823 case KEY_CTRLSHIFTRIGHT: case KEY_CTRLSHIFTNUMPAD6:
1825 // Перейти на конец строк
1826 if (ViewFile.Opened())
1828 int I, Y, Len, MaxLen = 0;
1830 for (I=0,Y=Y1; Y<=Y2; Y++,I++)
1832 Len = StrLength(Strings[I]->lpData);
1834 if (Len > MaxLen)
1835 MaxLen = Len;
1836 } /* for */
1838 if (MaxLen > Width)
1839 LeftPos = MaxLen - Width;
1840 else
1841 LeftPos = 0;
1843 Show();
1844 } /* if */
1846 return TRUE;
1848 case KEY_CTRLHOME: case KEY_CTRLNUMPAD7:
1849 case KEY_HOME: case KEY_NUMPAD7: case KEY_SHIFTNUMPAD7:
1851 // Перейти на начало файла
1852 if (ViewFile.Opened())
1853 LeftPos=0;
1855 case KEY_CTRLPGUP: case KEY_CTRLNUMPAD9:
1857 if (ViewFile.Opened())
1859 FilePos=0;
1860 Show();
1861 // LastSelPos=FilePos;
1864 return TRUE;
1865 case KEY_CTRLEND: case KEY_CTRLNUMPAD1:
1866 case KEY_END: case KEY_NUMPAD1: case KEY_SHIFTNUMPAD1:
1868 // Перейти на конец файла
1869 if (ViewFile.Opened())
1870 LeftPos=0;
1872 case KEY_CTRLPGDN: case KEY_CTRLNUMPAD3:
1874 if (ViewFile.Opened())
1876 /* $ 15.08.2002 IS
1877 Для обычного режима, если последняя строка не содержит перевод
1878 строки, крутанем вверх на один раз больше - иначе визуально
1879 обработка End (и подобных) на такой строке отличается от обработки
1880 Down.
1882 unsigned int max_counter=Y2-Y1;
1884 if (VM.Hex)
1885 vseek(0,SEEK_END);
1886 else
1888 vseek(-1,SEEK_END);
1889 WCHAR LastSym=0;
1891 if (vgetc(LastSym) && LastSym!=CRSym)
1892 ++max_counter;
1895 FilePos=vtell();
1899 char Buf[100];
1900 sprintf(Buf,"%llX",FilePos);
1901 Message(0,1,"End",Buf,"Ok");
1904 for (int i=0; static_cast<unsigned int>(i)<max_counter; i++)
1905 Up();
1909 char Buf[100];
1910 sprintf(Buf,"%llX, %d",FilePos, I);
1911 Message(0,1,"Up",Buf,"Ok");
1914 if (VM.Hex) {
1915 size_t len = 0x8;
1916 switch (VM.CodePage) {
1917 case CP_UTF32LE: case CP_UTF32BE: len*= 4; break;
1918 case CP_UTF16LE: case CP_UTF16BE: len*= 2; break;
1920 FilePos&= ~(len - 1);
1924 if (VM.Hex)
1926 char Buf[100];
1927 sprintf(Buf,"%llX",FilePos);
1928 Message(0,1,"VM.Hex",Buf,"Ok");
1931 Show();
1932 // LastSelPos=FilePos;
1935 return TRUE;
1936 default:
1938 if (Key>=L' ' && Key<0x10000)
1940 Search(0,Key);
1941 return TRUE;
1945 return FALSE;
1948 int Viewer::ProcessMouse(MOUSE_EVENT_RECORD *MouseEvent)
1950 if (!(MouseEvent->dwButtonState & 3))
1951 return FALSE;
1953 // Shift + Mouse click -> adhoc quick edit
1954 if ( (MouseEvent->dwControlKeyState & SHIFT_PRESSED) != 0
1955 && (MouseEvent->dwEventFlags & MOUSE_MOVED) == 0
1956 && (MouseEvent->dwButtonState & FROM_LEFT_1ST_BUTTON_PRESSED) != 0 ) {
1957 WINPORT(BeginConsoleAdhocQuickEdit)();
1958 return TRUE;
1961 /* $ 22.01.2001 IS
1962 Происходят какие-то манипуляции -> снимем выделение
1964 // SelectSize=0;
1966 /* $ 10.09.2000 SVS
1967 ! Постоянный скроллинг при нажатой клавише
1968 Обыкновенный захват мыши
1970 /* $ 02.10.2000 SVS
1971 > Если нажать в самом низу скролбара, вьюер отмотается на страницу
1972 > ниже нижней границы текста. Перед глазами будет пустой экран.
1974 if (ViOpt.ShowScrollbar && MouseX==X2+(m_bQuickView?1:0))
1976 /* $ 01.09.2000 SVS
1977 Небольшая бага с тыканием в верхнюю позицию ScrollBar`а
1979 if (MouseY == Y1)
1980 while (IsMouseButtonPressed())
1981 ProcessKey(KEY_UP);
1982 else if (MouseY==Y2)
1984 while (IsMouseButtonPressed())
1986 // _SVS(SysLog(L"Viewer/ KEY_DOWN= %i, %i",FilePos,FileSize));
1987 ProcessKey(KEY_DOWN);
1990 else if (MouseY == Y1+1)
1991 ProcessKey(KEY_CTRLHOME);
1992 else if (MouseY == Y2-1)
1993 ProcessKey(KEY_CTRLEND);
1994 else
1996 while (IsMouseButtonPressed())
1998 /* $ 14.05.2001 DJ
1999 более точное позиционирование; корректная работа на больших файлах
2001 FilePos=(FileSize-1)/(Y2-Y1-1)*(MouseY-Y1);
2002 int Perc;
2004 if (FilePos > FileSize)
2006 FilePos=FileSize;
2007 Perc=100;
2009 else if (FilePos < 0)
2011 FilePos=0;
2012 Perc=0;
2014 else
2015 Perc=ToPercent64(FilePos,FileSize);
2017 //_SVS(SysLog(L"Viewer/ ToPercent()=%i, %lld, %lld, Mouse=[%d:%d]",Perc,FilePos,FileSize,MsX,MsY));
2018 if (Perc == 100)
2019 ProcessKey(KEY_CTRLEND);
2020 else if (!Perc)
2021 ProcessKey(KEY_CTRLHOME);
2022 else
2024 /* $ 27.04.2001 DJ
2025 не рвем строки посередине
2027 AdjustFilePos();
2028 Show();
2033 return (TRUE);
2036 /* $ 16.12.2000 tran
2037 шелчок мышью на статус баре */
2039 /* $ 12.10.2001 SKV
2040 угу, а только если он нсть, statusline...
2042 if (MouseY == (Y1-1) && (HostFileViewer && HostFileViewer->IsTitleBarVisible())) // Status line
2044 int XCodePage, XPos, NameLength;
2045 NameLength=ObjWidth-40;
2047 if (Opt.ViewerEditorClock && HostFileViewer && HostFileViewer->IsFullScreen())
2048 NameLength-=6;
2050 if (NameLength<20)
2051 NameLength=20;
2053 XCodePage=NameLength+1;
2054 XPos=NameLength+1+10+1+10+1;
2056 while (IsMouseButtonPressed());
2058 if (MouseY != Y1-1)
2059 return TRUE;
2061 //_D(SysLog(L"MsX=%i, XTable=%i, XPos=%i",MsX,XTable,XPos));
2062 if (MouseX>=XCodePage && MouseX<=XCodePage+10)
2064 ProcessKey(KEY_SHIFTF8);
2065 return (TRUE);
2068 if (MouseX>=XPos && MouseX<=XPos+7+1+4+1+3)
2070 ProcessKey(KEY_ALTF8);
2071 return (TRUE);
2075 if (MouseX<X1 || MouseX>X2 || MouseY<Y1 || MouseY>Y2)
2076 return FALSE;
2078 if (MouseX<X1+7)
2079 while (IsMouseButtonPressed() && MouseX<X1+7)
2080 ProcessKey(KEY_LEFT);
2081 else if (MouseX>X2-7)
2082 while (IsMouseButtonPressed() && MouseX>X2-7)
2083 ProcessKey(KEY_RIGHT);
2084 else if (MouseY<Y1+(Y2-Y1)/2)
2085 while (IsMouseButtonPressed() && MouseY<Y1+(Y2-Y1)/2)
2086 ProcessKey(KEY_UP);
2087 else
2088 while (IsMouseButtonPressed() && MouseY>=Y1+(Y2-Y1)/2)
2089 ProcessKey(KEY_DOWN);
2091 return TRUE;
2094 void Viewer::FilePosShiftLeft(uint64_t Offset)
2096 if (FilePos > 0 && (uint64_t)FilePos > Offset) {
2097 FilePos-= Offset;
2098 } else
2099 FilePos = 0;
2102 void Viewer::Up()
2104 if (!ViewFile.Opened())
2105 return;
2107 wchar_t Buf[MAX_VIEWLINE + 1];
2108 int BufSize,I;
2110 if (FilePos > ((int64_t)(sizeof(Buf)/sizeof(wchar_t))) - 1)
2111 BufSize = (sizeof(Buf)/sizeof(wchar_t)) - 1;
2112 else if (FilePos != 0)
2113 BufSize = (int)FilePos;
2114 else
2115 return;
2117 LastPage=0;
2119 if (VM.Hex)
2121 // Alter-1: here we use BYTE COUNT, while in Down handler we use ::vread which may
2122 // accept either CHARACTER COUNT or w_char count.
2123 //int UpSize=IsFullWideCodePage(VM.CodePage) ? 8 : 8 * sizeof(wchar_t);
2124 int UpSize=16; // always have 16 bytes per row
2126 if (FilePos<(int64_t)UpSize)
2127 FilePos=0;
2128 else
2129 FilePosShiftLeft(UpSize);
2131 return;
2134 vseek(FilePos - (int64_t)BufSize, SEEK_SET);
2135 I = BufSize = vread(Buf, BufSize, true);
2136 if (I == -1)
2138 return;
2141 wchar_t CRSymEncoded = (unsigned int)CRSym;
2142 wchar_t CRRSymEncoded = (unsigned int)'\r';
2143 switch (VM.CodePage)
2145 case CP_UTF32BE:
2146 CRSymEncoded<<= 24;
2147 CRRSymEncoded<<= 24;
2148 break;
2149 case CP_UTF16BE:
2150 CRSymEncoded<<= 8;
2151 CRRSymEncoded<<= 8;
2152 break;
2155 if (I > 0 && Buf[I - 1] == CRSymEncoded)
2157 --I;
2160 if (I > 0 && CRSym == L'\n' && Buf[I - 1] == CRRSymEncoded)
2162 --I;
2165 while (I > 0 && Buf[I - 1] != CRSymEncoded)
2167 --I;
2170 int64_t WholeLineLength = (BufSize - I); // in code units
2172 if (VM.Wrap && Width > 0)
2174 vseek(FilePos - WholeLineLength, SEEK_SET);
2175 int WrapBufSize = vread(Buf, WholeLineLength, false);
2177 // we might read more code units and could actually overflow current position
2178 // so try to find out exact substring that matches into line start and current position
2179 Buf[WrapBufSize] = 0;
2180 // fprintf(stderr, "WrapBufSize=%d WholeLineLength=%d LINE='%ls'\n", WrapBufSize, WholeLineLength, &Buf[0]);
2181 // fprintf(stderr, "LINE1: '%ls'\n", &Buf[0]);
2182 while (WrapBufSize)
2184 int distance = CalcCodeUnitsDistance(VM.CodePage, &Buf[0], &Buf[WrapBufSize]);
2185 if (distance <= WholeLineLength)
2187 while (WrapBufSize && distance == CalcCodeUnitsDistance(VM.CodePage, &Buf[0], &Buf[WrapBufSize - 1]))
2189 --WrapBufSize;
2191 break;
2193 --WrapBufSize;
2195 Buf[WrapBufSize] = 0;
2196 // fprintf(stderr, "Matching LINE: '%ls'\n", &Buf[0]);
2198 if (VM.WordWrap)
2200 // khffgkjkfdg dfkghd jgfhklf |
2201 // sdflksj lfjghf fglh lf |
2202 // dfdffgljh ldgfhj |
2204 for (I = 0; I < WrapBufSize;)
2206 if (!IsSpace(Buf[I]))
2208 for (int CurLineStart = I, LastFitEnd = I + 1;; ++I)
2210 if (I == WrapBufSize)
2212 int distance = CalcCodeUnitsDistance(VM.CodePage, &Buf[CurLineStart], &Buf[WrapBufSize]);
2213 FilePosShiftLeft((distance > 0) ? distance : 1);
2214 return;
2217 if (!IsSpace(Buf[I]) && (I + 1 == WrapBufSize || IsSpace(Buf[I + 1])))
2219 if (CalcStrSize(&Buf[CurLineStart], I + 1 - CurLineStart) > Width)
2221 I = LastFitEnd;
2222 break;
2224 LastFitEnd = I + 1;
2228 } else
2230 ++I;
2235 for (int PrevSublineLength = 0, CurLineStart = I = 0;; ++I)
2237 if (I == WrapBufSize)
2239 int distance = CalcCodeUnitsDistance(VM.CodePage, &Buf[CurLineStart], &Buf[WrapBufSize]);
2240 FilePosShiftLeft((distance > 0) ? distance : 1);
2241 return;
2244 int SublineLength = CalcStrSize(&Buf[CurLineStart], I - CurLineStart);
2245 if (SublineLength > PrevSublineLength)
2247 if (SublineLength >= Width)
2249 CurLineStart = I;
2250 PrevSublineLength = 0;
2251 } else
2253 PrevSublineLength = SublineLength;
2259 // fprintf(stderr, "!!!!!!!!!!!!NOWRAP!!!!!!!!!!!!\n");
2260 FilePosShiftLeft( (WholeLineLength > 0) ? WholeLineLength : 1 );
2264 int Viewer::CalcStrSize(const wchar_t *Str,int Length)
2266 int Size,I;
2268 for (Size=0,I=0; I<Length; I++)
2269 switch (Str[I])
2271 case L'\t':
2272 Size+=ViOpt.TabSize-(Size % ViOpt.TabSize);
2273 break;
2274 case L'\n':
2275 case L'\r':
2276 break;
2277 default:
2278 Size++;
2279 break;
2282 return(Size);
2285 int Viewer::GetStrBytesNum(const wchar_t *Str, int Length)
2287 if (VM.CodePage == CP_UTF32LE || VM.CodePage == CP_UTF32BE)
2288 return Length;
2290 int cnt = WINPORT(WideCharToMultiByte)(VM.CodePage, 0, Str, Length, nullptr, 0, nullptr, nullptr);
2292 return (VM.CodePage == CP_UTF16LE || VM.CodePage == CP_UTF16BE) ? cnt * 2 : cnt;
2296 void Viewer::SetViewKeyBar(KeyBar *ViewKeyBar)
2298 Viewer::ViewKeyBar=ViewKeyBar;
2299 ChangeViewKeyBar();
2303 void Viewer::ChangeViewKeyBar()
2305 if (ViewKeyBar)
2307 /* $ 12.07.2000 SVS
2308 Wrap имеет 3 позиции
2310 /* $ 15.07.2000 SVS
2311 Wrap должен показываться следующий, а не текущий
2313 ViewKeyBar->Change(
2314 VM.Wrap ? Msg::ViewF2Unwrap : (VM.WordWrap ? Msg::ViewShiftF2 : Msg::ViewF2),
2316 ViewKeyBar->Change(KBL_SHIFT, VM.WordWrap ? Msg::ViewF2 : Msg::ViewShiftF2, 1);
2318 if (VM.Hex)
2319 ViewKeyBar->Change(Msg::ViewF4Text,3);
2320 else
2321 ViewKeyBar->Change(Msg::ViewF4,3);
2323 if (VM.CodePage != WINPORT(GetOEMCP)())
2324 ViewKeyBar->Change(Msg::ViewF8DOS,7);
2325 else
2326 ViewKeyBar->Change(Msg::ViewF8,7);
2328 if (VM.Processed)
2329 ViewKeyBar->Change(Msg::ViewF5Raw,4);
2330 else
2331 ViewKeyBar->Change(Msg::ViewF5Processed,4);
2333 ViewKeyBar->Redraw();
2336 // ViewerMode vm=VM;
2337 CtrlObject->Plugins.CurViewer=this; //HostFileViewer;
2338 // CtrlObject->Plugins.ProcessViewerEvent(VE_MODE,&vm);
2341 enum SEARCHDLG
2343 SD_DOUBLEBOX,
2344 SD_TEXT_SEARCH,
2345 SD_EDIT_TEXT,
2346 SD_EDIT_HEX,
2347 SD_SEPARATOR1,
2348 SD_RADIO_TEXT,
2349 SD_RADIO_HEX,
2350 SD_CHECKBOX_CASE,
2351 SD_CHECKBOX_WORDS,
2352 SD_CHECKBOX_REVERSE,
2353 SD_CHECKBOX_REGEXP,
2354 SD_SEPARATOR2,
2355 SD_BUTTON_OK,
2356 SD_BUTTON_CANCEL,
2358 DM_SDSETVISIBILITY = DM_USER + 1,
2361 LONG_PTR WINAPI ViewerSearchDlgProc(HANDLE hDlg,int Msg,int Param1,LONG_PTR Param2)
2363 switch (Msg)
2365 case DN_INITDIALOG:
2367 SendDlgMessage(hDlg,DM_SDSETVISIBILITY,SendDlgMessage(hDlg,DM_GETCHECK,SD_RADIO_HEX,0) == BSTATE_CHECKED,0);
2368 SendDlgMessage(hDlg,DM_EDITUNCHANGEDFLAG,SD_EDIT_TEXT,1);
2369 SendDlgMessage(hDlg,DM_EDITUNCHANGEDFLAG,SD_EDIT_HEX,1);
2370 return TRUE;
2372 case DM_SDSETVISIBILITY:
2374 SendDlgMessage(hDlg,DM_SHOWITEM,SD_EDIT_TEXT,!Param1);
2375 SendDlgMessage(hDlg,DM_SHOWITEM,SD_EDIT_HEX,Param1);
2376 SendDlgMessage(hDlg,DM_ENABLE,SD_CHECKBOX_CASE,!Param1);
2377 SendDlgMessage(hDlg,DM_ENABLE,SD_CHECKBOX_WORDS,!Param1);
2378 //SendDlgMessage(hDlg,DM_ENABLE,SD_CHECKBOX_REGEXP,!Param1);
2379 return TRUE;
2381 case DN_BTNCLICK:
2383 if ((Param1 == SD_RADIO_TEXT || Param1 == SD_RADIO_HEX) && Param2)
2385 SendDlgMessage(hDlg,DM_ENABLEREDRAW,FALSE,0);
2386 bool Hex=(Param1==SD_RADIO_HEX);
2387 FARString strDataStr;
2388 Transform(strDataStr,(const wchar_t *)SendDlgMessage(hDlg,DM_GETCONSTTEXTPTR,Hex?SD_EDIT_TEXT:SD_EDIT_HEX,0),Hex?L'X':L'S');
2389 SendDlgMessage(hDlg,DM_SETTEXTPTR,Hex?SD_EDIT_HEX:SD_EDIT_TEXT,(LONG_PTR)strDataStr.CPtr());
2390 SendDlgMessage(hDlg,DM_SDSETVISIBILITY,Hex,0);
2392 if (!strDataStr.IsEmpty())
2394 SendDlgMessage(hDlg,DM_EDITUNCHANGEDFLAG,Hex?SD_EDIT_HEX:SD_EDIT_TEXT,SendDlgMessage(hDlg,DM_EDITUNCHANGEDFLAG,Hex?SD_EDIT_TEXT:SD_EDIT_HEX,-1));
2397 SendDlgMessage(hDlg,DM_ENABLEREDRAW,TRUE,0);
2398 return TRUE;
2401 case DN_HOTKEY:
2403 if (Param1==SD_TEXT_SEARCH)
2405 SendDlgMessage(hDlg,DM_SETFOCUS,(SendDlgMessage(hDlg,DM_GETCHECK,SD_RADIO_HEX,0) == BSTATE_CHECKED)?SD_EDIT_HEX:SD_EDIT_TEXT,0);
2406 return FALSE;
2411 return DefDlgProc(hDlg,Msg,Param1,Param2);
2414 static void PR_ViewerSearchMsg()
2416 PreRedrawItem preRedrawItem=PreRedraw.Peek();
2417 ViewerSearchMsg((const wchar_t*)preRedrawItem.Param.Param1,(int)(INT_PTR)preRedrawItem.Param.Param2);
2420 void ViewerSearchMsg(const wchar_t *MsgStr,int Percent)
2422 FARString strProgress;
2424 if (Percent>=0)
2426 FormatString strPercent;
2427 strPercent<<Percent;
2429 size_t PercentLength=Max(strPercent.strValue().GetLength(),(size_t)3);
2430 size_t Length=Max(Min(static_cast<int>(MAX_WIDTH_MESSAGE-2),StrLength(MsgStr)),40)-PercentLength-2;
2431 wchar_t *Progress=strProgress.GetBuffer(Length);
2433 if (Progress)
2435 size_t CurPos=Min(Percent,100)*Length/100;
2436 wmemset(Progress,BoxSymbols[BS_X_DB],CurPos);
2437 wmemset(Progress+(CurPos),BoxSymbols[BS_X_B0],Length-CurPos);
2438 strProgress.ReleaseBuffer(Length);
2439 FormatString strTmp;
2440 strTmp<<L" "<<fmt::Expand(PercentLength)<<strPercent<<L"%";
2441 strProgress+=strTmp;
2446 Message(0,0,Msg::ViewSearchTitle,(SearchHex?Msg::ViewSearchingHex:Msg::ViewSearchingFor),MsgStr,strProgress.IsEmpty()?nullptr:strProgress.CPtr());
2447 PreRedrawItem preRedrawItem=PreRedraw.Peek();
2448 preRedrawItem.Param.Param1=(void*)MsgStr;
2449 preRedrawItem.Param.Param2=(LPVOID)(INT_PTR)Percent;
2450 PreRedraw.SetParam(preRedrawItem.Param);
2453 /* $ 27.01.2003 VVM
2454 + Параметр Next может принимать значения:
2455 0 - Новый поиск
2456 1 - Продолжить поиск со следующей позиции
2457 2 - Продолжить поиск с начала файла
2461 static inline bool CheckBufMatchesCaseInsensitive(size_t MatchLen, const wchar_t *Buf, const wchar_t *MatchUpperCase, const wchar_t *MatchLowerCase)
2463 for (size_t i = 0; i < MatchLen; ++i) {
2464 if (Buf[i] != MatchUpperCase[i] && Buf[i] != MatchLowerCase[i])
2465 return false;
2468 return true;
2471 static inline bool CheckBufMatchesCaseSensitive(size_t MatchLen, const wchar_t *Buf, const wchar_t *Match)
2473 for (size_t i = 0; i < MatchLen; ++i) {
2474 if (Buf[i] != Match[i])
2475 return false;
2478 return true;
2481 void Viewer::Search(int Next,int FirstChar)
2483 const wchar_t *TextHistoryName=L"SearchText";
2484 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 ";
2485 DialogDataEx SearchDlgData[]=
2487 {DI_DOUBLEBOX,3,1,72,11,{0},0,Msg::ViewSearchTitle},
2488 {DI_TEXT,5,2,0,2,{0},0,Msg::ViewSearchFor},
2489 {DI_EDIT,5,3,70,3,{(DWORD_PTR)TextHistoryName},DIF_FOCUS|DIF_HISTORY|DIF_USELASTHISTORY,L""},
2490 {DI_FIXEDIT,5,3,70,3,{(DWORD_PTR)HexMask},DIF_MASKEDIT,L""},
2491 {DI_TEXT,3,4,0,4,{0},DIF_SEPARATOR,L""},
2492 {DI_RADIOBUTTON,5,5,0,5,{1},DIF_GROUP,Msg::ViewSearchForText},
2493 {DI_RADIOBUTTON,5,6,0,6,{0},0,Msg::ViewSearchForHex},
2494 {DI_CHECKBOX,40,5,0,5,{0},0,Msg::ViewSearchCase},
2495 {DI_CHECKBOX,40,6,0,6,{0},0,Msg::ViewSearchWholeWords},
2496 {DI_CHECKBOX,40,7,0,7,{0},0,Msg::ViewSearchReverse},
2497 {DI_CHECKBOX,40,8,0,8,{0},DIF_DISABLE,Msg::ViewSearchRegexp},
2498 {DI_TEXT,3,9,0,9,{0},DIF_SEPARATOR,L""},
2499 {DI_BUTTON,0,10,0,10,{0},DIF_DEFAULT|DIF_CENTERGROUP,Msg::ViewSearchSearch},
2500 {DI_BUTTON,0,10,0,10,{0},DIF_CENTERGROUP,Msg::ViewSearchCancel}
2502 MakeDialogItemsEx(SearchDlgData,SearchDlg);
2503 FARString strSearchStr;
2504 FARString strMsgStr;
2505 int64_t MatchPos=0;
2506 int Case,WholeWords,ReverseSearch,SearchRegexp;
2508 if (!ViewFile.Opened() || (Next && strLastSearchStr.IsEmpty()))
2509 return;
2511 if (!strLastSearchStr.IsEmpty())
2512 strSearchStr = strLastSearchStr;
2513 else
2514 strSearchStr.Clear();
2516 SearchDlg[SD_RADIO_TEXT].Selected=!LastSearchHex;
2517 SearchDlg[SD_RADIO_HEX].Selected=LastSearchHex;
2518 SearchDlg[SD_CHECKBOX_CASE].Selected=LastSearchCase;
2519 SearchDlg[SD_CHECKBOX_WORDS].Selected=LastSearchWholeWords;
2520 SearchDlg[SD_CHECKBOX_REVERSE].Selected=LastSearchReverse;
2521 SearchDlg[SD_CHECKBOX_REGEXP].Selected=LastSearchRegexp;
2523 if (SearchFlags.Check(REVERSE_SEARCH))
2524 SearchDlg[SD_CHECKBOX_REVERSE].Selected=!SearchDlg[SD_CHECKBOX_REVERSE].Selected;
2526 if (IsFullWideCodePage(VM.CodePage))
2528 SearchDlg[SD_RADIO_TEXT].Selected=TRUE;
2529 SearchDlg[SD_RADIO_HEX].Flags|=DIF_DISABLE;
2530 SearchDlg[SD_RADIO_HEX].Selected=FALSE;
2533 if (SearchDlg[SD_RADIO_HEX].Selected)
2534 SearchDlg[SD_EDIT_HEX].strData = strSearchStr;
2535 else
2536 SearchDlg[SD_EDIT_TEXT].strData = strSearchStr;
2538 if (!Next)
2540 SearchFlags.Flags = 0;
2541 Dialog Dlg(SearchDlg,ARRAYSIZE(SearchDlg),ViewerSearchDlgProc);
2542 Dlg.SetPosition(-1,-1,76,13);
2543 Dlg.SetHelp(L"ViewerSearch");
2545 if (FirstChar)
2547 Dlg.InitDialog();
2548 Dlg.Show();
2549 Dlg.ProcessKey(FirstChar);
2552 Dlg.Process();
2554 if (Dlg.GetExitCode()!=SD_BUTTON_OK)
2555 return;
2558 SearchHex=SearchDlg[SD_RADIO_HEX].Selected;
2559 Case=SearchDlg[SD_CHECKBOX_CASE].Selected;
2560 WholeWords=SearchDlg[SD_CHECKBOX_WORDS].Selected;
2561 ReverseSearch=SearchDlg[SD_CHECKBOX_REVERSE].Selected;
2562 SearchRegexp=SearchDlg[SD_CHECKBOX_REGEXP].Selected;
2564 if (SearchHex)
2566 strSearchStr = SearchDlg[SD_EDIT_HEX].strData;
2567 RemoveTrailingSpaces(strSearchStr);
2569 else
2571 strSearchStr = SearchDlg[SD_EDIT_TEXT].strData;
2574 strLastSearchStr = strSearchStr;
2575 LastSearchHex=SearchHex;
2576 LastSearchCase=Case;
2577 LastSearchWholeWords=WholeWords;
2579 if (!SearchFlags.Check(REVERSE_SEARCH))
2580 LastSearchReverse=ReverseSearch;
2582 LastSearchRegexp=SearchRegexp;
2584 int SearchWChars, SearchCodeUnits;
2585 bool Match = false;
2587 TPreRedrawFuncGuard preRedrawFuncGuard(PR_ViewerSearchMsg);
2588 //SaveScreen SaveScr;
2589 SetCursorType(FALSE,0);
2590 strMsgStr = strSearchStr;
2592 if (strMsgStr.GetLength()+18 > static_cast<DWORD>(ObjWidth))
2593 TruncStrFromEnd(strMsgStr, ObjWidth-18);
2595 InsertQuote(strMsgStr);
2597 if (SearchHex)
2599 if (!strSearchStr.GetLength())
2600 return;
2602 Transform(strSearchStr,strSearchStr,L'S');
2603 WholeWords=0;
2606 SearchWChars = (int)strSearchStr.GetLength();
2607 if (!SearchWChars)
2608 return;
2610 SearchCodeUnits = CalcCodeUnitsDistance(VM.CodePage, strSearchStr.CPtr(), strSearchStr.CPtr() + SearchWChars);
2611 FARString strSearchStrLowerCase;
2613 if (!Case && !SearchHex)
2615 strSearchStrLowerCase = strSearchStr;
2616 strSearchStr.Upper();
2617 strSearchStrLowerCase.Lower();
2620 SelectSize = 0;
2622 if (Next)
2624 if (Next == 2)
2626 SearchFlags.Set(SEARCH_MODE2);
2627 LastSelPos = ReverseSearch?FileSize:0;
2629 #if 0
2630 else
2632 LastSelPos = SelectPos + (ReverseSearch?-1:1);
2634 #endif
2636 else
2638 LastSelPos = FilePos;
2640 if (!LastSelPos || LastSelPos == FileSize)
2642 SearchFlags.Set(SEARCH_MODE2);
2643 LastSelPos = ReverseSearch?FileSize:0;
2647 vseek(LastSelPos,SEEK_SET);
2648 Match = false;
2650 if (SearchWChars>0 && (!ReverseSearch || LastSelPos>=0))
2652 wchar_t Buf[16384];
2654 int ReadSize;
2655 wakeful W;
2656 INT64 StartPos=vtell();
2657 DWORD StartTime=WINPORT(GetTickCount)();
2659 while (!Match)
2661 /* $ 01.08.2000 KM
2662 Изменена строка if (ReverseSearch && CurPos<0) на if (CurPos<0),
2663 так как при обычном прямом и LastSelPos=0xFFFFFFFF, поиск
2664 заканчивался так и не начавшись.
2666 //if (CurPos<0)
2667 // CurPos=0;
2668 //vseek(CurPos,SEEK_SET);
2669 int BufSize = ARRAYSIZE(Buf);
2670 int64_t CurPos = vtell();
2671 if (ReverseSearch)
2673 /* $ 01.08.2000 KM
2674 Изменёно вычисление CurPos с учётом Whole words
2676 CurPos-= ARRAYSIZE(Buf) - SearchCodeUnits - !!WholeWords;
2677 if (CurPos < 0) {
2678 BufSize+= (int)CurPos;
2679 CurPos = 0;
2681 vseek(CurPos, SEEK_SET);
2684 if ((ReadSize=vread(Buf,BufSize,SearchHex!=0))<=0)
2685 break;
2687 DWORD CurTime=WINPORT(GetTickCount)();
2689 if (CurTime-StartTime>RedrawTimeout)
2691 StartTime=CurTime;
2693 if (CheckForEscSilent())
2695 if (ConfirmAbortOp())
2697 Redraw();
2698 return;
2702 INT64 Total=ReverseSearch?StartPos:FileSize-StartPos;
2703 INT64 Current=_abs64(CurPos-StartPos);
2704 int Percent=Total>0?static_cast<int>(Current*100/Total):-1;
2705 // В случае если файл увеличивается размере, то количество
2706 // процентов может быть больше 100. Обрабатываем эту ситуацию.
2707 if (Percent>100)
2709 SetFileSize();
2710 Total=FileSize-StartPos;
2711 Percent=Total>0?static_cast<int>(Current*100/Total):-1;
2712 if (Percent>100)
2714 Percent=100;
2717 ViewerSearchMsg(strMsgStr,Percent);
2720 /* $ 01.08.2000 KM
2721 Убран кусок текста после приведения поисковой строки
2722 и Buf к единому регистру, если поиск не регистрозависимый
2723 или не ищется Hex-строка и в связи с этим переработан код поиска
2725 int MaxSize=ReadSize-SearchWChars+1;
2726 int Increment=ReverseSearch ? -1:+1;
2728 for (int I=ReverseSearch ? MaxSize-1:0; I<MaxSize && I>=0; I+=Increment)
2730 /* $ 01.08.2000 KM
2731 Обработка поиска "Whole words"
2733 bool locResultLeft = false;
2734 bool locResultRight = false;
2736 if (WholeWords)
2738 if (I)
2740 if (IsSpace(Buf[I-1]) || IsEol(Buf[I-1]) ||
2741 (wcschr(Opt.strWordDiv,Buf[I-1])))
2742 locResultLeft = true;
2744 else
2746 locResultLeft = true;
2749 if (ReadSize!=BufSize && I+SearchWChars>=ReadSize)
2750 locResultRight = true;
2751 else if (I+SearchWChars<ReadSize &&
2752 (IsSpace(Buf[I+SearchWChars]) || IsEol(Buf[I+SearchWChars]) ||
2753 (wcschr(Opt.strWordDiv,Buf[I+SearchWChars]))))
2754 locResultRight = true;
2756 else
2758 locResultLeft = true;
2759 locResultRight = true;
2762 if (locResultLeft && locResultRight)
2764 if (!Case && !SearchHex) {
2765 Match = CheckBufMatchesCaseInsensitive(SearchWChars, &Buf[I], strSearchStr.CPtr(), strSearchStrLowerCase.CPtr());
2766 } else {
2767 Match = CheckBufMatchesCaseSensitive(SearchWChars, &Buf[I], strSearchStr.CPtr());
2769 if (Match)
2771 MatchPos = CurPos + CalcCodeUnitsDistance(VM.CodePage, Buf, Buf + I);
2772 break;
2777 if (ReverseSearch)
2779 if (CurPos <= 0)
2780 break;
2782 vseek(CurPos, SEEK_SET);
2784 else
2786 if (vtell() >= FileSize)
2787 break;
2789 vseek(-(SearchCodeUnits + !!WholeWords), SEEK_CUR);
2795 if (Match)
2797 /* $ 24.01.2003 KM
2798 ! По окончании поиска отступим от верха экрана на
2799 треть отображаемой высоты.
2801 SelectText(MatchPos, SearchCodeUnits, ReverseSearch ? 0x2 : 0);
2803 // Покажем найденное на расстоянии трети экрана от верха.
2804 int FromTop=(ScrY-(Opt.ViOpt.ShowKeyBar?2:1))/4;
2806 if (FromTop<0 || FromTop>ScrY)
2807 FromTop=0;
2809 for (int i=0; i<FromTop; i++)
2810 Up();
2812 AdjustSelPosition = TRUE;
2813 Show();
2814 AdjustSelPosition = FALSE;
2816 else
2818 //Show();
2819 /* $ 27.01.2003 VVM
2820 + После окончания поиска спросим о переходе поиска в начало/конец */
2821 if (SearchFlags.Check(SEARCH_MODE2))
2822 Message(MSG_WARNING,1,Msg::ViewSearchTitle,
2823 (SearchHex?Msg::ViewSearchCannotFindHex:Msg::ViewSearchCannotFind),strMsgStr,Msg::Ok);
2824 else
2826 if (!Message(MSG_WARNING,2,Msg::ViewSearchTitle,
2827 (SearchHex?Msg::ViewSearchCannotFindHex:Msg::ViewSearchCannotFind),strMsgStr,
2828 (ReverseSearch?Msg::ViewSearchFromEnd:Msg::ViewSearchFromBegin),
2829 Msg::HYes,Msg::HNo))
2830 Search(2,0);
2836 /*void Viewer::ConvertToHex(char *SearchStr,int &SearchLength)
2838 char OutStr[512],*SrcPtr;
2839 int OutPos=0,N=0;
2840 SrcPtr=SearchStr;
2841 while (*SrcPtr)
2843 while (IsSpaceA(*SrcPtr))
2844 SrcPtr++;
2845 if (SrcPtr[0])
2846 if (!SrcPtr[1] || IsSpaceA(SrcPtr[1]))
2848 N=HexToNum(SrcPtr[0]);
2849 SrcPtr++;
2851 else
2853 N=16*HexToNum(SrcPtr[0])+HexToNum(SrcPtr[1]);
2854 SrcPtr+=2;
2856 if (N>=0)
2857 OutStr[OutPos++]=N;
2858 else
2859 break;
2861 memcpy(SearchStr,OutStr,OutPos);
2862 SearchLength=OutPos;
2866 int Viewer::HexToNum(int Hex)
2868 Hex=toupper(Hex);
2870 if (Hex>='0' && Hex<='9')
2871 return(Hex-'0');
2873 if (Hex>='A' && Hex<='F')
2874 return(Hex-'A'+10);
2876 return(-1000);
2880 int Viewer::GetWrapMode()
2882 return(VM.Wrap);
2886 void Viewer::SetWrapMode(int Wrap)
2888 Viewer::VM.Wrap=Wrap;
2891 void Viewer::EnableHideCursor(int HideCursor)
2893 Viewer::HideCursor=HideCursor;
2897 int Viewer::GetWrapType()
2899 return(VM.WordWrap);
2903 void Viewer::SetWrapType(int TypeWrap)
2905 Viewer::VM.WordWrap=TypeWrap;
2909 void Viewer::GetFileName(FARString &strName)
2911 strName = strFullFileName;
2914 void Viewer::SetProcessed(bool Processed)
2916 VM.Processed = Processed;
2917 ChangeViewKeyBar();
2918 Show();
2921 void Viewer::ShowConsoleTitle()
2923 FARString strTitle;
2924 strTitle.Format(Msg::InViewer, PointToName(strFileName));
2925 ConsoleTitle::SetFarTitle(strTitle);
2928 void Viewer::SetTitle(const wchar_t *Title)
2930 if (!Title)
2931 strTitle.Clear();
2932 else
2933 strTitle = Title;
2936 void Viewer::SetFilePos(int64_t Pos)
2938 FilePos=Pos;
2941 void Viewer::SetPluginData(const wchar_t *PluginData)
2943 Viewer::strPluginData = NullToEmpty(PluginData);
2947 void Viewer::SetNamesList(NamesList *List)
2949 if (List)
2950 List->MoveData(ViewNamesList);
2953 int Viewer::vread(wchar_t *Buf,int Count, bool Raw)
2955 if (VM.CodePage == CP_WIDE_LE || VM.CodePage == CP_WIDE_BE)
2957 DWORD ReadSize = ViewFile.Read((char *)Buf, Count * sizeof(wchar_t));
2958 DWORD ResultedCount = ReadSize / sizeof(wchar_t);
2959 if ((ReadSize % sizeof(wchar_t)) != 0 && (int)ResultedCount < Count) {
2960 memset(((char *)Buf) + ReadSize, 0, sizeof(wchar_t) - (ReadSize % sizeof(wchar_t)));
2961 ++ResultedCount;
2964 if (VM.CodePage == CP_WIDE_BE && !Raw) {
2965 WideReverse((wchar_t *)Buf, ResultedCount);
2968 return ResultedCount;
2970 else if (VM.CodePage == CP_UTF8 && !Raw)
2972 INT64 Ptr;
2973 ViewFile.GetPointer(Ptr);
2974 int ResultedCount = 0;
2975 for (DWORD WantViewSize = Count; ResultedCount < Count;) {
2976 DWORD ViewSize = WantViewSize;
2977 char *SrcView = (char *)ViewFile.ViewBytesAt(Ptr, ViewSize);
2978 if (!ViewSize) {
2979 break;
2982 size_t SourcedCount = ViewSize;
2983 size_t SinkedCount = (Count - ResultedCount);
2985 const auto cr = MB2Wide_Unescaped(SrcView, SourcedCount, &Buf[ResultedCount], SinkedCount, false);
2987 Ptr+= SourcedCount;
2988 ResultedCount+= (int)SinkedCount;
2990 if ( (cr & CONV_NEED_MORE_SRC) != 0 && SourcedCount == 0) {
2991 if (ViewSize < WantViewSize) {
2992 if (ResultedCount >= Count) {
2993 break;
2995 Buf[ResultedCount] = WCHAR_REPLACEMENT;
2996 ++ResultedCount;
2997 ++Ptr;
2999 } else {
3000 WantViewSize+= (4 + WantViewSize / 4);
3003 } else if ((cr & CONV_NEED_MORE_DST) != 0 || (cr & CONV_NEED_MORE_SRC) == 0) {
3004 break;
3008 ViewFile.SetPointer(Ptr);
3009 return ResultedCount;
3011 else
3013 INT64 Ptr;
3014 ViewFile.GetPointer(Ptr);
3016 DWORD ReadSize = Count;
3017 if (VM.CodePage == CP_UTF16LE || VM.CodePage == CP_UTF16BE ) {
3018 ReadSize*= 2;
3021 LPBYTE View = ViewFile.ViewBytesAt(Ptr, ReadSize);
3023 if (Count == 1 && ReadSize == 2 && !Raw && (VM.CodePage==CP_UTF16LE || VM.CodePage==CP_UTF16BE ))
3025 //Если UTF16 то простой ли это символ или нет?
3026 if (*(uint16_t *)View >= 0xd800 && *(uint16_t *)View <= 0xdfff) {
3027 ReadSize+= 2;
3028 View = ViewFile.ViewBytesAt(Ptr, ReadSize);
3032 ViewFile.SetPointer(Ptr + ReadSize);
3034 if (!View || !ReadSize)
3035 return 0;
3037 if (Raw)
3039 if (VM.CodePage == CP_UTF16LE || VM.CodePage == CP_UTF16BE) {
3040 ReadSize/= 2;
3041 for (DWORD i = 0; i < ReadSize; ++i)
3043 Buf[i] = (unsigned char)View[i * 2 + 1];
3044 Buf[i]<<= 8;
3045 Buf[i]|= (unsigned char)View[i * 2];
3047 } else {
3048 for (DWORD i = 0; i < ReadSize; i++)
3050 Buf[i] = (wchar_t)(unsigned char)View[i];
3054 else
3056 ReadSize = WINPORT(MultiByteToWideChar)(VM.CodePage, 0, (const char *)View, ReadSize, Buf, Count);
3059 return ReadSize;
3064 void Viewer::vseek(int64_t Offset,int Whence)
3066 switch (VM.CodePage)
3068 case CP_UTF32BE: case CP_UTF32LE: Offset*= 4; break;
3069 case CP_UTF16BE: case CP_UTF16LE: Offset*= 2; break;
3071 ViewFile.SetPointer(Offset, Whence);
3075 int64_t Viewer::vtell()
3077 INT64 Ptr=0;
3078 ViewFile.GetPointer(Ptr);
3079 switch (VM.CodePage)
3081 case CP_UTF32BE: case CP_UTF32LE: Ptr=(Ptr+(Ptr&3))/4; break;
3082 case CP_UTF16BE: case CP_UTF16LE: Ptr=(Ptr+(Ptr&1))/2; break;
3084 return Ptr;
3088 bool Viewer::vgetc(WCHAR& C)
3090 bool Result=false;
3092 if (vread(&C,1))
3094 Result=true;
3096 return Result;
3100 #define RB_PRC 3
3101 #define RB_HEX 4
3102 #define RB_DEC 5
3104 void Viewer::GoTo(int ShowDlg,int64_t Offset, DWORD Flags)
3106 int64_t Relative=0;
3107 const wchar_t *LineHistoryName=L"ViewerOffset";
3108 DialogDataEx GoToDlgData[]=
3110 {DI_DOUBLEBOX,3,1,31,7,{0},0,Msg::ViewerGoTo},
3111 {DI_EDIT,5,2,29,2,{(DWORD_PTR)LineHistoryName},DIF_FOCUS|DIF_DEFAULT|DIF_HISTORY|DIF_USELASTHISTORY,L""},
3112 {DI_TEXT,3,3,0,3,{0},DIF_SEPARATOR,L""},
3113 {DI_RADIOBUTTON,5,4,0,4,{0},DIF_GROUP,Msg::GoToPercent},
3114 {DI_RADIOBUTTON,5,5,0,5,{0},0,Msg::GoToHex},
3115 {DI_RADIOBUTTON,5,6,0,6,{0},0,Msg::GoToDecimal}
3117 MakeDialogItemsEx(GoToDlgData,GoToDlg);
3118 static int PrevMode=0;
3119 GoToDlg[3].Selected=GoToDlg[4].Selected=GoToDlg[5].Selected=0;
3121 if (VM.Hex)
3122 PrevMode=1;
3124 GoToDlg[PrevMode+3].Selected=TRUE;
3126 if (ShowDlg)
3128 Dialog Dlg(GoToDlg,ARRAYSIZE(GoToDlg));
3129 Dlg.SetHelp(L"ViewerGotoPos");
3130 Dlg.SetPosition(-1,-1,35,9);
3131 Dlg.Process();
3133 if (Dlg.GetExitCode()<=0)
3134 return;
3136 if (GoToDlg[1].strData.At(0)==L'+' || GoToDlg[1].strData.At(0)==L'-') // юзер хочет относительности
3138 if (GoToDlg[1].strData.At(0)==L'+')
3139 Relative=1;
3140 else
3141 Relative=-1;
3143 GoToDlg[1].strData.LShift(1);
3146 if (GoToDlg[1].strData.Contains(L'%')) // он хочет процентов
3148 GoToDlg[RB_HEX].Selected=GoToDlg[RB_DEC].Selected=0;
3149 GoToDlg[RB_PRC].Selected=1;
3151 else if (!StrCmpNI(GoToDlg[1].strData,L"0x",2)
3152 || GoToDlg[1].strData.At(0)==L'$'
3153 || GoToDlg[1].strData.Contains(L'h')
3154 || GoToDlg[1].strData.Contains(L'H')) // он умный - hex код ввел!
3156 GoToDlg[RB_PRC].Selected=GoToDlg[RB_DEC].Selected=0;
3157 GoToDlg[RB_HEX].Selected=1;
3159 if (!StrCmpNI(GoToDlg[1].strData,L"0x",2))
3160 GoToDlg[1].strData.LShift(2);
3161 else if (GoToDlg[1].strData.At(0)==L'$')
3162 GoToDlg[1].strData.LShift(1);
3164 //Relative=0; // при hex значении никаких относительных значений?
3167 if (GoToDlg[RB_PRC].Selected)
3169 //int cPercent=ToPercent64(FilePos,FileSize);
3170 PrevMode=0;
3171 int Percent=_wtoi(GoToDlg[1].strData);
3173 //if ( Relative && (cPercent+Percent*Relative<0) || (cPercent+Percent*Relative>100)) // за пределы - низя
3174 // return;
3175 if (Percent>100)
3176 return;
3178 //if ( Percent<0 )
3179 // Percent=0;
3180 Offset=FileSize/100*Percent;
3182 switch (VM.CodePage) {
3183 case CP_UTF32LE: case CP_UTF32BE: Offset*= 4; break;
3184 case CP_UTF16LE: case CP_UTF16BE: Offset*= 2; break;
3187 while (ToPercent64(Offset,FileSize)<Percent)
3188 Offset++;
3191 if (GoToDlg[RB_HEX].Selected)
3193 PrevMode=1;
3194 swscanf(GoToDlg[1].strData,L"%llx",&Offset);
3197 if (GoToDlg[RB_DEC].Selected)
3199 PrevMode=2;
3200 swscanf(GoToDlg[1].strData,L"%lld",&Offset);
3202 }// ShowDlg
3203 else
3205 Relative=(Flags&VSP_RELATIVE)*(Offset<0?-1:1);
3207 if (Flags&VSP_PERCENT)
3209 int64_t Percent=Offset;
3211 if (Percent>100)
3212 return;
3214 //if ( Percent<0 )
3215 // Percent=0;
3216 Offset=FileSize/100*Percent;
3218 switch (VM.CodePage) {
3219 case CP_UTF32LE: case CP_UTF32BE: Offset*= 4; break;
3220 case CP_UTF16LE: case CP_UTF16BE: Offset*= 2; break;
3223 while (ToPercent64(Offset,FileSize)<Percent)
3224 Offset++;
3228 if (Relative)
3230 if (Relative==-1 && Offset>FilePos) // меньше нуля, if (FilePos<0) не пройдет - FilePos у нас uint32_t
3231 FilePos=0;
3232 else switch (VM.CodePage) {
3233 case CP_UTF32LE: case CP_UTF32BE:
3234 FilePos = FilePos+Offset*Relative / 4;
3235 break;
3237 case CP_UTF16LE: case CP_UTF16BE:
3238 FilePos = FilePos+Offset*Relative / 2;
3239 break;
3241 default:
3242 FilePos = FilePos+Offset*Relative;
3247 else switch (VM.CodePage) {
3248 case CP_UTF32LE: case CP_UTF32BE:
3249 FilePos = Offset / 4;
3250 break;
3252 case CP_UTF16LE: case CP_UTF16BE:
3253 FilePos = Offset / 2;
3254 break;
3256 default:
3257 FilePos = Offset;
3260 if (FilePos>FileSize || FilePos<0) // и куда его несет?
3261 FilePos=FileSize; // там все равно ничего нету
3263 // коррекция
3264 AdjustFilePos();
3266 // LastSelPos=FilePos;
3267 if (!(Flags&VSP_NOREDRAW))
3268 Show();
3271 void Viewer::AdjustFilePos()
3273 if (!VM.Hex)
3275 wchar_t Buf[4096];
3276 int64_t StartLinePos=-1,GotoLinePos=FilePos-(int64_t)sizeof(Buf)/sizeof(wchar_t);
3278 if (GotoLinePos<0)
3279 GotoLinePos=0;
3281 vseek(GotoLinePos,SEEK_SET);
3282 int ReadSize=(int)Min((int64_t)ARRAYSIZE(Buf),(int64_t)(FilePos-GotoLinePos));
3283 ReadSize=vread(Buf,ReadSize);
3286 for (int I=ReadSize-1; I>=0; I--)
3287 if (Buf[I]==(wchar_t)CRSym)
3289 StartLinePos=GotoLinePos+I;
3290 break;
3293 vseek(FilePos+1,SEEK_SET);
3295 if (VM.Hex) {
3296 size_t len = 8;
3297 switch (VM.CodePage) {
3298 case CP_UTF32LE: case CP_UTF32BE:
3299 len*= 4;
3300 break;
3302 case CP_UTF16LE: case CP_UTF16BE:
3303 len*= 2;
3304 break;
3307 FilePos&= ~(len - 1);
3309 else
3311 if (FilePos!=StartLinePos)
3312 Up();
3317 void Viewer::SetFileSize()
3319 if (!ViewFile.Opened())
3320 return;
3322 UINT64 uFileSize=0; // BUGBUG, sign
3323 ViewFile.GetSize(uFileSize);
3324 FileSize=uFileSize;
3326 /* $ 20.02.2003 IS
3327 Везде сравниваем FilePos с FileSize, FilePos для юникодных файлов
3328 уменьшается в два раза, поэтому FileSize тоже надо уменьшать
3330 switch (VM.CodePage) {
3331 case CP_UTF32LE: case CP_UTF32BE:
3332 FileSize=(FileSize+(FileSize&3)) / 4;
3333 break;
3335 case CP_UTF16LE: case CP_UTF16BE:
3336 FileSize=(FileSize+(FileSize&1)) / 2;
3337 break;
3342 void Viewer::GetSelectedParam(int64_t &Pos, int64_t &Length, DWORD &Flags)
3344 Pos=SelectPos;
3345 Length=SelectSize;
3346 Flags=SelectFlags;
3349 /* $ 19.01.2001 SVS
3350 Выделение - в качестве самостоятельной функции.
3351 Flags=0x01 - показывать (делать Show())
3352 0x02 - "обратный поиск" ?
3354 void Viewer::SelectText(const int64_t &MatchPos,const int64_t &SearchLength, const DWORD Flags)
3356 if (!ViewFile.Opened())
3357 return;
3359 wchar_t Buf[MAX_VIEWLINE];
3360 int64_t StartLinePos=-1,SearchLinePos=MatchPos-sizeof(Buf)/sizeof(wchar_t);
3362 if (SearchLinePos<0)
3363 SearchLinePos=0;
3365 vseek(SearchLinePos,SEEK_SET);
3366 int ReadSize=(int)Min((int64_t)ARRAYSIZE(Buf),(int64_t)(MatchPos-SearchLinePos));
3367 ReadSize=vread(Buf,ReadSize);
3369 for (int I=ReadSize-1; I>=0; I--)
3370 if (Buf[I]==(wchar_t)CRSym)
3372 StartLinePos=SearchLinePos+I;
3373 break;
3376 //MessageBeep(0);
3377 vseek(MatchPos+1,SEEK_SET);
3378 SelectPos=FilePos=MatchPos;
3379 SelectSize=SearchLength;
3380 SelectFlags=Flags;
3382 // LastSelPos=SelectPos+((Flags&0x2) ? -1:1);
3383 LastSelPos = SelectPos + SearchLength * ((Flags & 0x2) ? -1 : 1);
3384 if (VM.Hex)
3386 size_t len = 8;
3387 switch (VM.CodePage) {
3388 case CP_UTF32LE: case CP_UTF32BE:
3389 len*= 4;
3390 break;
3392 case CP_UTF16LE: case CP_UTF16BE:
3393 len*= 2;
3394 break;
3396 FilePos&= ~(len - 1);
3398 else
3400 if (SelectPos!=StartLinePos)
3402 Up();
3403 Show(); //update OutStr
3406 int64_t Length=SelectPos-StartLinePos-1;
3408 if (VM.Wrap)
3409 Length%=Width+1; //??
3411 if (Length<=Width)
3412 LeftPos=0;
3414 if (Length-LeftPos>Width || Length<LeftPos)
3416 LeftPos=Length;
3418 if (LeftPos>(MAX_VIEWLINE-1) || LeftPos<0)
3419 LeftPos=0;
3420 else if (LeftPos>10)
3421 LeftPos-=10;
3425 if (Flags&1)
3427 AdjustSelPosition = TRUE;
3428 Show();
3429 AdjustSelPosition = FALSE;
3433 int Viewer::ViewerControl(int Command,void *Param)
3435 switch (Command)
3437 case VCTL_GETINFO:
3439 if (Param)
3441 ViewerInfo *Info=(ViewerInfo *)Param;
3442 memset(&Info->ViewerID,0,Info->StructSize-sizeof(Info->StructSize));
3443 Info->ViewerID=ViewerID;
3444 Info->FileName=strFullFileName;
3445 Info->WindowSizeX=ObjWidth;
3446 Info->WindowSizeY=Y2-Y1+1;
3447 Info->FilePos=FilePos;
3448 Info->FileSize=FileSize;
3449 Info->CurMode=VM;
3450 Info->Options=0;
3452 if (Opt.ViOpt.SavePos) Info->Options|=VOPT_SAVEFILEPOSITION;
3454 if (ViOpt.AutoDetectCodePage) Info->Options|=VOPT_AUTODETECTCODEPAGE;
3456 Info->TabSize=ViOpt.TabSize;
3457 Info->LeftPos=LeftPos;
3458 return TRUE;
3461 break;
3464 Param = ViewerSetPosition
3465 сюда же будет записано новое смещение
3466 В основном совпадает с переданным
3468 case VCTL_SETPOSITION:
3470 if (Param)
3472 ViewerSetPosition *vsp=(ViewerSetPosition*)Param;
3473 bool isReShow=vsp->StartPos != FilePos;
3475 if ((LeftPos=vsp->LeftPos) < 0)
3476 LeftPos=0;
3478 /* $ 20.01.2003 IS
3479 Если кодировка - юникод, то оперируем числами, уменьшенными в
3480 2 раза. Поэтому увеличим StartPos в 2 раза, т.к. функция
3481 GoTo принимает смещения в _байтах_.
3484 int64_t NewPos = vsp->StartPos;
3486 switch (VM.CodePage) {
3487 case CP_UTF32LE: case CP_UTF32BE:
3488 NewPos*= 4;
3489 break;
3491 case CP_UTF16LE: case CP_UTF16BE:
3492 NewPos*= 2;
3493 break;
3496 GoTo(FALSE, NewPos, vsp->Flags);
3498 if (isReShow && !(vsp->Flags&VSP_NOREDRAW))
3499 ScrBuf.Flush();
3501 if (!(vsp->Flags&VSP_NORETNEWPOS))
3503 vsp->StartPos=FilePos;
3504 vsp->LeftPos=LeftPos;
3507 return TRUE;
3510 break;
3512 // Param=ViewerSelect
3513 case VCTL_SELECT:
3515 if (Param)
3517 ViewerSelect *vs=(ViewerSelect *)Param;
3518 int64_t SPos=vs->BlockStartPos;
3519 int SSize=vs->BlockLen;
3521 if (SPos < FileSize)
3523 if (SPos+SSize > FileSize)
3525 SSize=(int)(FileSize-SPos);
3528 SelectText(SPos,SSize,0x1);
3529 ScrBuf.Flush();
3530 return TRUE;
3533 else
3535 SelectSize = 0;
3536 Show();
3539 break;
3541 /* Функция установки Keybar Labels
3542 Param = nullptr - восстановить, пред. значение
3543 Param = -1 - обновить полосу (перерисовать)
3544 Param = KeyBarTitles
3546 case VCTL_SETKEYBAR:
3548 KeyBarTitles *Kbt=(KeyBarTitles*)Param;
3550 if (!Kbt)
3551 { // восстановить пред значение!
3552 if (HostFileViewer)
3553 HostFileViewer->InitKeyBar();
3555 else
3557 if ((LONG_PTR)Param != (LONG_PTR)-1) // не только перерисовать?
3559 for (int i=0; i < 12; i++)
3561 if (Kbt->Titles[i])
3562 ViewKeyBar->Change(KBL_MAIN,Kbt->Titles[i],i);
3564 if (Kbt->CtrlTitles[i])
3565 ViewKeyBar->Change(KBL_CTRL,Kbt->CtrlTitles[i],i);
3567 if (Kbt->AltTitles[i])
3568 ViewKeyBar->Change(KBL_ALT,Kbt->AltTitles[i],i);
3570 if (Kbt->ShiftTitles[i])
3571 ViewKeyBar->Change(KBL_SHIFT,Kbt->ShiftTitles[i],i);
3573 if (Kbt->CtrlShiftTitles[i])
3574 ViewKeyBar->Change(KBL_CTRLSHIFT,Kbt->CtrlShiftTitles[i],i);
3576 if (Kbt->AltShiftTitles[i])
3577 ViewKeyBar->Change(KBL_ALTSHIFT,Kbt->AltShiftTitles[i],i);
3579 if (Kbt->CtrlAltTitles[i])
3580 ViewKeyBar->Change(KBL_CTRLALT,Kbt->CtrlAltTitles[i],i);
3584 ViewKeyBar->Refresh(true);
3585 ScrBuf.Flush(); //?????
3588 return TRUE;
3590 // Param=0
3591 case VCTL_REDRAW:
3593 ChangeViewKeyBar();
3594 Show();
3595 ScrBuf.Flush();
3596 return TRUE;
3598 // Param=0
3599 case VCTL_QUIT:
3601 /* $ 28.12.2002 IS
3602 Разрешаем выполнение VCTL_QUIT только для вьюера, который
3603 не является панелью информации и быстрого просмотра (т.е.
3604 фактически панелей на экране не видно)
3606 if (!FrameManager->IsPanelsActive())
3608 /* $ 29.09.2002 IS
3609 без этого не закрывался вьюер, а просили именно это
3611 FrameManager->DeleteFrame(HostFileViewer);
3613 if (HostFileViewer)
3614 HostFileViewer->SetExitCode(0);
3616 return TRUE;
3619 /* Функция установки режимов
3620 Param = ViewerSetMode
3622 case VCTL_SETMODE:
3624 ViewerSetMode *vsmode=(ViewerSetMode *)Param;
3626 if (vsmode)
3628 bool isRedraw=vsmode->Flags&VSMFL_REDRAW?true:false;
3630 switch (vsmode->Type)
3632 case VSMT_HEX:
3633 ProcessHexMode(vsmode->Param.iParam,isRedraw);
3634 return TRUE;
3635 case VSMT_WRAP:
3636 ProcessWrapMode(vsmode->Param.iParam,isRedraw);
3637 return TRUE;
3638 case VSMT_WORDWRAP:
3639 ProcessTypeWrapMode(vsmode->Param.iParam,isRedraw);
3640 return TRUE;
3644 return FALSE;
3648 return FALSE;
3651 int Viewer::ProcessHexMode(int newMode, bool isRedraw)
3653 // BUGBUG
3654 // До тех пор, пока не будет реализован адекватный hex-просмотр в UTF8 - будем смотреть в OEM.
3655 // Ибо сейчас это не просмотр, а генератор однотипных унылых багрепортов.
3656 if (VM.CodePage==CP_UTF8 && newMode)
3658 VM.CodePage=WINPORT(GetACP)();
3661 int oldHex=VM.Hex;
3662 VM.Hex=newMode&1;
3664 if (isRedraw)
3666 ChangeViewKeyBar();
3667 Show();
3670 // LastSelPos=FilePos;
3671 return oldHex;
3674 int Viewer::ProcessWrapMode(int newMode, bool isRedraw)
3676 int oldWrap=VM.Wrap;
3677 VM.Wrap=newMode&1;
3679 if (VM.Wrap)
3680 LeftPos = 0;
3682 if (!VM.Wrap && LastPage)
3683 Up();
3685 if (isRedraw)
3687 ChangeViewKeyBar();
3688 Show();
3691 Opt.ViOpt.ViewerIsWrap=VM.Wrap;
3692 // LastSelPos=FilePos;
3693 return oldWrap;
3696 int Viewer::ProcessTypeWrapMode(int newMode, bool isRedraw)
3698 int oldTypeWrap=VM.WordWrap;
3699 VM.WordWrap=newMode&1;
3701 if (!VM.Wrap)
3703 VM.Wrap=!VM.Wrap;
3704 LeftPos = 0;
3707 if (isRedraw)
3709 ChangeViewKeyBar();
3710 Show();
3713 Opt.ViOpt.ViewerWrap=VM.WordWrap;
3714 //LastSelPos=FilePos;
3715 return oldTypeWrap;