//----------------------------------------------------------------------------
//  ObjectWindow - OWL NExt
//  Copyright 1999. Yura Bidus. All Rights reserved.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the
// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.
//
//  OVERVIEW
//  ~~~~~~~~
//  Syntax coloring Hex editor.
//----------------------------------------------------------------------------
 
#include <coolprj/pch.h>
#pragma hdrstop
 
#include <owl/file.h>
#include <owl/filename.h>
#include <owl/configfl.h>
#include <owl/scroller.h>
#include <owl/uimetric.h>
 
#include <sstream>
#include <algorithm>
 
using namespace owl;
using namespace std;
 
#include <coolprj/hexedit.h>
 
//#define USE_AUTOORG
 
#define HEX_TIMER_DEBOUNCE      1002
#ifdef WIN32
const int MSB=0x80000000;
#else
const int MSB=0x8000;
#endif
 
static const _TCHAR*  configSection     = _T("HexSyntaxFormat");
static const _TCHAR*  configBaseFont    = _T("BaseFont");
static const _TCHAR*  configFormatIdx   = _T("FormatIdx%d");
static const _TCHAR*  configOWLNExt     = _T("OWL NExt");
 
_TCHAR hextable[16] = {_T ('0'), _T ('1'), _T ('2'), _T ('3'), _T ('4'), _T ('5'), _T ('6'), _T ('7'),
                     _T ('8'), _T ('9'), _T ('A'), _T ('B'), _T ('C'), _T ('D'), _T ('E'), _T ('F')};
#define TOHEX(a, b) {*b++ = hextable[a >> 4];*b++ = hextable[a&0x0f];}
 
static const char*  streamSelectionId = "HEX000";
static const char*  lineSelectionId   = "HEX001";
 
 
void __Trace(LPCTSTR lpszFormat, ...);
//------------------------------------------------------------------------------
inline void __NoTrace(LPCTSTR /*lpszFormat*/, ...){}
//------------------------------------------------------------------------------
 
#if 0
#define _TRACE __Trace
#else
#define _TRACE __NoTrace
#endif
 
THexBuffer::THexBuffer()
:
  SyntaxArray(0)
  ,Flags(bfCreateBackup)
{
  memset(&BaseFont, 0, sizeof(BaseFont));
  _tcscpy(BaseFont.lfFaceName, _T("Courier New"));
  BaseFont.lfHeight         = -13;
  BaseFont.lfWeight         = FW_NORMAL;
  BaseFont.lfItalic         = FALSE;
  BaseFont.lfCharSet        = DEFAULT_CHARSET;
  BaseFont.lfOutPrecision   = OUT_DEFAULT_PRECIS;
  BaseFont.lfClipPrecision  = CLIP_DEFAULT_PRECIS;
  BaseFont.lfQuality        = DEFAULT_QUALITY;
  BaseFont.lfPitchAndFamily = FIXED_PITCH;
}
//-----------------------------------------------------------------------------
THexBuffer::~THexBuffer()
{
  delete SyntaxArray;
}
//-----------------------------------------------------------------------------
static void
SetSyntaxNode(TCoolTextBuffer::TSyntaxDescr& node, TCoolTextBuffer::TSyntaxDescr* descr)
{
  // white Space
  if(descr){
    node.TxColor    = descr->TxColor;
    node.BkColor    = descr->BkColor;
    node.FontIndex  = descr->FontIndex;
  }
}
//-----------------------------------------------------------------------------
void THexBuffer::SetSyntaxDescr(int index, const TSyntaxDescr& node)
{
  if(!SyntaxArray)
    SyntaxArray = new TSyntaxDescr[COLORINDEX_LAST];
  SyntaxArray[index] = node;
}
//-----------------------------------------------------------------------------
THexBuffer::TSyntaxDescr* THexBuffer::GetDefSyntaxDescr(int index)
{
  static TSyntaxDescr array[] = {
    TSyntaxDescr(TColor::SysWindowText,       TColor::SysWindow,         0), //HEXINDEX_WHITESPACE
    // Address
    TSyntaxDescr(TColor::SysWindowText,       TColor::SysWindow,         0), //HEXINDEX_ADDRESS
    // Digits part
    TSyntaxDescr(TColor::SysWindowText,       TColor::SysWindow,         0), //HEXINDEX_DIGITS
    // ASCII part
    TSyntaxDescr(TColor::SysWindowText,       TColor::SysWindow,         0), //HEXINDEX_ASCII
    // Selected Text
    TSyntaxDescr(TColor::White,               TColor::Black,             0), //HEXINDEX_SELECTION
  };
  return index < static_cast<int>(COUNTOF(array)) ? &array[index] : 0;
}
//-----------------------------------------------------------------------------
void THexBuffer::BuildDefSyntaxDescr()
{
  if(!SyntaxArray)
    SyntaxArray = new TSyntaxDescr[HEXINDEX_LAST];
 
  for(int i = 0; i < HEXINDEX_LAST; i++)
    SetSyntaxNode(SyntaxArray[i], GetDefSyntaxDescr(i));
}
//-----------------------------------------------------------------------------
void THexBuffer::SetSyntaxArray(TSyntaxDescr* array)
{
  PRECONDITION(array);
  delete SyntaxArray;
  SyntaxArray = array;
}
//-----------------------------------------------------------------------------
void THexBuffer::SaveSyntaxDescr(TConfigFile& file)
{
  PRECONDITION(SyntaxDescrExist());
 
  TConfigFileSection section(file, configSection);
  // 1. Save Font Info
  section.WriteFont(configBaseFont, BaseFont);
 
  // 2. Write color data + special flag, so WriteColor not applicable
  // maybe better WriteInteger???
  _TCHAR buffer[40];
  for(int i = 0; i < HEXINDEX_LAST; i++){
    wsprintf(buffer, configFormatIdx, i);
    section.WriteData(buffer, (void*)&(SyntaxArray[i]), sizeof(SyntaxArray[i]));
  }
}
//-----------------------------------------------------------------------------
void THexBuffer::RestoreSyntaxDescr(TConfigFile& file)
{
  if(!SyntaxArray)
    SyntaxArray = new TSyntaxDescr[HEXINDEX_LAST];
  if(!file.SectionExists(configSection))
    BuildDefSyntaxDescr();
  else{
 
    TConfigFileSection section(file, configSection);
    // 1. Restore Font Info
    LOGFONT lf;
    if(section.ReadFont(configBaseFont, lf))
      SetFont(lf);
 
    // 2. Read all Syntax format items
    _TCHAR buffer[40];
    TSyntaxDescr synVal;
 
    for(int i = 0; i < COLORINDEX_LAST; i++){
      wsprintf(buffer, configFormatIdx, i);
      if(section.ReadData(buffer, (void*)&synVal, sizeof(synVal)))
        SyntaxArray[i] = synVal;
    }
  }
}
//-----------------------------------------------------------------------------
void THexBuffer::GetFont(LOGFONT& lf) const
{
  lf = BaseFont;
}
//-----------------------------------------------------------------------------
void THexBuffer::SetFont(const LOGFONT& lf)
{
  BaseFont = lf;
}
//-----------------------------------------------------------------------------
uint32 THexBuffer::Search(const TEditPos& /*searchRange_*/, uint8* /*text*/,
                          uint /*textLen*/, bool /*up*/)
{
/*
  if (!text || !text[0] || !textLen)
    return uint32(-1);
 
  TEditPos searchRange(searchRange_);
  if(!searchRange.Valid()){
    // if not valid -> all file
    searchRange.col = 0;
    searchRange.row = GetDataSize();
  }
 
  // Lock the text buffer to get the pointer, and search thru it for the text
  //
  uint8* pos;
  uint8* buffer = up ? GetData() + searchRange.row-1 : GetData()+searchRange.col;
  uint bufLen    = searchRange.row-searchRange.col;
 
  if(up)
    pos = binrstrcd(buffer, bufLen, text, textLen);
  else
    pos = binstrcd(buffer, bufLen, text, textLen);
 
  // If we've got a match, select that text, cleanup & return.
  //
  if(pos)
    return pos - GetData();
*/
  return uint32(-1);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
THexFileBuffer::THexFileBuffer()
:
  THexBuffer()
  ,BinaryData(0)
  ,DataSize(0)
{
}
//-----------------------------------------------------------------------------
THexFileBuffer::~THexFileBuffer()
{
  delete BinaryData;
}
//-----------------------------------------------------------------------------
bool THexFileBuffer::Load(LPCTSTR filename)
{
  TFile file(filename);
  if(file.IsOpen())
    return Load(file);
  return false;
}
//-----------------------------------------------------------------------------
bool THexFileBuffer::Save(LPCTSTR filename, bool clearDirty)
{
  // save to tmp file and create backup
  TFileName savename(filename);
  TFileName tmpname(TFileName::TempFile);
 
  TFile file;
  if(file.Open(tmpname.Canonical().c_str(), TFile::WriteOnly|
               TFile::PermExclusive|TFile::CreateAlways)){
    if(Save(file, clearDirty)){
      if((Flags&bfCreateBackup) && savename.Exists()){
        TFileName bakname(savename);
        owl::tstring backupExt = bakname.GetParts(TFileName::Ext);
        backupExt[1] = _T('~');
        bakname.SetPart(TFileName::Ext, backupExt);
        savename.Move(bakname, TFileName::ReplaceExisting|
                      TFileName::CopyAllowed);
      }
      tmpname.Move(savename, TFileName::ReplaceExisting|TFileName::CopyAllowed);
      file.Close();
      return true;
    }
    file.Close();
  }
  return false;
}
//-----------------------------------------------------------------------------
bool THexFileBuffer::Load(TFile& file)
{
  TFileStatus status;
  if(!file.GetStatus(status))
    return false;
  if(status.size==0)
    return true;
  TAPointer<uint8> buffer(new uint8[status.size]);
  uint32 size = file.Read((uint8*) buffer, status.size);
  if(size == TFILE_ERROR)
    return false;
 
  delete BinaryData;
  BinaryData = buffer.Relinquish();
  DataSize = size;
  EnableReadOnly((status.attribute&TFile::RdOnly) !=0);
  SetDirty(false);
 
  return true;
}
//-----------------------------------------------------------------------------
bool THexFileBuffer::Save(TFile& file, bool clearDirty)
{
  if(!file.Write(BinaryData, DataSize))
    return false;
  if(clearDirty)
    SetDirty(false);
  return true;
}
//-----------------------------------------------------------------------------
void THexFileBuffer::Clear()
{
  delete BinaryData;
  BinaryData = 0;
  DataSize   = 0;
  SetDirty(false);
}
//-----------------------------------------------------------------------------
uint THexFileBuffer::GetBuffer(uint index, uint8* buffer, uint size)
{
  if(index >= DataSize)
    return 0;
  uint count = std::min(size, DataSize-index);
  memcpy(buffer,BinaryData+index,count);
  return count;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//{{THexViewWnd Implementation}}
 
UINT THexViewWnd::SelClipFormat = 0;
 
//
// Build a response table for all messages/commands handled
// by the application.
//
DEFINE_RESPONSE_TABLE1(THexViewWnd, TControl)
  EV_WM_SIZE,
  EV_WM_SETCURSOR,
  EV_COMMAND(CM_EDITCOPY, CmEditCopy),
  EV_COMMAND_ENABLE(CM_EDITCOPY, CeSelectEnable),
  EV_WM_KILLFOCUS,
  EV_WM_SYSCOLORCHANGE,
  EV_WM_SETFOCUS,
  EV_WM_HSCROLL,
  EV_WM_VSCROLL,
  EV_WM_MOUSEWHEEL,
  EV_WM_LBUTTONDOWN,
  EV_WM_MOUSEMOVE,
  EV_WM_LBUTTONUP,
#if !defined(COOL_NODRAGDROP)
  EV_WM_TIMER,
#endif
  EV_WM_KEYDOWN,
END_RESPONSE_TABLE;
 
//-----------------------------------------------------------------------------
bool
THexViewWnd::TTextSelection::TextToClipboard(TClipboard& cb, uint8* text, int len)
{
  // Allocate a global memory object for the text.
  HGLOBAL hHandle = ::GlobalAlloc(GMEM_DDESHARE, len);
  if (hHandle){
    // Lock the handle and copy the text to the buffer.
    void* pBlock = ::GlobalLock(hHandle);
    memcpy(pBlock, text, len);
    ::GlobalUnlock(hHandle);
    // Place the handle on the clipboard, as internal format.
    cb.SetClipboardData(GetFormat(), hHandle);
    return true;
  }
  return false;
}
//-----------------------------------------------------------------------------
bool
THexViewWnd::TTextSelection::Copy()
{
  bool retval = false;
  TClipboard clipboard(*GetParent());
  if (clipboard.EmptyClipboard()){
    auto ostrm = ostringstream{};
    if(Copy(ostrm)){
      const auto s = ostrm.str();
      retval = TextToClipboard(clipboard, (uint8*)s.c_str(), static_cast<int>(s.size()));
    }
  }
  return retval;
}
//-----------------------------------------------------------------------------
THexViewWnd::TLineSelection::TLineSelection(THexViewWnd* parent,
                                            int start, int end)
:
  TTextSelection(parent), Start(start), End(end)
{
}
//-----------------------------------------------------------------------------
bool
THexViewWnd::TLineSelection::GetSelection(int line, int& startChar,
                                          int& endChar)
{
  if(HasSelection(line)){
    startChar = 0;
    endChar   = GetParent()->GetLineLength(line)+1;
  }
  return true;
}
//-----------------------------------------------------------------------------
bool
THexViewWnd::TLineSelection::Copy(ostream& os)
{
  int start = Start, end   = End;
  if(start > end)
    std::swap(start,end);
 
  os << lineSelectionId;
 
 
  uint charInLine = GetParent()->GetCharsInLine();
  TTmpBuffer<uint8> buff(charInLine);
 
  for(int i = start; i <= end; i++){
    uint length = GetParent()->GetLineData(i, buff, charInLine);
    os.write((const char*)(uint8*)buff,length);
  }
  os << ends;
  return true;
}
//-----------------------------------------------------------------------------
bool
THexViewWnd::TStreamSelection::GetSelection(int line, int& startChar,
                                            int& endChar)
{
  if(HasSelection(line)){
    TEditRange range(Range);
    range.Normalize();
    startChar = line > range.srow ? 0 : range.scol;
    endChar   = line < range.erow ? GetParent()->GetLineLength(line)+1 : range.ecol;
  }
  return true;
}
//-----------------------------------------------------------------------------
bool THexViewWnd::TStreamSelection::Copy(ostream& os)
{
  TEditRange range(Range);
  range.Normalize();
 
  os << streamSelectionId;
 
  uint charInLine = GetParent()->GetCharsInLine();
  TTmpBuffer<uint8> buff(charInLine);
 
  for(int i = range.srow; i <= range.erow; i++){
    int length = GetParent()->GetLineData(i, buff, charInLine);
    uint8* text = buff;
    if(i == range.srow){
      if(range.srow == range.erow && range.ecol < length)
        length = range.ecol;
      text += range.scol;
    }
    else if(i == range.erow && range.ecol < length)
      length = range.ecol;
    length -= range.scol;
    os.write((const char*)text,length);
  }
  os << ends;
  return true;
}
//-----------------------------------------------------------------------------
// class THexViewWnd
// ~~~~~ ~~~~~~~~~~~~
//
THexViewWnd::THexViewWnd(TWindow* parent,int id,LPCTSTR title,int x, int y,
                         int w, int h, TModule* module)
:
  Inherited(parent,id,title,x,y,w,h,module)
  ,MemoryBitmap(0)
  ,CursorPos(0,0)
  ,Flags(cfAddress|cfDigit|cfAscii/*|cfNoUseCaret*/)
  ,LineHeight(-1)
  ,CharWidth(-1)
  ,ScreenLines(-1)
  ,ScreenChars(-1)
  ,SelType(stStreamSelection)
  ,Selection(0)
  ,PosType(psDigit)
#if !defined(COOL_NODRAGDROP)
  ,DragDropProxy(new THexDragDropProxy)
  ,DragDropSupport(DragDropProxy)
  ,DragSelTimer(0)
#endif
{
  ModifyStyle(0, WS_VSCROLL|WS_HSCROLL);
  SetBkgndColor(TColor::Transparent);
  memset(TextFonts,0,sizeof(TextFonts));
  if(!SelClipFormat)
    SelClipFormat = ::RegisterClipboardFormat(_T("HexEdit Binary Data"));
}
//-----------------------------------------------------------------------------
THexViewWnd::~THexViewWnd()
{
#if !defined(COOL_NODRAGDROP)
  delete DragDropProxy;
#endif
  delete MemoryBitmap;
  for(int i = 0; i < 8; i++)
    delete TextFonts[i];
  delete Selection;
}
//-----------------------------------------------------------------------------
void THexViewWnd::SetupWindow()
{
  if(!Scroller)
    Scroller = new TScroller(this, 7, 16, 80, 60);
#if !defined(USE_AUTOORG)
  Scroller->AutoOrg = false;
#endif
 
  // Call base class function.
  Inherited::SetupWindow();
 
#if !defined(COOL_NODRAGDROP)
  DragDropSupport.Register(*this);
#endif
 
  SetCursor(0, IDC_IBEAM);
 
  THexBuffer* buffer = GetBuffer();
  if(!buffer->SyntaxDescrExist())
    buffer->BuildDefSyntaxDescr();
 
}
//-----------------------------------------------------------------------------
void THexViewWnd::CleanupWindow()
{
#if !defined(COOL_NODRAGDROP)
  DragDropSupport.UnRegister(*this);
 
  if(DragSelTimer)
    KillTimer(DragSelTimer);
  DragSelTimer = 0;
#endif
 
  // Call base class function.
  Inherited::CleanupWindow();
}
//
TConfigFile*
THexViewWnd::CreateConfigFile()
{
  return new TRegConfigFile(configOWLNExt);
}
//-----------------------------------------------------------------------------
void
THexViewWnd::GetWindowClass(WNDCLASS& wndClass)
{
  // Call base class function.
  Inherited::GetWindowClass(wndClass);
  wndClass.style |= CS_DBLCLKS;
}
//-----------------------------------------------------------------------------
auto THexViewWnd::GetWindowClassName() -> TWindowClassName
{
  return TWindowClassName{_T("HEXEDIT")};
}
//
void
THexViewWnd::FileNew()
{
  PRECONDITION(GetBuffer());
  GetBuffer()->Clear();
  ResetView();
  AdjustScroller();
  Invalidate();
}
//
bool
THexViewWnd::FileOpen(LPCTSTR filename)
{
  PRECONDITION(GetBuffer());
  if(!GetBuffer()->Load(filename))
    return false;
  ResetView();
  if(GetHandle()){
    AdjustScroller();
    Invalidate();
  }
  return true;
}
//-----------------------------------------------------------------------------
void THexViewWnd::ResetView()
{
  //ClearFlag(cfBookmarkExist);
  SelType           = stStreamSelection;
  delete Selection;
  Selection         = 0;
  CursorPos.row      = 0;
  CursorPos.col      = 0;
  //LastHit.x          = -1;
  //LastHit.y          = -1;
  if(Scroller){
    Scroller->XPos = 0;
    Scroller->YPos = 0;
  }
 
  if(GetHandle())
    UpdateCaret();
}
//-----------------------------------------------------------------------------
void THexViewWnd::AdjustScroller()
{
  static bool in_work = false;
  if(!Scroller || in_work)
    return;
 
  in_work = true;
  TRect clientRect;
  GetClientRect(clientRect);
 
  // Only show scrollbars when image is larger than
  // the client area and we are not stretching to fit.
  //
  int charsInLine = 0;
  if(IsShowAddress())
    charsInLine += IsShowWideAddress() ? 10 : 6;
  if(IsShowDigit())
    charsInLine += GetCharsInLine()*3+1;
  if(IsShowAscii())
    charsInLine += GetCharsInLine();
 
  TPoint range(charsInLine, GetNumLines()-1);
  Scroller->SetUnits(GetCharWidth(), GetLineHeight());
 
  Scroller->SetRange(range.x, range.y);
 
  //Scroller->ScrollTo(0, 0);
  if (!GetUpdateRect(clientRect, false))
    InvalidateLines(0, -1, false);
  in_work = false;
}
//-----------------------------------------------------------------------------
void THexViewWnd::EvSize(uint sizeType, const TSize& size)
{
  Inherited::EvSize(sizeType, size);
  if (sizeType != SIZEICONIC) {
    delete MemoryBitmap;
    MemoryBitmap = 0;
    ScreenLines = -1;
    ScreenChars = -1;
    AdjustScroller();
    //Invalidate(false);
  }
}
//-----------------------------------------------------------------------------
TFont*
THexViewWnd::CreateFont(int index)
{
  if(!TextFonts[index]){
    PRECONDITION(GetBuffer());
    LOGFONT lf;
    GetBuffer()->GetFont(lf);
    lf.lfWeight   = (index & 0x01) ? FW_BOLD : FW_NORMAL;
    lf.lfItalic   = (uint8)((index & 0x02)!=0);
    lf.lfUnderline= (uint8)((index & 0x04)!=0);
    TextFonts[index] = new TFont(lf);
  }
  return TextFonts[index];
}
//-----------------------------------------------------------------------------
TFont*
THexViewWnd::GetFont(bool italic, bool bold, bool underline)
{
  int index = 0;
  if(bold)
    index |= 0x0001;
  if(italic)
    index |= 0x0002;
  if(underline)
    index |= 0x0004;
  return CreateFont(index);
}
//
void
THexViewWnd::SetFont(const LOGFONT &lf)
{
  PRECONDITION(GetBuffer());
  GetBuffer()->SetFont(lf);
 
  LineHeight = -1;
  CharWidth = -1;
  ScreenLines = -1;
  ScreenChars = -1;
  delete MemoryBitmap;
  MemoryBitmap = 0;
  for (int i = 0; i < 8; i ++){
    if (TextFonts[i]){
      delete TextFonts[i];
      TextFonts[i] = 0;
    }
  }
  if(IsWindow()){
    AdjustScroller();
    UpdateCaret();
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::Paint(TDC& dc, bool /*erase*/, TRect&)
{
  TRect clientRect;
  GetClientRect(clientRect);
 
  THexBuffer* buffer = GetBuffer();
  if(!buffer){
    TColor oldClr = dc.SetBkColor(GetBkColor(HEXINDEX_WHITESPACE));
    dc.TextRect(clientRect);
    dc.SetBkColor(oldClr);
    return;
  }
  int lineCount  = GetNumLines();
  int lineHeight = GetLineHeight();
 
  // must be member variables
  TMemoryDC  memDC(dc);
  if(!MemoryBitmap)
    MemoryBitmap = new TBitmap(dc, clientRect.Width(), lineHeight);
  memDC.SelectObject(*MemoryBitmap);
 
  TRect lineRect(clientRect.left, clientRect.top,
                 clientRect.right, clientRect.top + lineHeight);
  TRect cacheMargin(0, 0, GetMarginWidth(), lineHeight);
  TRect cacheLine(0, 0, lineRect.Width(), lineHeight);
 
  int currentLine = GetTopLine();
  while (lineRect.Top() < clientRect.Bottom()){
    if(currentLine < lineCount){
      DrawMargin(memDC, cacheMargin, currentLine);
      DrawLine(memDC, cacheLine, currentLine);
    }
    else{
      DrawMargin(memDC, cacheMargin, -1);
      DrawLine(memDC, cacheLine, -1);
    }
 
    // Now transfer what was drawn on the (invisible) memory DC onto the
    // visible dc This one BitBlt transfer is much faster than the many
    // individual operations that were performed above.
    //
    VERIFY(dc.BitBlt(lineRect, memDC, TPoint(0,0)));
    currentLine++;
    lineRect.Offset(0, lineHeight);
  }
 
 
  // Restore original bitmap before leaving
  //
  memDC.RestoreBitmap();
}
//-----------------------------------------------------------------------------
void
THexViewWnd::CalcLineCharDim()
{
  TClientDC  clientDC(*this);
  TSize extent = GetFont()->GetTextExtent(clientDC, _T("X"));
  LineHeight = extent.cy;
  if (LineHeight < 1)
    LineHeight = 1;
  CharWidth = extent.cx;
}
//-----------------------------------------------------------------------------
int THexViewWnd::GetNumLines() const
{
  THexBuffer* buffer = ((THexViewWnd*)this)->GetBuffer();
  if(!buffer)
    return 0;
  uint num = buffer->GetDataSize()/GetCharsInLine();
  if(buffer->GetDataSize()%GetCharsInLine())
    num++;
  return num;
}
//-----------------------------------------------------------------------------
int
THexViewWnd::GetCharsInLine() const
{
  return 16;//?????????????????????????????????????????????????????????????????
}
//-----------------------------------------------------------------------------
int
THexViewWnd::GetCharWidth() const
{
  if (CharWidth == -1)
    CONST_CAST(THexViewWnd*,this)->CalcLineCharDim();
  return CharWidth;
}
//-----------------------------------------------------------------------------
int
THexViewWnd::GetLineHeight() const
{
  if (LineHeight == -1)
    CONST_CAST(THexViewWnd*,this)->CalcLineCharDim();
  return LineHeight;
}
//-----------------------------------------------------------------------------
int
THexViewWnd::GetTopLine()
{
  return Scroller->YPos;
}
//-----------------------------------------------------------------------------
int
THexViewWnd::GetOffsetChar()
{
  return Scroller->XPos;
}
//-----------------------------------------------------------------------------
void
THexViewWnd::SetSyntaxFormat(TDC& dc, int index)
{
  THexBuffer::TSyntaxDescr& node = GetSyntaxDescr(index);
  dc.SetTextColor(node.TxColor);
  dc.SetBkColor(node.BkColor);
  dc.SelectObject(*GetFont(node.FontIndex));
}
//-----------------------------------------------------------------------------
void
THexViewWnd::InvalidateLines(int index1, int index2, bool invalidateMargin)
{
  invalidateMargin = true;
  if(index2 == -1){
    TRect rect;
    GetClientRect(rect);
    if (!invalidateMargin)
      rect.left += GetMarginWidth();
    rect.top = (index1 - GetTopLine()) * GetLineHeight();
    InvalidateRect(rect, false);
  }
  else{
    if (index2 < index1)
      std::swap(index1, index2);
    TRect rect;
    GetClientRect(rect);
    if (!invalidateMargin)
      rect.left += GetMarginWidth();
    rect.top = (index1 - GetTopLine()) * GetLineHeight();
    rect.bottom = (index2 - GetTopLine() + 1) * GetLineHeight();
    InvalidateRect(rect, false);
  }
}
//-----------------------------------------------------------------------------
int
THexViewWnd::GetScreenLines()
{
  if (ScreenLines == -1){
    TRect rect;
    GetClientRect(rect);
    ScreenLines = rect.Height() / GetLineHeight();
  }
  return ScreenLines;
}
//-----------------------------------------------------------------------------
bool
THexViewWnd::IsInSelection(const TEditPos& textPos) const
{
  if(Selection && Selection->HasSelection(textPos.row)){
    int startSel, endSel;
    if(Selection->GetSelection(textPos.row, startSel, endSel))
      return textPos.col >= startSel && textPos.col < endSel;
  }
  return false;
}
//-----------------------------------------------------------------------------
void
THexViewWnd::EnableShowAddress(bool enable)
{
  if(IsShowAddress() != enable){
    enable ? SetFlag(cfAddress) : ClearFlag(cfAddress);
    if (IsWindow())
      InvalidateLines(0, -1);
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::EnableShowDigit(bool enable)
{
  if(IsShowDigit() != enable){
    enable ? SetFlag(cfDigit) : ClearFlag(cfDigit);
    if (IsWindow())
      InvalidateLines(0, -1);
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::EnableShowAscii(bool enable)
{
  if(IsShowAscii() != enable){
    enable ? SetFlag(cfAscii) : ClearFlag(cfAscii);
    if (IsWindow())
      InvalidateLines(0, -1);
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::EnableShowWideAddress(bool enable)
{
  if(IsShowWideAddress() != enable){
    enable ? SetFlag(cfShowWideAddr) : ClearFlag(cfShowWideAddr);
    if (IsWindow())
      InvalidateLines(0, -1);
  }
}
//-----------------------------------------------------------------------------
int
THexViewWnd::GetScreenChars()
{
  if (ScreenChars == -1){
    TRect rect;
    GetClientRect(rect);
    ScreenChars = (rect.Width() - GetMarginWidth()) / GetCharWidth();
  }
  return ScreenChars;
}
//-----------------------------------------------------------------------------
int
THexViewWnd::GetLineLength(int index)
{
  THexBuffer* buffer = ((THexViewWnd*)this)->GetBuffer();
  CHECK(buffer);
  index = index*GetCharsInLine();
  int retsize = buffer->GetDataSize() - index;
  return std::min(retsize, GetCharsInLine());
}
//-----------------------------------------------------------------------------
uint
THexViewWnd::GetLineData(int index, uint8* buff, uint size) const
{
  THexBuffer* buffer = ((THexViewWnd*)this)->GetBuffer();
  CHECK(buffer);
  index = index*GetCharsInLine();
  int retsize = buffer->GetDataSize() - index;
  retsize = std::min(std::min(retsize, GetCharsInLine()), static_cast<int>(size));
  return buffer->GetBuffer(index, buff, retsize);
}
//-----------------------------------------------------------------------------
void
THexViewWnd::DrawMargin(TDC&, const TRect&, int /*lineIndex*/)
{
  // nothing for now
}
//-----------------------------------------------------------------------------
void
THexViewWnd::DrawLine(TDC& dc, const TRect& rect, int lineIndex)
{
  PRECONDITION(lineIndex >= -1 && lineIndex < GetNumLines());
  PRECONDITION(GetBuffer());
 
  if(lineIndex == -1){
    //  Draw line beyond the text
    dc.SetBkColor(GetBkColor(HEXINDEX_WHITESPACE));
    dc.TextRect(rect);
    return;
  }
 
  //  Acquire the background color for the current line
  bool drawWhitespace = false;
  int colorIdx = HEXINDEX_ADDRESS;
 
 
  TTmpBuffer<uint8> buff(GetCharsInLine());
  int length = GetLineData(lineIndex, buff, GetCharsInLine());
  uint8* pszChars = buff;
 
  if (!length){
    //  Draw the empty line
    TRect r(rect);
    TEditPos editPos(0, lineIndex);
    if(IsFlagSet(cfFocused|cfShowInactiveSel) && IsInSelection(editPos)){
      dc.SetBkColor(GetBkColor(HEXINDEX_SELECTION));
      TRect rc(r.left, r.top, r.left + GetCharWidth(), r.bottom);
      dc.TextRect(rc);
      r.left += GetCharWidth();
    }
    dc.SetBkColor(GetBkColor(drawWhitespace ? colorIdx : HEXINDEX_WHITESPACE));
    dc.TextRect(r);
    return;
  }
 
  //  Draw the line text
#if !defined(USE_AUTOORG)
  TPoint origin(rect.left - GetOffsetChar() * GetCharWidth(), rect.top);
#else
  TPoint origin(rect.left, rect.top);
#endif
 
  TTmpBuffer<_TCHAR> buffer(256);
  uint charWidth = GetCharWidth();
 
  // paint address
  if(IsFlagSet(cfAddress)){
    _TCHAR fmt[8] = {_T ('%'), _T ('0'), _T ('4'), _T ('l'), _T ('X'), _T(' '),_T(' ')};
    int len  = IsShowWideAddress() ? 10 : 6;
    if(IsShowWideAddress())
      fmt[2] = _T ('8');
    wsprintf(buffer, fmt, lineIndex*GetCharsInLine());
    SetSyntaxFormat(dc, HEXINDEX_ADDRESS);
    VERIFY(dc.ExtTextOut(origin, ETO_CLIPPED, &rect, &buffer[0], len));
 
    // constant
    origin.x += charWidth * len;
  }
 
  int len = std::min(length, GetCharsInLine());
  if(IsFlagSet(cfDigit)){
    LPTSTR p = (_TCHAR*)buffer;
    int w = 0;
    int i = 0;
    for(; i < len; i++){
      TOHEX(pszChars[i], p);
      *p++ = _T (' ');
      w += 3;
    }
    *p = _T (' ');
    w++;
    SetSyntaxFormat(dc, HEXINDEX_DIGITS);
    VERIFY(dc.ExtTextOut(origin, ETO_CLIPPED, &rect, &buffer[0], w));
 
    // constant
    origin.x += charWidth * w;
    int width = 3*GetCharsInLine()+1;
    if(width > w){
      TRect frect(rect);
      frect.left = origin.x;
      dc.TextRect(frect);
      origin.x += charWidth * (width - w);
    }
  }
 
  if(IsFlagSet(cfAscii)){
    LPTSTR p = (_TCHAR*)buffer;
    int w = 0;
    for(int i = 0; i < len; i++){
      *p++ = _istprint(pszChars[i]) ? pszChars[i] : _T('.');
      w++;
    }
 
    SetSyntaxFormat(dc, HEXINDEX_ASCII);
    VERIFY(dc.ExtTextOut(origin, ETO_CLIPPED, &rect, &buffer[0], w));
 
    origin.x += charWidth * w;
 
    int width = GetCharsInLine();
    if(width > w){
      TRect frect(rect);
      frect.left = origin.x;
      dc.TextRect(frect);
      origin.x += charWidth * (width - w);
    }
  }
 
  //  Draw whitespaces to the left of the text
  TRect frect(rect);
  if (origin.x > frect.left)
    frect.left = origin.x;
  if (frect.right > frect.left){
    if((IsFlagSet(cfFocused|cfShowInactiveSel)) && IsInSelection(TEditPos(length, lineIndex))){
      dc.SetBkColor(GetBkColor(HEXINDEX_SELECTION));
      TRect rc(frect.left, frect.top, frect.left+GetCharWidth(), frect.bottom);
      dc.TextRect(rc);
      frect.left += GetCharWidth();
    }
    if(frect.right > frect.left){
      dc.SetBkColor(GetBkColor(drawWhitespace ? colorIdx : HEXINDEX_WHITESPACE));
      dc.TextRect(frect);
    }
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::EnableCaret(bool enable)
{
  if(enable != IsCaretEnable()){
    enable ? ClearFlag(cfNoUseCaret) : SetFlag(cfNoUseCaret);
    if (IsWindow() && IsFlagSet(cfFocused)){
      if(IsFlagSet(cfNoUseCaret)){
        HideCaret();
        DestroyCaret();
      }
      else {
        ClearFlag(cfCursorHidden);
        ClearFlag(cfHasCaret);
        UpdateCaret();
        ShowCaret();
      }
    }
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::EnableShowInactiveSel(bool enable)
{
  if(enable != IsShowInactiveSel()){
    enable ? SetFlag(cfShowInactiveSel) : ClearFlag(cfShowInactiveSel);
    if (IsWindow())
      InvalidateLines(0, -1);
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::EnableDragDrop(bool enable)
{
  if(IsFlagSet(cfNoDragDrop) != enable){
    enable ? SetFlag(cfNoDragDrop) : ClearFlag(cfNoDragDrop);
  }
}
//-----------------------------------------------------------------------------
bool
THexViewWnd::EvSetCursor(HWND hWndCursor, uint hitTest, uint mouseMsg)
{
  if (hitTest == HTCLIENT){
    TPoint point;
    Inherited::GetCursorPos(point);
    ScreenToClient(point);
    TPosType pos = Client2PosType(point);
    if(pos==psDigit || pos==psAscii){
      if(Selection && IsInSelection(Client2Text(point))){
#if !defined(COOL_NODRAGDROP)
        if(!IsFlagSet(cfNoDragDrop))
#endif
          ::SetCursor(::LoadCursor(0, TResId(IDC_ARROW))); // Set To Arrow Cursor
        return true;
      }
      else
        return Inherited::EvSetCursor(hWndCursor, hitTest, mouseMsg);
    }
    else{
      ::SetCursor(::LoadCursor(0, TResId(IDC_ARROW))); // Set To Arrow Cursor
      return true;
    }
  }
  return Inherited::EvSetCursor(hWndCursor, hitTest, mouseMsg);
}
//-----------------------------------------------------------------------------
THexViewWnd::TPosType
THexViewWnd::Client2PosType(const TPoint& point)
{
  int margin = GetMarginWidth();
  int leftMargin = GetCharWidth()*GetOffsetChar();
  if(IsShowAddress()){
    int addWidth = GetCharWidth()*(IsShowWideAddress() ? 8 : 4)+margin;
    if((point.x > margin-leftMargin) && (point.x < addWidth-leftMargin))
      return ptAddress;
    margin += addWidth + GetCharWidth()*2-margin;
  }
  if(IsShowDigit()){
    int digWidth = GetCharWidth()*(GetCharsInLine()*3-1)+margin;
    if((point.x > margin-leftMargin) && (point.x < digWidth-leftMargin))
      return psDigit;
    margin += digWidth + GetCharWidth()*2-margin;
  }
  if(IsShowAscii()){
    int ascWidth = GetCharWidth()*GetCharsInLine()+margin;
    if((point.x > margin-leftMargin) && (point.x < ascWidth-leftMargin))
      return psAscii;
  }
  return ptNone;
}
//-----------------------------------------------------------------------------
TEditPos
THexViewWnd::Client2Text(const TPoint& point, bool* sPart)
{
  int lineCount     = GetNumLines();
  TPosType posType = Client2PosType(point);
  if(posType==ptNone||posType==ptAddress)
    return TEditPos();
 
  TEditPos p(0, GetTopLine() + point.y / GetLineHeight());
  if(p.row >= lineCount)
    p.row = lineCount - 1;
  if(p.row < 0)
    p.row = 0;
 
  int  length = 0;
  if(p.row >= 0 && p.row < lineCount)
    length = GetLineLength(p.row);
 
  int index = -1;
  int charWidth = GetCharWidth();
  uint spos = GetOffsetChar()*charWidth;
  if(IsShowAddress())
    spos += charWidth*(IsShowWideAddress() ? 10 : 6);
 
  if(posType==psDigit){
    index = (point.x-spos)/(charWidth*3);
    if(index < GetCharsInLine()){
      p.col = index;
      if(sPart){
        *sPart = false;
        int rem = (point.x-spos)/charWidth%3;
        if(rem==1)
          *sPart = true;
        else if(rem==2)
          p.col++;
      }
      return p;
    }
  }
  else if(posType==psAscii){
    if(IsShowDigit())
      spos += charWidth*(GetCharsInLine()*3+1);
    index = (point.x-spos)/charWidth;
    if(index < GetCharsInLine()){
      p.col = index;
      return p;
    }
  }
 
  CHECK(index >= 0 && index <= length); InUse(length);
  p.col = index;
  return p;
}
//-----------------------------------------------------------------------------
TPoint
THexViewWnd::Text2Client(const TEditPos& pos)
{
  PRECONDITION(GetBuffer());
//  VERIFY_TEXTPOS(pos);
 
  TPoint p(0,(pos.row - GetTopLine())*GetLineHeight());
  if(PosType==ptAddress){
    p.x = 0;
  }
  else if(PosType==psDigit){
    if(IsShowAddress())
      p.x += IsShowWideAddress() ? 10 : 6;
    p.x += pos.col*3;
    if(IsFlagSet(cfSecondDigit))
      p.x += 1;
  }
  else if(PosType==psAscii){
    if(IsShowAddress())
      p.x += IsShowWideAddress() ? 10 : 6;
    if(IsShowDigit())
      p.x += 16*3+1;
    p.x += pos.col;
  }
  p.x = (p.x - GetOffsetChar()) * GetCharWidth() + GetMarginWidth();
  return p;
}
//-----------------------------------------------------------------------------
void
THexViewWnd::ShowCursor()
{
  if(!IsFlagSet(cfNoUseCaret)){
    ClearFlag(cfCursorHidden);
    UpdateCaret();
    ShowCaret();
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::HideCursor()
{
  if(!IsFlagSet(cfNoUseCaret)){
    SetFlag(cfCursorHidden);
    UpdateCaret();
    HideCaret();
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::SetCursorPos(const TEditPos& newPos)
{
  //VERIFY_TEXTPOS(newPos);
  CursorPos = newPos;
  UpdateCaret();
}
//-----------------------------------------------------------------------------
bool
THexViewWnd::CreateCaret()
{
  Inherited::CreateCaret(false, GetCharWidth(), GetLineHeight());
  return true;
}
//-----------------------------------------------------------------------------
void
THexViewWnd::UpdateCaret()
{
  if(IsFlagSet(cfNoUseCaret))
    return;
  if (IsFlagSet(cfFocused) && !IsFlagSet(cfCursorHidden) &&
      CalculateActualOffset(CursorPos.row, CursorPos.col) >= GetOffsetChar()){
    if(!IsFlagSet(cfHasCaret)){
      if(CreateCaret())
        SetFlag(cfHasCaret);
    }
    if(IsFlagSet(cfHasCaret)){
      SetCaretPos(Text2Client(CursorPos));
    }
  }
}
//-----------------------------------------------------------------------------
int
THexViewWnd::CalculateActualOffset(int lineIndex, int charIndex)
{
  int length = GetLineLength(lineIndex);
  return charIndex < length ? charIndex : length-1;
}
//-----------------------------------------------------------------------------
void THexViewWnd::EvKillFocus(HWND hWndGetFocus)
{
  ClearFlag(cfFocused);
  ClearFlag(cfHasCaret);
  if(!IsFlagSet(cfNoUseCaret)){
    HideCursor();
    DestroyCaret();
  }
 
  if (Selection)
    InvalidateLines(Selection->GetStart().row, Selection->GetEnd().row);
#if !defined(COOL_NODRAGDROP)
  if(IsFlagSet(cfDragPending)){
    ReleaseCapture();
    if(DragSelTimer)
      KillTimer(DragSelTimer);
    DragSelTimer = 0;
    ClearFlag(cfDragPending);
  }
#endif
 
  Inherited::EvKillFocus(hWndGetFocus);
}
//-----------------------------------------------------------------------------
void THexViewWnd::EvSysColorChange()
{
  Inherited::EvSysColorChange();
  InvalidateLines(0, -1);
}
//-----------------------------------------------------------------------------
#if !defined(COOL_NODRAGDROP)
void THexViewWnd::EvTimer(uint timerId)
{
  if(timerId == HEX_TIMER_DEBOUNCE){
    CHECK(DragSelTimer);
    if(IsFlagSet(cfDragPending)){
      //_TRACE("TCoolTextWnd::EvTimer()\n");
      ExecuteDragDrop();
    }
  }
}
#endif
//-----------------------------------------------------------------------------
void THexViewWnd::EvSetFocus(HWND hWndLostFocus)
{
  Inherited::EvSetFocus(hWndLostFocus);
 
  SetFlag(cfFocused);
  if (Selection)
    InvalidateLines(Selection->GetStart().row, Selection->GetEnd().row);
  ShowCursor();
}
//-----------------------------------------------------------------------------
void THexViewWnd::GoToAddress(int address, bool relative)
{
  THexBuffer* buffer = GetBuffer();
  CHECK(buffer);
 
  int ypos = address/GetCharsInLine();
  int xpos = address%GetCharsInLine();
  TEditPos cursorPos(CursorPos);
  if(relative){
    cursorPos.row += ypos;
    cursorPos.col += xpos;
    if(cursorPos.col > GetCharsInLine()){
      cursorPos.row++;
      cursorPos.col = cursorPos.col-GetCharsInLine();
    }
  }
  else{
    cursorPos.row = ypos;
    cursorPos.col = xpos;
  }
  int maxpos = buffer->GetDataSize();
  if((cursorPos.row*GetCharsInLine()+cursorPos.col) > maxpos){
    cursorPos.row = maxpos/GetCharsInLine();
    cursorPos.col = maxpos%GetCharsInLine();
  }
  //VERIFY_TEXTPOS(cursorPos);
  SetCursorPos(cursorPos);
  SetSelection(TEditRange(cursorPos, cursorPos));
  ScrollToCaret(cursorPos);
}
//-----------------------------------------------------------------------------
void
THexViewWnd::ClearSelection()
{
  if(Selection){
    InvalidateLines(Selection->GetStart().row, Selection->GetEnd().row);
    delete Selection;
    Selection = 0;
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::SetSelection(const TEditRange& range)
{
  //VERIFY_TEXTPOS(range.Start());
  //VERIFY_TEXTPOS(range.End());
  if(Selection && Selection->GetStart().row == range.srow){
    if(Selection->GetStart().row != range.erow)
      InvalidateLines(range.erow, Selection->GetEnd().row);
  }
  else{
    InvalidateLines(range.srow, range.erow);
    if(Selection)
      InvalidateLines(Selection->GetStart().row, Selection->GetEnd().row);
  }
  delete Selection;
  Selection = 0;
  if(range.Start() == range.End())
    return;
  Selection = CreateSelection(range);
}
//-----------------------------------------------------------------------------
THexViewWnd::TTextSelection*
THexViewWnd::CreateSelection(const TEditRange& range)
{
  switch(SelType){
    case stStreamSelection:
      return new TStreamSelection(this, range);
    case stLineSelection:
      return new TLineSelection(this, range.srow, range.srow);
  }
  return 0;
}
//-----------------------------------------------------------------------------
void THexViewWnd::EvHScroll(uint scrollCode, uint thumbPos, HWND hWndCtl)
{
  Inherited::EvHScroll(scrollCode,thumbPos,hWndCtl);
  if(IsFlagSet(cfHasCaret))
    SetCaretPos(Text2Client(CursorPos));
}
//-----------------------------------------------------------------------------
void THexViewWnd::EvVScroll(uint scrollCode, uint thumbPos, HWND hWndCtl)
{
  Inherited::EvVScroll(scrollCode,thumbPos,hWndCtl);
  if(IsFlagSet(cfHasCaret))
    SetCaretPos(Text2Client(CursorPos));
}
//-----------------------------------------------------------------------------
void
THexViewWnd::EvMouseWheel(uint modKeys, int zDelta, const TPoint& point)
{
  Inherited::EvMouseWheel(modKeys, zDelta, point);
  if(IsFlagSet(cfHasCaret))
    SetCaretPos(Text2Client(CursorPos));
}
//-----------------------------------------------------------------------------
void
THexViewWnd::SelectAll()
{
  int lineCount = GetNumLines();
  CursorPos.col = GetLineLength(lineCount - 1);
  CursorPos.row = lineCount - 1;
  SetSelection(TEditRange(0, 0, CursorPos.col, CursorPos.row));
  UpdateCaret();
}
//
#if !defined(COOL_NODRAGDROP)
void
THexViewWnd::ExecuteDragDrop()
{
  //_TRACE("TCoolTextWnd::ExecuteDragDrop() START\n");
  if(DragSelTimer)
    KillTimer(DragSelTimer);
  DragSelTimer = 0;
  ClearFlag(cfMouseDown);
  ClearFlag(cfDragPending);
  ReleaseCapture();
  if (SetDragData()){
    SetFlag(cfDraggingText);
    Scroller->AutoMode = false; //workaround
    //_TRACE("TCoolTextWnd::ExecuteDragDrop() Scroller->AutoMode = false\n");
    DROPEFFECT de = DragDropSupport.DoDragDrop(GetDropEffect());
    if (de != DROPEFFECT_NONE)
      DropSource(de);
    ClearFlag(cfDraggingText);
    DragDropProxy->ResetDragData();
    //_TRACE("TCoolTextWnd::ExecuteDragDrop() ResetDragData\n");
  }
  //_TRACE("TCoolTextWnd::ExecuteDragDrop() END\n");
}
//
TDragDropProxy*
THexViewWnd::SetDragDropProxy(TDragDropProxy* proxy)
{
  TDragDropProxy* tmp = DragDropProxy;
  DragDropProxy = proxy;
  DragDropSupport.SetProxy(DragDropProxy);
  return tmp;
}
#endif
//-----------------------------------------------------------------------------
bool
THexViewWnd::Search(const TEditPos& /*startPos_*/, LPCTSTR text, uint /*len*/,
                     TFindFlags /*flags*/)
{
  if (!text || !text[0])  //???????????????????????????????????????????????????????
    return false;
/*
  TCoolTextBuffer* buffer = GetBuffer();
  TEditPos startPos(startPos_);
  if(!startPos.Valid())
    startPos = CursorPos;
  TEditPos endPos;
 
  if(flags&ffWholeDoc){
    startPos = TEditPos(-1,-1);
    endPos   = TEditPos(-1,-1);
  }
  else{
    if(flags&ffDirectionUp)
      endPos = TEditPos(0,0);
    else{
      int lastLine = buffer->GetLineCount()-1;
      TEditPos(buffer->GetLineLength(lastLine),lastLine);
    }
  }
 
  TEditRange searchRange(startPos,endPos);
  searchRange.Normalize();
 
  TEditPos resPos = buffer->Search(searchRange, text,
                                   ToBool(flags&ffMatchCase),
                                   ToBool(flags&ffWholeWord),
                                   ToBool(flags&ffDirectionUp));
 
  // If we've got a match, select that text, cleanup & return.
  //
  if(resPos.Valid()){
    TEditRange resRange(resPos.col,resPos.row,resPos.col+_tcslen(text), resPos.row);
    SetSelection(resRange);
    CursorPos = resRange.End();
    ScrollToCaret(CursorPos);
    return true;
  }
*/
  return false;
}
//
#if !defined(COOL_NODRAGDROP)
DROPEFFECT
THexViewWnd::GetDropEffect()
{
  //_TRACE("THexViewWnd::GetDropEffect\n");
  return DROPEFFECT_COPY;
}
//
void
THexViewWnd::DropSource(DROPEFFECT de)
{
  CHECK(de == DROPEFFECT_COPY); InUse(de);
  //_TRACE("THexViewWnd::DropSource\n");
}
//
void
THexViewWnd::DropText(IDataObject*, const TPoint&, DROPEFFECT)
{
  //_TRACE("THexViewWnd::DropText\n");
  // nothing
}
//
bool
THexViewWnd::SetDragData()
{
  if(Selection){
    auto ostrm = ostringstream{};
    Selection->Copy(ostrm);
    const auto s = ostrm.str();
    const char* text = s.c_str();
    std::streamsize len = s.size();
 
    // Allocate a global memory object for the text.
    HGLOBAL hHandle = ::GlobalAlloc(GMEM_DDESHARE, static_cast<SIZE_T>(len));
    if (hHandle){
      // Lock the handle and copy the text to the buffer.
      void* pBlock = ::GlobalLock(hHandle);
      memcpy(pBlock, text, static_cast<size_t>(len));
      ::GlobalUnlock(hHandle);
      // Place the handle on the clipboard, as internal format.
      DragDropProxy->SetDragData(SelClipFormat, hHandle);
    }
    else{
      return false;
    }
    return true;
  }
  return false;
}
#endif
//-----------------------------------------------------------------------------
void THexViewWnd::EvLButtonDown(uint modKeys, const TPoint& point)
{
  //_TRACE("TCoolTextWnd::EvLButtonDown()\n");
  bool needRedraw = false;
  bool bShift   = modKeys & MK_SHIFT;
  bool bControl = modKeys & MK_CONTROL;
 
  //We don't need to capture the mouse - TScroller does it.
  SetFlag(cfMouseDown);
  if(point.x < GetMarginWidth()){
    //AdjustTextPoint(point);
    if(bControl){
      SelectAll();
      ClearFlag(cfMouseDown);
      return;
    }
    else{
      CursorPos   = Client2Text(point);
      CursorPos.col = 0; // set new position on start of line
      TEditPos endPos(CursorPos);
      endPos.col  = GetLineLength(CursorPos.row); //  Force full line
      SetSelection(TEditRange(CursorPos, endPos));
      UpdateCaret();
      Inherited::EvLButtonDown(modKeys, point);
      return;
    }
  }
 
  PosType   = Client2PosType(point);
  bool selPos;
  CursorPos = Client2Text(point, &selPos);
  selPos ? SetFlag(cfSecondDigit) : ClearFlag(cfSecondDigit);
 
  if(Selection){
    if(bShift)
      Selection->Extend(CursorPos);
    else{
#if !defined(COOL_NODRAGDROP)
      TEditPos textPos = Client2Text(point);
      if(!IsFlagSet(cfNoDragDrop) && IsInSelection(textPos)){
        //_TRACE("TCoolTextWnd::EvLButtonDown() Start pending\n");
        SavedDragPos = point;
        SetFlag(cfDragPending);
        DragSelTimer = SetTimer(HEX_TIMER_DEBOUNCE, DragDropSupport.GetDelay());
      }
      else
#endif
      {
        delete Selection;
        Selection = 0;
      }
    }
    needRedraw = true;
  }
  // start selection
  if(!Selection){
    TSelType selType = SelType;
    SelType   = stStreamSelection;
    Selection = CreateSelection(TEditRange(CursorPos, CursorPos));
    SelType   = selType;
  }
  UpdateCaret();
  if(needRedraw){
    InvalidateLines(0, -1, false);
  }
  Inherited::EvLButtonDown(modKeys, point);
}
//-----------------------------------------------------------------------------
void THexViewWnd::EvMouseMove(uint modKeys, const TPoint& point)
{
  if(IsFlagSet(cfMouseDown)){
    TEditPos startPos = CursorPos;
    CursorPos = Client2Text(point);
    if(point.x < GetMarginWidth()){
      if(Selection){
        TEditRange range(Selection->GetStart(), CursorPos);
        range.ecol  = GetLineLength(CursorPos.row); //  Force full line
        CursorPos.col = 0;
        SetSelection(range);
      }
    }
    else{
#if !defined(COOL_NODRAGDROP)
      if(IsFlagSet(cfDragPending)){
        if(DragDropSupport.CanDragDrop(SavedDragPos, point))
          ExecuteDragDrop();
      }
      else
#endif
      if(Selection)
        Selection->Extend(CursorPos);
    }
    UpdateCaret();
    InvalidateLines(startPos.row, CursorPos.row);
    UpdateWindow();
  }
  Inherited::EvMouseMove(modKeys, point);
}
//-----------------------------------------------------------------------------
void THexViewWnd::EvLButtonUp(uint modKeys, const TPoint& point)
{
  //_TRACE("THexViewWnd::EvLButtonUp() START\n");
#if !defined(COOL_NODRAGDROP)
  // Workaround in conflict Scroller->AutoMode  and Drag-and-Drop
  if(!Scroller->AutoMode){
    Scroller->AutoMode = true;
    return;
  }
#endif
  if(IsFlagSet(cfMouseDown)){
    ClearFlag(cfMouseDown);
 
    InvalidateLines(0, -1, false);
    CursorPos = Client2Text(point);
    if(point.x < GetMarginWidth()){
      if(Selection){
        TEditRange range(Selection->GetStart(), CursorPos);
        range.ecol  = GetLineLength(CursorPos.row); //  Force full line
        CursorPos.col = 0;
        SetSelection(range);
      }
    }
    else{
#if !defined(COOL_NODRAGDROP)
      if(IsFlagSet(cfDragPending)){
        ClearFlag(cfDragPending);
        delete Selection;
        Selection = 0;
      }
      else
#endif
        if(Selection)
          Selection->Extend(CursorPos);
    }
#if !defined(COOL_NODRAGDROP)
    if(DragSelTimer)
      KillTimer(DragSelTimer);
    DragSelTimer = 0;
#endif
 
    PostCheckSelection();
    ReleaseCapture();
    UpdateCaret();
  }
  //_TRACE("THexViewWnd::EvLButtonUp() Scroller->AutoMode = true\n");
  Inherited::EvLButtonUp(modKeys, point);
}
//-----------------------------------------------------------------------------
void
THexViewWnd::PreCheckSelection()
{
  if(!Selection)
    Selection = CreateSelection(TEditRange(CursorPos, CursorPos));
}
//-----------------------------------------------------------------------------
void
THexViewWnd::PostCheckSelection()
{
  if(Selection && Selection->IsEmpty()){
    delete Selection;
    Selection = 0;
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::ScrollToCaret(const TEditPos& cursorPos)
{
  int dx=0,dy=0;
 
  //VERIFY_TEXTPOS(cursorPos);
  TPoint caretPos = Text2Client(cursorPos);
  TRect  clientRect;
  GetClientRect(clientRect);
 
  //
  if(Scroller->YRange){
    clientRect.bottom -= TUIMetric::CyHScroll;
    if(clientRect.bottom%GetLineHeight())
      clientRect.bottom -= clientRect.bottom%GetLineHeight();
  }
  if(Scroller->XRange){
    clientRect.right -= TUIMetric::CyVScroll;
    if(clientRect.right%GetCharWidth())
      clientRect.right -= clientRect.right%GetCharWidth();
  }
  clientRect.left += GetMarginWidth();
 
  if(!clientRect.Contains(caretPos)){
    if(caretPos.x > clientRect.right)
      dx = caretPos.x - clientRect.right;
    else if (caretPos.x < clientRect.left)
      dx = caretPos.x - clientRect.left;
    if (caretPos.y > clientRect.bottom)
      dy = caretPos.y - clientRect.bottom;
    else if (caretPos.y < clientRect.top)
      dy = caretPos.y - clientRect.top;
 
    Scroller->ScrollBy(dx/GetCharWidth(),dy/GetLineHeight());
 
    //VERIFY_TEXTPOS(cursorPos);
    caretPos = Text2Client(cursorPos);
 
  }
  SetCursorPos(Client2Text(caretPos));
}
//-----------------------------------------------------------------------------
void
THexViewWnd::EvKeyDown(uint key, uint repeatCount, uint /*flags*/)
{
#if 1
  bool is_shift = ::GetAsyncKeyState(VK_SHIFT) & MSB;
  bool is_ctrl  = ::GetAsyncKeyState(VK_CONTROL) & MSB;
#else
  bool is_shift = flags & MK_SHIFT;  //?????
  bool is_ctrl  = flags & MK_CONTROL;// ????
#endif
  switch (key){
    case VK_ESCAPE:
      KeyEscape(repeatCount);
      break;
    case VK_RIGHT:
      if(is_shift){
         ExtendRight(repeatCount);
      }
      else{
        ClearSelection();
        MoveRight(repeatCount);
      }
      break;
    case VK_LEFT:
      if(is_shift){
        ExtendLeft(repeatCount);
      }
      else{
        ClearSelection();
        MoveLeft(repeatCount);
      }
      break;
    case VK_DOWN:
      if(is_ctrl){
        if(!is_shift)
          ScrollDown(repeatCount);
      }
      else{
        if(is_shift)
          ExtendDown(repeatCount);
        else{
          ClearSelection();
          MoveDown(repeatCount);
        }
      }
      break;
 
    case VK_UP:
      if(is_ctrl){
        if(!is_shift)
          ScrollUp(repeatCount);
      }
      else{
        if(is_shift)
          ExtendUp(repeatCount);
        else{
          ClearSelection();
          MoveUp(repeatCount);
        }
      }
      break;
 
    case VK_NEXT: //Page Down
      if(!is_ctrl){
        if(is_shift)
          ExtendPgDown(repeatCount);
        else{
          ClearSelection();
          MovePgDown(repeatCount);
        }
      }
      break;
 
    case VK_PRIOR:  //Page up
      if(!is_ctrl){
        if(is_shift)
          ExtendPgUp(repeatCount);
        else{
          ClearSelection();
          MovePgUp(repeatCount);
        }
      }
      break;
 
    case VK_HOME:
      if(is_shift){
        if(is_ctrl)
          ExtendCtrlHome(repeatCount);
        else
          ExtendHome(repeatCount);
      }
      else{
        ClearSelection();
        if(is_ctrl)
          MoveCtrlHome(repeatCount);
        else
          MoveHome(repeatCount);
      }
      break;
 
    case VK_END:
      if(is_shift){
        if(is_ctrl)
          ExtendCtrlEnd(repeatCount);
        else
          ExtendEnd(repeatCount);
      }
      else{
        ClearSelection();
        if(is_ctrl)
          MoveCtrlEnd(repeatCount);
        else
          MoveEnd(repeatCount);
      }
      break;
      //Anything else is a character key - let windows have it
      //back and we will catch it in EvChar.
    default:
      DefaultProcessing();
      return; //NOTE - return not break!
  }
  //Inherited::EvKeyDown(key, repeatCount, flags); //????
}
//-----------------------------------------------------------------------------
void
THexViewWnd::KeyEscape(int /*repeatCount*/)
{
  if(!IsStreamSelMode())
    SelType = stStreamSelection;
  else if(Selection)
    ClearSelection();
  else
    DefaultProcessing();
}
//-----------------------------------------------------------------------------
void
THexViewWnd::ExtendRight(int repeatCount)
{
  int lastLine = GetNumLines()-1;
  if(CursorPos.row < lastLine ||
      (CursorPos.row == lastLine && CursorPos.col < GetLineLength(lastLine))){
    PreCheckSelection();
    int startPos = CursorPos.row;
    while(repeatCount--){
      if(PosType==psDigit){
        if(IsFlagSet(cfSecondDigit))
          CursorPos.col++,ClearFlag(cfSecondDigit);
        else
          SetFlag(cfSecondDigit);
      }
      else
        CursorPos.col++;
      if(CursorPos.col >= GetLineLength(CursorPos.row)){
        if(CursorPos.row < GetNumLines()-1){
          CursorPos.row++;
          CursorPos.col = 0;
        }
      }
    }
    Selection->Extend(CursorPos);
    PostCheckSelection();
    ScrollToCaret(CursorPos);
    InvalidateLines(startPos, CursorPos.row);
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::MoveRight(int repeatCount)
{
  int lastLine = GetNumLines()-1;
  if(CursorPos.row < lastLine ||
      (CursorPos.row == lastLine && CursorPos.col < GetLineLength(lastLine))){
    int startPos = CursorPos.row;
    while(repeatCount--){
      if(PosType==psDigit){
        if(IsFlagSet(cfSecondDigit))
          CursorPos.col++,ClearFlag(cfSecondDigit);
        else
          SetFlag(cfSecondDigit);
      }
      else
        CursorPos.col++;
      if(CursorPos.col >= GetLineLength(CursorPos.row)){
        if(CursorPos.row < GetNumLines()-1){
          CursorPos.row++;
          CursorPos.col = 0;
        }
      }
    }
    ScrollToCaret(CursorPos);
    InvalidateLines(startPos, CursorPos.row);
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::ExtendLeft(int repeatCount)
{
  if(CursorPos.row > 0 || (CursorPos.row == 0 && CursorPos.col > 0)){
    int startPos = CursorPos.row;
    PreCheckSelection();
    while(repeatCount--){
      if(PosType==psDigit){
        if(IsFlagSet(cfSecondDigit))
          ClearFlag(cfSecondDigit);
        else
          CursorPos.col--,SetFlag(cfSecondDigit);
      }
      else
        CursorPos.col--;
      if(CursorPos.col < 0){
        if(CursorPos.row > 0){
          CursorPos.row--;
          CursorPos.col = GetLineLength(CursorPos.row);
        }
        else
          CursorPos.col = 0;
      }
    }
    ScrollToCaret(CursorPos);
    InvalidateLines(startPos, CursorPos.row);
 
    Selection->Extend(CursorPos);
    PostCheckSelection();
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::MoveLeft(int repeatCount)
{
  if(CursorPos.row>0 || (CursorPos.row==0 && CursorPos.col>0)){
    int startPos = CursorPos.row;
    while(repeatCount--){
      if(PosType==psDigit){
        if(IsFlagSet(cfSecondDigit))
          ClearFlag(cfSecondDigit);
        else
          CursorPos.col--,SetFlag(cfSecondDigit);
      }
      else
        CursorPos.col--;
 
      if(CursorPos.col < 0){
        if(CursorPos.row > 0){
          CursorPos.row--;
          CursorPos.col = GetLineLength(CursorPos.row);
        }
        else
          CursorPos.col = 0;
      }
    }
    ScrollToCaret(CursorPos);
    InvalidateLines(startPos, CursorPos.row);
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::ExtendDown(int repeatCount)
{
  if(CursorPos.row < GetNumLines()-1){
    int startPos = CursorPos.row;
    PreCheckSelection();
    CursorPos.row += repeatCount;
    if(CursorPos.row > GetNumLines()-1)
      CursorPos.row = GetNumLines()-1;
    Selection->Extend(CursorPos);
    PostCheckSelection();
    ScrollToCaret(CursorPos);
    InvalidateLines(startPos, CursorPos.row);
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::MoveDown(int repeatCount)
{
  if(CursorPos.row < GetNumLines()-1){
    int startPos = CursorPos.row;
    CursorPos.row += repeatCount;
    if(CursorPos.row > GetNumLines()-1)
      CursorPos.row = GetNumLines()-1;
    ScrollToCaret(CursorPos);
    InvalidateLines(startPos, CursorPos.row);
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::ExtendUp(int repeatCount)
{
  if(CursorPos.row > 0){
    int startPos = CursorPos.row;
    PreCheckSelection();
    CursorPos.row -= repeatCount;
    if(CursorPos.row < 0)
      CursorPos.row = 0;
    Selection->Extend(CursorPos);
    PostCheckSelection();
    ScrollToCaret(CursorPos);
    InvalidateLines(startPos, CursorPos.row);
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::MoveUp(int repeatCount)
{
  if(CursorPos.row > 0){
    int startPos = CursorPos.row;
    CursorPos.row -= repeatCount;
    if(CursorPos.row < 0)
      CursorPos.row = 0;
    ScrollToCaret(CursorPos);
    InvalidateLines(startPos, CursorPos.row);
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::ExtendPgDown(int repeatCount)
{
  if(CursorPos.row < GetNumLines()-1){
    int startPos  = CursorPos.row;
    int topLine   = GetTopLine();
    int delta     = repeatCount*GetScreenLines() - 1;
    int newTopLine= topLine + delta;
    if(newTopLine >= GetNumLines())
      newTopLine = GetNumLines() - 1;
 
    if(topLine != newTopLine){
      PreCheckSelection();
      CursorPos.row += delta;
      if(CursorPos.row >= GetNumLines())
        CursorPos.row = GetNumLines() - 1;
      Scroller->ScrollBy(0, newTopLine-topLine);
      Selection->Extend(CursorPos);
      PostCheckSelection();
      ScrollToCaret(CursorPos);
      InvalidateLines(startPos, CursorPos.row);
    }
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::MovePgDown(int repeatCount)
{
  if(CursorPos.row < GetNumLines()-1){
    int startPos = CursorPos.row;
    int topLine   = GetTopLine();
    int delta     = repeatCount*GetScreenLines() - 1;
    int newTopLine= topLine + delta;
    if(newTopLine >= GetNumLines())
      newTopLine = GetNumLines() - 1;
 
    if(topLine != newTopLine){
      CursorPos.row += delta;
      if(CursorPos.row >= GetNumLines())
        CursorPos.row = GetNumLines() - 1;
      Scroller->ScrollBy(0, newTopLine-topLine);
      ScrollToCaret(CursorPos);
      InvalidateLines(startPos, CursorPos.row);
    }
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::ExtendPgUp(int repeatCount)
{
  if(CursorPos.row > 0){
    int startPos = CursorPos.row;
    int topLine = GetTopLine();
    int delta   = repeatCount*GetScreenLines() - 1;
    int newTopLine = topLine - delta;
 
    if (newTopLine < 0)
      newTopLine = 0;
 
    if(topLine != newTopLine || CursorPos.row>0){
      PreCheckSelection();
      CursorPos.row -= delta;
      if(CursorPos.row < 0)
        CursorPos.row = 0;
      Scroller->ScrollBy(0, newTopLine-topLine);
      Selection->Extend(CursorPos);
      PostCheckSelection();
      ScrollToCaret(CursorPos);
      InvalidateLines(startPos, CursorPos.row);
    }
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::MovePgUp(int repeatCount)
{
  if(CursorPos.row > 0){
    int startPos = CursorPos.row;
    int topLine = GetTopLine();
    int delta   = repeatCount*GetScreenLines() - 1;
    int newTopLine = topLine - delta;
 
    if (newTopLine < 0)
      newTopLine = 0;
 
    if(topLine != newTopLine || CursorPos.row>0){
      CursorPos.row -= delta;
      if(CursorPos.row < 0)
        CursorPos.row = 0;
      Scroller->ScrollBy(0, newTopLine-topLine);
      ScrollToCaret(CursorPos);
      InvalidateLines(startPos, CursorPos.row);
    }
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::ExtendCtrlHome(int /*repeatCount*/)
{
  if(CursorPos.col || CursorPos.row){
    int startPos = CursorPos.row;
    PreCheckSelection();
    CursorPos.col = 0;
    CursorPos.row = 0;
    Selection->Extend(CursorPos);
    PostCheckSelection();
    ScrollToCaret(CursorPos);
    InvalidateLines(startPos, CursorPos.row);
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::MoveCtrlHome(int /*repeatCount*/)
{
  if(CursorPos.col || CursorPos.row){
    int startPos = CursorPos.row;
    CursorPos.col = 0;
    CursorPos.row = 0;
    ScrollToCaret(CursorPos);
    InvalidateLines(startPos, CursorPos.row);
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::ExtendHome(int /*repeatCount*/)
{
  if(CursorPos.col){
    int startPos = CursorPos.row;
    PreCheckSelection();
    CursorPos.col = 0;
    Selection->Extend(CursorPos);
    PostCheckSelection();
    ScrollToCaret(CursorPos);
    InvalidateLines(startPos, CursorPos.row);
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::MoveHome(int /*repeatCount*/)
{
  if(CursorPos.col){
    int startPos = CursorPos.row;
    CursorPos.col = 0;
    ScrollToCaret(CursorPos);
    InvalidateLines(startPos, CursorPos.row);
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::ExtendCtrlEnd(int /*repeatCount*/)
{
  int endLine = GetNumLines()-1;
  TEditPos endPos(GetLineLength(endLine), endLine);
  if(CursorPos != endPos){
    int startPos = CursorPos.row;
    PreCheckSelection();
    CursorPos = endPos;
    Selection->Extend(CursorPos);
    PostCheckSelection();
    ScrollToCaret(CursorPos);
    InvalidateLines(startPos, CursorPos.row);
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::MoveCtrlEnd(int /*repeatCount*/)
{
  int endLine = GetNumLines()-1;
  TEditPos endPos(GetLineLength(endLine), endLine);
  if(CursorPos != endPos){
    int startPos = CursorPos.row;
    CursorPos = endPos;
    ScrollToCaret(CursorPos);
    InvalidateLines(startPos, CursorPos.row);
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::ExtendEnd(int /*repeatCount*/)
{
  int endPos = GetLineLength(CursorPos.row);
  if(CursorPos.col != endPos){
    PreCheckSelection();
    CursorPos.col = endPos;
    Selection->Extend(CursorPos);
    PostCheckSelection();
    ScrollToCaret(CursorPos);
    InvalidateLines(CursorPos.row, CursorPos.row);
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::MoveEnd(int /*repeatCount*/)
{
  int endPos = GetLineLength(CursorPos.row);
  if(CursorPos.col != endPos){
    CursorPos.col = endPos;
    ScrollToCaret(CursorPos);
    InvalidateLines(CursorPos.row, CursorPos.row);
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::ScrollDown(int repeatCount)
{
  if(Scroller->YPos < Scroller->YRange){
    if(repeatCount > (Scroller->YRange-Scroller->YPos))
      repeatCount = Scroller->YRange - Scroller->YPos;
    Scroller->ScrollBy(0,repeatCount);
    CursorPos.row += repeatCount;
    SetCursorPos(CursorPos);
  }
}
//-----------------------------------------------------------------------------
void
THexViewWnd::ScrollUp(int repeatCount)
{
  if(Scroller->YPos > 0){
    if(repeatCount > Scroller->YPos)
      repeatCount = Scroller->YPos;
    Scroller->ScrollBy(0,-(int)repeatCount);
    CursorPos.row -= repeatCount;
    SetCursorPos(CursorPos);
  }
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// THexEditView Implementation
//
//
// Build a response table for all messages/commands handled
// by THexEditView derived from THexViewWnd.
//
DEFINE_RESPONSE_TABLE1(THexEditView, THexViewWnd)
  EV_VN_ISWINDOW,
END_RESPONSE_TABLE;
//
THexEditView::THexEditView(TDocument& doc, TWindow* parent)
:
  THexViewWnd(parent, 0, _T(""), 0, 0, 0, 0, 0),
  TView(doc)
{
}
//
THexEditView::~THexEditView()
{
  //
}
 
/*
void CHexEditView::OnLButtonDown(UINT nFlags, CPoint point)
{
  SetFocus();
  if(!m_pData || !*m_pData)
    return;
 
  if(nFlags & MK_SHIFT)
  {
    m_selStart = m_currentAddress;
  }
  CPoint pt = CalcPos(point.x, point.y);
  if(pt.x > -1)
  {
    m_editPos = pt;
    pt.x *= m_nullWidth;
    pt.y *= m_lineHeight;
 
    if(pt.x == 0 && (m_dwFlags & HVW_SHOW_ADDRESS))
      CreateAddressCaret();
    else
      CreateEditCaret();
 
    SetCaretPos(pt);
    if(nFlags & MK_SHIFT)
    {
      m_selEnd = m_currentAddress;
      if(m_currentMode == EDIT_HIGH || m_currentMode == EDIT_LOW)
        m_selEnd++;
      RedrawWindow();
    }
  }
  if(!(nFlags & MK_SHIFT))
  {
    if(DragDetect(this->m_hWnd, point))
    {
      m_selStart = m_currentAddress;
      m_selEnd   = m_selStart;
      SetCapture();
    }
    else
    {
      BOOL bsel = m_selStart != 0xffffffff;
 
      m_selStart = 0xffffffff;
      m_selEnd   = 0xffffffff;
      if(bsel)
        RedrawWindow();
    }
  }
  if(!IsSelected())
  {
    ShowCaret();
  }
}
 
void CHexEditView::OnLButtonDblClk(UINT nFlags, CPoint point)
{
  nFlags; point;
}
 
void CHexEditView::OnMouseMove(UINT nFlags, CPoint point)
{
  if(!m_pData || !*m_pData)
    return;
 
  if(nFlags & MK_LBUTTON && m_selStart != 0xffffffff)
  {
    CRect rc;
    GetClientRect(&rc);
    if(!rc.PtInRect(point))
    {
      if(point.y < 0)
      {
        OnVScroll(SB_LINEUP, 0, NULL);
        point.y = 0;
      }
      else if(point.y > rc.Height())
      {
        OnVScroll(SB_LINEDOWN, 0, NULL);
        point.y = rc.Height() -1;
      }
    }
 
    //
    // we are selecting
    //
    int  seo = m_selEnd;
    CPoint pt = CalcPos(point.x, point.y);
    if(pt.x > -1)
    {
      m_selEnd = m_currentAddress;
      if(m_currentMode == EDIT_HIGH || m_currentMode == EDIT_LOW)
        m_selEnd++;
    }
    if(IsSelected())
      DestroyCaret();
 
    if(seo != m_selEnd)
      RedrawWindow();
  }
}
 
void CHexEditView::OnLButtonUp(UINT nFlags, CPoint point)
{
  if(IsSelected())
    ReleaseCapture();
 
  CWnd::OnLButtonUp(nFlags, point);
}
 
void CHexEditView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
  nFlags;nRepCnt;
  if(!m_pData || !*m_pData)
    return;
  if(nChar == _T ('\t'))
    return;
  if(GetKeyState(VK_CONTROL) & 0x80000000)
  {
    switch(nChar)
    {
      case 0x03:
        if(IsSelected())
          OnEditCopy();
        return;
      case 0x16:
        OnEditPaste();
        return;
      case 0x18:
        if(IsSelected())
          OnEditCut();
        return;
      case 0x1a:
        OnEditUndo();
        return;
    }
  }
 
  if(nChar == 0x08)
  {
    if(m_currentAddress > 0)
    {
      m_currentAddress--;
      SelDelete(m_currentAddress, m_currentAddress+1);
      RepositionCaret(m_currentAddress);
      RedrawWindow();
    }
    return;
  }
 
  SetSel(-1, -1);
  if (!(m_dwFlags & HVW_READ_ONLY))
    switch(m_currentMode)
    {
      case EDIT_NONE:
        return;
      case EDIT_HIGH:
      case EDIT_LOW:
        if((nChar >= _T ('0') && nChar <= _T ('9')) || (nChar >= _T ('a') && nChar <= _T ('f')))
        {
          UINT b = nChar - _T ('0');
          if(b > 9)
            b = 10 + nChar - _T ('a');
 
          if(m_currentMode == EDIT_HIGH)
          {
            (*m_pData)[m_currentAddress] = (unsigned char)(((*m_pData)[m_currentAddress] & 0x0f) | (b << 4));
          }
          else
          {
            (*m_pData)[m_currentAddress] = (unsigned char)(((*m_pData)[m_currentAddress] & 0xf0) | b);
          }
          CDocument *pDoc = GetDocument ();
          pDoc->SetModifiedFlag ();
          Move(1,0);
        }
        break;
      case EDIT_ASCII:
        {
          (*m_pData)[m_currentAddress] = (unsigned char)nChar;
          CDocument *pDoc = GetDocument ();
          pDoc->SetModifiedFlag ();
          Move(1,0);
        }
        break;
    }
  RedrawWindow();
}
 
void CHexEditView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
  nFlags; nRepCnt;
 
  BOOL bShift = GetKeyState(VK_SHIFT) & 0x80000000;
  BOOL bac = (m_dwFlags & HVW_NO_ADDRESS_CHANGE) != 0;
  m_dwFlags |= HVW_NO_ADDRESS_CHANGE;
  switch(nChar)
  {
    case VK_DOWN:
      if(bShift)
      {
        if(!IsSelected())
        {
          m_selStart = m_currentAddress;
        }
        Move(0,1);
        m_selEnd   = m_currentAddress;
        if(m_currentMode == EDIT_HIGH || m_currentMode == EDIT_LOW)
          m_selEnd++;
        RedrawWindow();
        break;
      }
      else
        SetSel(-1, -1);
      Move(0,1);
      break;
    case VK_UP:
      if(bShift)
      {
        if(!IsSelected())
        {
          m_selStart = m_currentAddress;
        }
        Move(0,-1);
        m_selEnd   = m_currentAddress;
        RedrawWindow();
        break;
      }
      else
        SetSel(-1, -1);
      Move(0,-1);
      break;
    case VK_LEFT:
      if(bShift)
      {
        if(!IsSelected())
        {
          m_selStart = m_currentAddress;
        }
        Move(-1,0);
        m_selEnd   = m_currentAddress;
        RedrawWindow();
        break;
      }
      else
        SetSel(-1, -1);
      Move(-1,0);
      break;
    case VK_RIGHT:
      if(bShift)
      {
        if(!IsSelected())
        {
          m_selStart = m_currentAddress;
        }
        Move(1,0);
        m_selEnd   = m_currentAddress;
        if(m_currentMode == EDIT_HIGH || m_currentMode == EDIT_LOW)
          m_selEnd++;
        RedrawWindow();
        break;
      }
      else
        SetSel(-1, -1);
      Move(1,0);
      break;
    case VK_PRIOR:
      if(bShift)
      {
        if(!IsSelected())
        {
          m_selStart = m_currentAddress;
        }
        OnVScroll(SB_PAGEUP, 0, NULL);
        Move(0,0);
        m_selEnd   = m_currentAddress;
        RedrawWindow();
        break;
      }
      else
        SetSel(-1, -1);
      OnVScroll(SB_PAGEUP, 0, NULL);
      Move(0,0);
      break;
    case VK_NEXT:
      if(bShift)
      {
        if(!IsSelected())
        {
          m_selStart = m_currentAddress;
        }
        OnVScroll(SB_PAGEDOWN, 0, NULL);
        Move(0,0);
        m_selEnd   = m_currentAddress;
        RedrawWindow();
        break;
      }
      else
        SetSel(-1, -1);
      OnVScroll(SB_PAGEDOWN, 0, NULL);
      Move(0,0);
      break;
    case VK_HOME:
      if(bShift)
      {
        if(!IsSelected())
        {
          m_selStart = m_currentAddress;
        }
        if(GetKeyState(VK_CONTROL) & 0x80000000)
        {
          OnVScroll(SB_THUMBTRACK, 0, NULL);
          Move(0,0);
        }
        else
        {
          m_currentAddress /= m_bpr;
          m_currentAddress *= m_bpr;
          Move(0,0);
        }
        m_selEnd   = m_currentAddress;
        RedrawWindow();
        break;
      }
      else
        SetSel(-1, -1);
      if(GetKeyState(VK_CONTROL) & 0x80000000)
      {
        OnVScroll(SB_THUMBTRACK, 0, NULL);
        m_currentAddress = 0;
        Move(0,0);
      }
      else
      {
        m_currentAddress /= m_bpr;
        m_currentAddress *= m_bpr;
        Move(0,0);
      }
      break;
    case VK_END:
      if(bShift)
      {
        if(!IsSelected())
        {
          m_selStart = m_currentAddress;
        }
        if(GetKeyState(VK_CONTROL) & 0x80000000)
        {
          m_currentAddress = *m_length-1;
          OnVScroll(SB_THUMBTRACK, ((*m_length+(m_bpr/2)) / m_bpr) - m_lpp, NULL);
          Move(0,0);
        }
        else
        {
          m_currentAddress /= m_bpr;
          m_currentAddress *= m_bpr;
          m_currentAddress += m_bpr - 1;
          if(m_currentAddress > *m_length)
            m_currentAddress = *m_length-1;
          Move(0,0);
        }
        m_selEnd   = m_currentAddress;
        RedrawWindow();
        break;
      }
      else
        SetSel(-1, -1);
      if(GetKeyState(VK_CONTROL) & 0x80000000)
      {
        m_currentAddress = *m_length-1;
        if(m_dwFlags & HVW_HALF_PAGE)
          OnVScroll(SB_THUMBTRACK, 0, NULL);
        else
          OnVScroll(SB_THUMBTRACK, ((*m_length+(m_bpr/2)) / m_bpr) - m_lpp, NULL);
        Move(0,0);
      }
      else
      {
        m_currentAddress /= m_bpr;
        m_currentAddress *= m_bpr;
        m_currentAddress += m_bpr - 1;
        if(m_currentAddress > *m_length)
          m_currentAddress = *m_length-1;
        Move(0,0);
      }
      break;
    case VK_INSERT:
      SelInsert(m_currentAddress, std::max(1, m_selEnd-m_selStart));
      RedrawWindow();
      break;
    case VK_DELETE:
      if(IsSelected())
      {
        OnEditClear();
      }
      else
      {
        SelDelete(m_currentAddress, m_currentAddress+1);
        RedrawWindow();
      }
      break;
    case _T ('\t'):
      switch(m_currentMode)
      {
        case EDIT_NONE:
          m_currentMode = EDIT_HIGH;
          break;
        case EDIT_HIGH:
        case EDIT_LOW:
          m_currentMode = EDIT_ASCII;
          break;
        case EDIT_ASCII:
          m_currentMode = EDIT_HIGH;
          break;
      }
      Move(0,0);
      break;
 
  }
  if (bac)
    m_dwFlags |= HVW_NO_ADDRESS_CHANGE;
  else
    m_dwFlags &= ~HVW_NO_ADDRESS_CHANGE;
}
 
void CHexEditView::Move(int x, int y)
{
  switch(m_currentMode)
  {
    case EDIT_NONE:
      return;
    case EDIT_HIGH:
      if(x != 0)
        m_currentMode = EDIT_LOW;
      if(x == -1)
        m_currentAddress --;
      m_currentAddress += y* m_bpr;
      break;
    case EDIT_LOW:
      if(x != 0)
        m_currentMode = EDIT_HIGH;
      if(x == 1)
        m_currentAddress++;
      m_currentAddress += y* m_bpr;
      break;
    case EDIT_ASCII:
      {
        m_currentAddress += x;
        m_currentAddress += y*m_bpr;
      }
      break;
  }
  if(m_currentAddress < 0)
    m_currentAddress = 0;
 
  if(m_currentAddress >= *m_length)
  {
    m_currentAddress -= x;
    m_currentAddress -= y*m_bpr;
  }
  m_dwFlags |= HVW_NO_ADDRESS_CHANGE;
  if(m_currentAddress < m_topindex)
  {
    OnVScroll(SB_LINEUP, 0, NULL);
  }
  if(m_currentAddress >= m_topindex + m_lpp*m_bpr)
  {
    OnVScroll(SB_LINEDOWN, 0, NULL);
  }
  m_dwFlags &= ~HVW_NO_ADDRESS_CHANGE;
  //ScrollIntoView(m_currentAddress);
  RepositionCaret(m_currentAddress);
}
 
void CHexEditView::RepositionCaret(int   p)
{
  int x, y;
 
  y = (p - m_topindex) / m_bpr;
  x = (p - m_topindex) % m_bpr;
 
  switch(m_currentMode)
  {
    case EDIT_NONE:
      CreateAddressCaret();
      x = 0;
      break;
    case EDIT_HIGH:
      CreateEditCaret();
      x *= m_nullWidth*3;
      x += m_offHex;
      break;
    case EDIT_LOW:
      CreateEditCaret();
      x *= m_nullWidth*3;
      x += m_nullWidth;
      x += m_offHex;
      break;
    case EDIT_ASCII:
      CreateEditCaret();
      x *= m_nullWidth;
      x += m_offAscii;
      break;
  }
  m_editPos.x = x;
  m_editPos.y = y*m_lineHeight;
  CRect rc;
  GetClientRect(&rc);
  if(rc.PtInRect(m_editPos))
  {
    SetCaretPos(m_editPos);
    ShowCaret();
  }
}
 
void CHexEditView::OnEditClear()
{
  m_currentAddress = m_selStart;
  SelDelete(m_selStart, m_selEnd);
  RepositionCaret(m_currentAddress);
  RedrawWindow();
}
 
void CHexEditView::OnEditCopy()
{
  COleDataSource*   pSource = new COleDataSource();
  EmptyClipboard();
  if(m_currentMode != EDIT_ASCII)
  {
    int     dwLen = (m_selEnd-m_selStart);
    HGLOBAL   hMemb = ::GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE|GMEM_ZEROINIT, m_selEnd-m_selStart);
    HGLOBAL   hMema = ::GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE|GMEM_ZEROINIT, (dwLen) * 3);
 
    if (!hMema)
      return;
 
    // copy binary
    LPBYTE  p = (BYTE*)::GlobalLock(hMemb);
    memcpy(p, (*m_pData)+m_selStart, dwLen);
    ::GlobalUnlock(hMemb);
 
    // copy ascii
    p = (BYTE*)::GlobalLock(hMema);
    for(int  i = 0; i < dwLen;)
    {
      TOHEX((*m_pData)[m_selStart+i], p);
      *p++ = _T (' ');
      i++;
    }
 
    ::GlobalUnlock(hMema);
 
    pSource->CacheGlobalData(RegisterClipboardFormat(_T ("BinaryData")), hMemb);
    pSource->CacheGlobalData(CF_TEXT, hMema);
  }
  else
  {
    HGLOBAL       hMemb = ::GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE|GMEM_ZEROINIT, m_selEnd-m_selStart);
    HGLOBAL       hMema = ::GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE|GMEM_ZEROINIT, m_selEnd-m_selStart);
    if (!hMemb || !hMema)
      return;
 
    // copy binary
    LPBYTE  p = (BYTE*)::GlobalLock(hMemb);
    int   dwLen = m_selEnd-m_selStart;
 
    memcpy(p, (*m_pData)+m_selStart, dwLen);
    ::GlobalUnlock(hMemb);
 
    // copy ascii
    p = (BYTE*)::GlobalLock(hMema);
    memcpy(p, (*m_pData)+m_selStart, dwLen);
    for(int i = 0; i < dwLen; p++, i++)
      if(!isprint(*p))
        *p = _T ('.');
    ::GlobalUnlock(hMema);
 
    pSource->CacheGlobalData(RegisterClipboardFormat(_T ("BinaryData")), hMemb);
    pSource->CacheGlobalData(CF_TEXT, hMema);
  }
  pSource->SetClipboard();
}
 
void CHexEditView::OnEditPaste()
{
  COleDataObject  obj;
  if (obj.AttachClipboard())
  {
    HGLOBAL hmem = NULL;
    if (obj.IsDataAvailable(RegisterClipboardFormat(_T ("BinaryData"))))
    {
      hmem = obj.GetGlobalData(RegisterClipboardFormat(_T ("BinaryData")));
    }
    else if (obj.IsDataAvailable(CF_TEXT))
    {
      hmem = obj.GetGlobalData(CF_TEXT);
    }
    if(hmem)
    {
      LPBYTE  p = (BYTE*)::GlobalLock(hmem);
      DWORD dwLen = ::GlobalSize(hmem);
      int   insert;
      int   oa = m_currentAddress;
 
      NormalizeSel();
      if(m_selStart == 0xffffffff)
      {
        if(m_currentMode == EDIT_LOW)
          m_currentAddress++;
        insert = m_currentAddress;
        SelInsert(m_currentAddress, dwLen);
      }
      else
      {
        insert = m_selStart;
        SelDelete(m_selStart, m_selEnd);
        SelInsert(insert, dwLen);
        SetSel(-1, -1);
      }
 
      memcpy((*m_pData)+insert, p, dwLen);
 
      m_currentAddress = oa;
      RedrawWindow();
      ::GlobalUnlock(hmem);
    }
  }
}
 
int CHexEditView::GetOrigData(LPBYTE &p)
{
  p = *m_pData;
  return *m_length;
}
 
void CHexEditView::OnBpr (UINT nCmd)
{
  SetBPR(nCmd - ID_FORMAT_BPR_FIRST + 1);
  RedrawWindow ();
}
 
void CHexEditView::OnUpdateBpr (CCmdUI * pCmdUI)
{
  pCmdUI->SetCheck (pCmdUI->m_nID - ID_FORMAT_BPR_FIRST + 1 == (UINT) m_bpr);
}
*/
 

V611 The memory was allocated using 'new T[]' operator but was released using the 'delete' operator. Consider inspecting this code. It's probably better to use 'delete [] SyntaxArray;'.

V763 Parameter 'invalidateMargin' is always rewritten in function body before being used.

V1004 The 'buffer' pointer was used unsafely after it was verified against nullptr. Check lines: 933, 935.

V1004 The 'buffer' pointer was used unsafely after it was verified against nullptr. Check lines: 943, 945.

V1004 The 'buffer' pointer was used unsafely after it was verified against nullptr. Check lines: 1370, 1387.

V547 Expression '!invalidateMargin' is always false.

V547 Expression '!invalidateMargin' is always false.

V547 Expression 'drawWhitespace' is always false.

V547 Expression 'drawWhitespace' is always false.

V560 A part of conditional expression is always true: p.row >= 0.

V657 It's odd that this function always returns one and the same value.

V821 Decreased performance. The 'savename' variable can be constructed in a lower level scope.