ultimatepp/uppsrc/HexView/HexView.cpp
Mirek Fidler 34ff691308 sizeof(wchar) is changed to 4 (32 bits) to support non BMP unicode characters
This might bring some incompatibilities in the code that expects wchar to be 16 bit, which
  escpecially involves dealing with Win32 (and to lesser extend MacOS) APIs, so if your application
  is doing that, please check all instances of WCHAR (UniChar on MacOS) or even wchar
  especially type casts.

  To support host APIs, char16 is introduced (but there is no 16-bit String varian).

  Use ToSystemCharsetW, FromSystemCharsetW to convert texts to Win32 API.

- Support of drawing non-BMP characters in GUI
- Vastly improved character font replacement code (when drawing characters missing with requested font, replacement font is used)
- Last instances of Win32 ANSI calls (those ending with A) are removed
- UTF handling routines are refactored and their's naming is unified
- RTF is now being able to handle non-BMP characters (RTF is used as clipboard format for RichText)

Other minor changes:

- fixed TryRealloc issue
- improved MemoryCheck
- Removed MemoryAlloc48/MemoryFree48
- In theide Background parsing should less often cause delays in the main thread
2021-12-02 12:03:19 +01:00

510 lines
9.7 KiB
C++

#include "HexView.h"
namespace Upp {
inline int FormatHexDigit(int c) {
return c < 10 ? c + '0' : c - 10 + 'a';
}
void FormatHex(char *buffer, int64 number, int n) {
buffer[n] = '\0';
while(n) {
buffer[--n] = FormatHexDigit((byte)number & 0x0f);
number >>= 4;
}
}
void HexViewInfo::PrintValue(Draw& w, int x, int y, int bytes, bool be)
{
dword d = 0;
Size fsz = GetTextSize("X", font);
for(int i = 0; i < bytes; i++) {
int b = data[be ? i : bytes - i - 1];
if(b < 0) {
w.DrawText(x, y, String('?', 2 * bytes), font, SColorHighlight);
x += 2 * bytes * fsz.cx;
w.DrawText(x, y, "=", font);
x += fsz.cx;
w.DrawText(x, y, "?", font, Red);
return;
}
d = (d << 8) | (byte) b;
}
w.DrawText(x, y, FormatIntHex(d, 2 * bytes), font, SColorHighlight);
x += 2 * bytes * fsz.cx;
w.DrawText(x, y, "=", font);
x += fsz.cx;
String txt = FormatUnsigned(d);
w.DrawText(x, y, txt, font, Red);
x += GetTextSize(txt, font).cx;
w.DrawText(x, y, "=", font);
x += fsz.cx;
int q = d;
if(bytes == 1)
q = (int8) d;
else
if(bytes == 2)
q = (int16) d;
w.DrawText(x, y, FormatInt(q), font, Magenta);
}
void HexViewInfo::Paint(Draw& w)
{
Size sz = GetSize();
w.DrawRect(sz, SColorLtFace);
if(mode < 1 || empty)
return;
Size fsz = GetTextSize("X", font);
char h[17];
FormatHex(h, pos, longmode ? 16 : 8);
int xx = 0;
w.DrawText(xx, 0, h, font, SColorHighlight);
xx += (longmode ? 16 : 8) * fsz.cx;
w.DrawText(xx, 0, "=", font);
xx += fsz.cx;
w.DrawText(xx, 0, Format64(pos), font, Red);
xx += (longmode ? 22 : 12) * fsz.cx;
int y = 0;
int x = 0;
for(int q = 0; q < mode; q++) {
x = xx;
if(q < 1)
PrintValue(w, x, y, 1, q);
x += 12 * fsz.cx;
PrintValue(w, x, y, 2, q);
x += 18 * fsz.cx;
PrintValue(w, x, y, 4, q);
x += 32 * fsz.cx;
y += fsz.cy;
}
char sh[80];
memset(sh, 0, sizeof(sh));
int i;
for(i = 0; i < 80; i++) {
if(data[i] < 0)
break;
sh[i] = data[i];
}
WString ws = ToUtf32(sh, i);
w.DrawText(x, 0, ws, font, Cyan, i);
if(mode < 2)
return;
wchar wh[40];
memset(wh, 0, sizeof(wh));
for(i = 0; i < 40; i++) {
if(data[2 * i] < 0 || data[2 * i + 1] < 0)
break;
wh[i] = MAKEWORD(data[2 * i], data[2 * i + 1]);
}
w.DrawText(x, fsz.cy, wh, font, Cyan, i);
String txt;
String ftxt;
i = 0;
for(;;) {
if(data[i] < 0) {
if((unsigned)i < sizeof(float))
ftxt = "?";
txt = "?";
break;
}
if((unsigned)i >= sizeof(double)) {
double h;
memcpy(&h, sh, sizeof(double));
txt = Sprintf("%.8g", h);
break;
}
if(i == sizeof(float)) {
float h;
memcpy(&h, sh, sizeof(float));
ftxt = Sprintf("%.6g", h);
}
sh[i] = data[i];
i++;
}
w.DrawText(0, fsz.cy, txt, font, Red);
w.DrawText(18 * fsz.cx, fsz.cy, ftxt, font, Red);
}
void HexViewInfo::SetMode(int _mode)
{
mode = _mode;
Height(mode * GetTextSize("X", CourierZ(12)).cy + 3);
Show(mode);
}
HexViewInfo::HexViewInfo()
{
SetMode(0);
AddFrame(TopSeparatorFrame());
AddFrame(RightSeparatorFrame());
font = CourierZ(12);
}
int HexView::Byte(int64 adr)
{
return 0;
}
void HexView::Paint(Draw& w)
{
Size sz = GetSize();
w.DrawRect(sz, SColorPaper);
if(!total) {
w.DrawText(Zx(10), Zx(10), "No data", ArialZ(20).Italic(), SRed());
return;
}
int y = 0;
uint64 adr = sc;
while(y < sz.cy) {
char h[17];
FormatHex(h, adr + start, IsLongMode() ? 16 : 8);
w.DrawText(0, y, h, font);
int x = (IsLongMode() ? 17 : 9) * fsz.cx;
int tx = x + columns * fcx3;
for(int q = columns; q--;) {
if(adr >= total)
return;
if(adr == cursor) {
w.DrawRect(x, y, fsz.cx * 2, fsz.cy, LtCyan);
w.DrawRect(tx, y, fsz.cx, fsz.cy, LtCyan);
}
int b = Byte(adr++);
if(b < 0) {
w.DrawText(x, y, "??", font, Brown);
w.DrawText(tx, y, "?", font, Brown);
}
else {
h[0] = FormatHexDigit((b & 0xf0) >> 4);
h[1] = FormatHexDigit(b & 0x0f);
h[2] = '\0';
w.DrawText(x, y, h, font, SColorText);
Color color = SColorMark;
switch(b) {
case '\a': *h = 'a'; break;
case '\b': *h = 'b'; break;
case '\t': *h = 't'; break;
case '\f': *h = 'f'; break;
case '\r': *h = 'r'; break;
case '\n': *h = 'n'; break;
case '\v': *h = 'v'; break;
case '\0': *h = '0'; break;
default:
if(b >= 32) {
*h = b;
color = SColorText;
}
else {
*h = '.';
color = SColorDisabled;
}
}
h[1] = '\0';
w.DrawText(tx, y, h, charset, font, color);
}
tx += fsz.cx;
x += fcx3;
}
y += fsz.cy;
}
}
void HexView::MouseWheel(Point, int zdelta, dword)
{
sb.Wheel(zdelta);
}
void HexView::SetSb()
{
sbm = 0;
uint64 sz = total;
while((sz >> sbm) > (1 << 30))
sbm++;
sb.SetTotal(int(sz >> sbm) / columns + 2);
sb.SetPage(int(rows >> sbm));
sb.Set(int(sc >> sbm) / columns + 1);
}
void HexView::Layout()
{
Size sz = GetSize();
columns = fixed ? fixed : max(4, (sz.cx - (IsLongMode() ? 18 : 10) * fsz.cx) / (4 * fsz.cx));
rows = max(1, sz.cy / fsz.cy);
bytes = columns * rows;
SetSb();
}
void HexView::SetStart(uint64 start_)
{
start = start_;
Layout();
SetSb();
Refresh();
RefreshInfo();
}
void HexView::SetTotal(uint64 _total)
{
total = _total;
Layout();
SetSb();
Refresh();
RefreshInfo();
}
void HexView::SetSc(uint64 address)
{
sc = minmax(address, start, total);
SetSb();
Refresh();
}
void HexView::Scroll()
{
int64 q = (int64)(int)sb << sbm;
if(q == 0)
sc = 0;
else
sc = (q - 1) * columns + sc % columns;
Refresh();
}
void HexView::RefreshInfo()
{
if(total) {
info.SetPos(cursor + start, IsLongMode());
for(int i = 0; i < 80; i++)
info.Set(i, Byte(cursor + i));
}
else
info.SetEmpty();
}
void HexView::SetCursor(uint64 _cursor)
{
cursor = _cursor;
if(cursor > total)
cursor = total - 1;
int q = int(sc % columns);
if(cursor >= sc + bytes)
sc = cursor - bytes + columns;
if(cursor < sc) {
sc = cursor;
}
if(sc > (uint64)q)
sc = (sc - q) / columns * columns + q;
if(sc >= total)
sc = total - 1;
SetSb();
Refresh();
RefreshInfo();
}
void HexView::LeftDown(Point p, dword)
{
int rowi = p.y / fsz.cy;
int x = (IsLongMode() ? 17 : 9) * fsz.cx;
int tx = x + columns * fcx3;
if(p.x >= x && p.x < tx) {
x = p.x - x;
int q = x / fcx3;
if(x - q * fcx3 < 2 * fsz.cx && q < columns) {
uint64 c = sc + rowi * columns + q;
if(c < total)
SetCursor(c);
}
}
else
if(p.x >= tx) {
int q = (p.x - tx) / fsz.cx;
if(q >= 0 && q < columns) {
uint64 c = sc + rowi * columns + q;
if(c < total)
SetCursor(c);
}
}
SetFocus();
}
bool HexView::Key(dword key, int)
{
int pg = max(columns, bytes - columns);
int q = int(sc % columns);
switch(key) {
case K_LEFT:
SetCursor(cursor - 1);
return true;
case K_RIGHT:
SetCursor(cursor + 1);
return true;
case K_UP:
SetCursor(cursor - columns);
return true;
case K_DOWN:
SetCursor(cursor + columns);
return true;
case K_PAGEUP:
SetSc(sc - pg);
SetCursor(cursor - pg);
return true;
case K_PAGEDOWN:
SetSc(sc + pg);
SetCursor(cursor + pg);
return true;
case K_CTRL_LEFT:
SetSc(sc - 1);
break;
case K_CTRL_RIGHT:
SetSc(sc + 1);
break;
case K_CTRL_UP:
SetSc(sc - columns);
break;
case K_CTRL_DOWN:
SetSc(sc + columns);
break;
case K_HOME:
SetCursor((cursor - q) / columns * columns + q);
break;
case K_END:
SetCursor((cursor - q) / columns * columns + q + columns - 1);
break;
case K_CTRL_HOME:
case K_CTRL_PAGEUP:
SetCursor(0);
break;
case K_CTRL_END:
case K_CTRL_PAGEDOWN:
SetCursor(total - 1);
break;
}
return MenuBar::Scan(WhenBar, key);
}
void HexView::SetColumns(int x)
{
FixedColumns(x);
}
void HexView::SetCharset(int chr)
{
Charset(chr);
}
void HexView::StdGoto(const String& s)
{
CParser p(s);
int n = 10;
if(p.Char2('0', 'x') || p.Char('$') || p.Char('#'))
n = 16;
if(p.IsNumber(n)) {
uint64 a = p.ReadNumber(n);
if(a < total) {
SetCursor(a);
SetSc(a);
return;
}
}
Exclamation("Invalid position!");
}
void HexView::Goto()
{
if(go.Execute() == IDOK)
WhenGoto((String)~go.text);
}
void HexView::ColumnsMenu(Bar& bar)
{
bar.Add("Auto", THISBACK1(SetColumns, 0))
.Radio(fixed == 0);
bar.Add("8", THISBACK1(SetColumns, 8))
.Radio(fixed == 8);
bar.Add("16", THISBACK1(SetColumns, 16))
.Radio(fixed == 16);
bar.Add("32", THISBACK1(SetColumns, 32))
.Radio(fixed == 32);
}
void HexView::SetInfo(int m)
{
info.SetMode(m);
}
void HexView::InfoMenu(Bar& bar)
{
bar.Add("None", THISBACK1(SetInfo, 0))
.Check(info.GetMode() == 0);
bar.Add("Standard", THISBACK1(SetInfo, 1))
.Check(info.GetMode() == 1);
bar.Add("Extended", THISBACK1(SetInfo, 2))
.Check(info.GetMode() == 2);
}
void HexView::CharsetMenu(Bar& bar)
{
for(int i = 1; i < CharsetCount(); i++)
bar.Add(CharsetName(i), THISBACK1(SetCharset, i))
.Radio(charset == i);
}
void HexView::StdMenu(Bar& bar)
{
bar.Add("Go to..", [=] { WhenGotoDlg(); })
.Key(K_CTRL_G);
bar.Add("Columns", THISBACK(ColumnsMenu));
bar.Add("Charset", THISBACK(CharsetMenu));
bar.Add("Position info", THISBACK(InfoMenu));
}
void HexView::RightDown(Point p, dword w)
{
LeftDown(p, w);
MenuBar::Execute(WhenBar);
}
HexView& HexView::SetFont(Font fnt)
{
font = fnt;
fsz = GetTextSize("X", font);
fcx3 = 3 * fsz.cx;
Layout();
Refresh();
SetSb();
return *this;
}
void HexView::SerializeSettings(Stream& s)
{
int version = 0;
s / version;
s / fixed;
s % charset;
int mode = info.GetMode();
s / mode;
info.SetMode(mode);
go.text.SerializeList(s);
}
HexView::HexView()
{
SetFont(CourierZ(12));
BackPaint();
charset = CHARSET_WIN1252;
sb <<= THISBACK(Scroll);
SetFrame(InsetFrame());
AddFrame(sb);
cursor = sc = 0;
total = 0;
fixed = 0;
SetSc(0);
SetCursor(0);
AddFrame(info);
info.SetMode(1);
WhenBar = THISBACK(StdMenu);
CtrlLayoutOKCancel(go, "Go to");
WhenGoto = THISBACK(StdGoto);
WhenGotoDlg = THISBACK(Goto);
}
}