* Copyright 2004-2015, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#include "DataView.h"
#include <stdio.h>
#include <stdlib.h>
#include <Autolock.h>
#include <Beep.h>
#include <Clipboard.h>
#include <Mime.h>
#include <ScrollBar.h>
#include <Window.h>
#include "DataEditor.h"
static const uint32 kBlockSize = 16;
static const uint32 kHorizontalSpace = 8;
static const uint32 kVerticalSpace = 4;
static const uint32 kPositionLength = 4;
static const uint32 kBlockSpace = 3;
static const uint32 kHexByteWidth = 3;
string, following the convention from the DataView::ConvertLine()
method: everything that's not replaced by a '.' will be accepted.
*/
bool
is_valid_utf8(uint8 *data, size_t size)
{
for (size_t i = 0; i < size; i++) {
if (i == size - 1 && data[i] == '\0')
return true;
if ((data[i] & 0x80) == 0) {
if ((data[i] < ' '
&& data[i] != 0x0d && data[i] != 0x09 && data[i] != 0x0a)
|| data[i] == 0x7f)
return false;
continue;
}
if ((data[i] & 0xc0) == 0x80) {
return false;
}
uint8 mask = 0x80;
uint32 result = (uint32)(data[i++] & 0xff);
while (result & mask) {
if (mask == 0x02) {
return false;
}
result &= ~mask;
mask >>= 1;
}
while (i < size && (data[i] & 0xc0) == 0x80) {
result <<= 6;
result += data[i++] & 0x3f;
mask <<= 1;
if (mask == 0x40)
break;
}
if (mask != 0x40) {
return false;
}
}
return true;
}
DataView::DataView(DataEditor &editor)
: BView("dataView", B_WILL_DRAW | B_NAVIGABLE | B_FRAME_EVENTS),
fEditor(editor),
fFocus(kHexFocus),
fBase(kHexBase),
fIsActive(true),
fMouseSelectionStart(-1),
fKeySelectionStart(-1),
fBitPosition(0),
fFitFontSize(false),
fDragMessageSize(-1)
{
fStart = fEnd = 0;
if (fEditor.Lock()) {
fDataSize = fEditor.ViewSize();
fOffset = fEditor.ViewOffset();
fEditor.Unlock();
} else
fDataSize = 512;
fData = (uint8 *)malloc(fDataSize);
SetFontSize(12.0);
UpdateFromEditor();
}
DataView::~DataView()
{
}
void
DataView::DetachedFromWindow()
{
fEditor.StopWatching(this);
}
void
DataView::AttachedToWindow()
{
fEditor.StartWatching(this);
MakeFocus(true);
SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR);
SetLowUIColor(ViewUIColor());
SetHighUIColor(B_DOCUMENT_TEXT_COLOR);
}
void
DataView::UpdateFromEditor(BMessage *message)
{
if (fData == NULL)
return;
BAutolock locker(fEditor);
fFileSize = fEditor.FileSize();
int32 start = 0, end = fDataSize - 1;
off_t offset, size;
if (message != NULL
&& message->FindInt64("offset", &offset) == B_OK
&& message->FindInt64("size", &size) == B_OK) {
if (offset > fOffset + (off_t)fDataSize
|| offset + (off_t)size < fOffset) {
return;
}
if (offset > fOffset)
start = offset - fOffset;
if (offset + (off_t)size < fOffset + (off_t)fDataSize)
end = offset + size - fOffset;
}
if (fOffset + (off_t)fDataSize > fFileSize)
fSizeInView = fFileSize - fOffset;
else
fSizeInView = fDataSize;
const uint8 *data;
if (fEditor.GetViewBuffer(&data) == B_OK)
memcpy(fData, data, fDataSize);
InvalidateRange(start, end);
if (start <= fEnd && end >= fStart) {
BMessage update;
update.AddInt64("start", fStart);
update.AddInt64("end", fEnd);
SendNotices(kDataViewSelection, &update);
}
}
bool
DataView::AcceptsDrop(const BMessage *message)
{
return !fEditor.IsReadOnly()
&& (message->HasData("text/plain", B_MIME_TYPE)
|| message->HasData(B_FILE_MIME_TYPE, B_MIME_TYPE));
}
void
DataView::MessageReceived(BMessage *message)
{
switch (message->what) {
case kMsgUpdateData:
case kMsgDataEditorUpdate:
UpdateFromEditor(message);
break;
case kMsgDataEditorParameterChange:
{
int32 viewSize;
off_t offset;
if (message->FindInt64("offset", &offset) == B_OK) {
fOffset = offset;
SetSelection(0, 0);
MakeVisible(0);
}
if (message->FindInt32("view_size", &viewSize) == B_OK) {
fDataSize = viewSize;
fData = (uint8 *)realloc(fData, fDataSize);
UpdateScroller();
SendNotices(kDataViewPreferredSize);
}
if (message->FindInt64("file_size", &offset) == B_OK)
UpdateFromEditor();
break;
}
case kMsgBaseType:
{
int32 type;
if (message->FindInt32("base", &type) != B_OK)
break;
SetBase((base_type)type);
break;
}
case kMsgSetSelection:
{
int64 start, end;
if (message->FindInt64("start", &start) != B_OK
|| message->FindInt64("end", &end) != B_OK)
break;
SetSelection(start, end);
break;
}
case B_SELECT_ALL:
SetSelection(0, fDataSize - 1);
break;
case B_COPY:
Copy();
break;
case B_PASTE:
Paste();
break;
case B_UNDO:
fEditor.Undo();
break;
case B_REDO:
fEditor.Redo();
break;
case B_MIME_DATA:
if (AcceptsDrop(message)) {
const void *data;
ssize_t size;
if (message->FindData("text/plain", B_MIME_TYPE, &data, &size) == B_OK
|| message->FindData(B_FILE_MIME_TYPE, B_MIME_TYPE, &data, &size) == B_OK) {
if (fEditor.Replace(fOffset + fStart, (const uint8 *)data, size) != B_OK)
SetSelection(fStoredStart, fStoredEnd);
fDragMessageSize = -1;
}
}
break;
default:
BView::MessageReceived(message);
}
}
void
DataView::Copy()
{
if (!be_clipboard->Lock())
return;
be_clipboard->Clear();
BMessage *clip;
if ((clip = be_clipboard->Data()) != NULL) {
uint8 *data = fData + fStart;
size_t length = fEnd + 1 - fStart;
clip->AddData(B_FILE_MIME_TYPE, B_MIME_TYPE, data, length);
if (is_valid_utf8(data, length))
clip->AddData("text/plain", B_MIME_TYPE, data, length);
be_clipboard->Commit();
}
be_clipboard->Unlock();
}
void
DataView::Paste()
{
if (!be_clipboard->Lock())
return;
const void *data;
ssize_t length;
BMessage *clip;
if ((clip = be_clipboard->Data()) != NULL
&& (clip->FindData(B_FILE_MIME_TYPE, B_MIME_TYPE, &data, &length) == B_OK
|| clip->FindData("text/plain", B_MIME_TYPE, &data, &length) == B_OK)) {
if (fOffset + fStart + length > fFileSize)
length = fFileSize - fOffset;
if (fEditor.Replace(fOffset + fStart, (const uint8 *)data, length) == B_OK)
SetSelection(fStart + length, fStart + length);
} else
beep();
be_clipboard->Unlock();
}
void
DataView::ConvertLine(char *line, off_t offset, const uint8 *buffer, size_t size)
{
if (size == 0) {
line[0] = '\0';
return;
}
line += sprintf(line, fBase == kHexBase ? "%0*" B_PRIxOFF": " : "%0*"
B_PRIdOFF": ", (int)kPositionLength, offset);
for (uint32 i = 0; i < kBlockSize; i++) {
if (i >= size) {
strcpy(line, " ");
line += kHexByteWidth;
} else
line += sprintf(line, "%02x ", *(unsigned char *)(buffer + i));
}
strcpy(line, " ");
line += 3;
for (uint32 i = 0; i < kBlockSize; i++) {
if (i < size) {
char c = buffer[i];
if (c < ' ' || c == 0x7f)
*line++ = '.';
else
*line++ = c;
} else
break;
}
*line = '\0';
}
void
DataView::Draw(BRect updateRect)
{
if (fData == NULL || fFileSize == 0)
return;
char line[255];
BPoint location(kHorizontalSpace, kVerticalSpace + fAscent);
const float kTintDarkenTouch = (B_NO_TINT + B_DARKEN_1_TINT) / 2.0f;
for (uint32 lineNum = 0, i = 0; i < fSizeInView; lineNum++, i += kBlockSize) {
float tint = (lineNum % 2 == 0) ? B_NO_TINT : kTintDarkenTouch;
SetLowUIColor(B_DOCUMENT_BACKGROUND_COLOR, tint);
FillRect(BRect(location.x - kHorizontalSpace,
kVerticalSpace + lineNum * fFontHeight,
location.x - kHorizontalSpace / 2 + Bounds().right,
kVerticalSpace + (lineNum + 1) * fFontHeight), B_SOLID_LOW);
ConvertLine(line, i, fData + i, fSizeInView - i);
DrawString(line, location);
location.y += fFontHeight;
}
BPoint line_start(kHorizontalSpace + fCharWidth * 18 + fCharWidth / 2, 0);
BPoint line_end(line_start.x, Bounds().bottom);
rgb_color lineColor = tint_color(lineColor, B_LIGHTEN_2_TINT);
uint32 kDrawNumLines = 3;
BeginLineArray(kDrawNumLines);
for (uint32 i = 0; i < kDrawNumLines; i++) {
AddLine(line_start, line_end, lineColor);
line_start.x += fCharWidth * 12;
line_end.x = line_start.x;
}
EndLineArray();
DrawSelection();
}
BRect
DataView::DataBounds(bool inView) const
{
return BRect(0, 0,
fCharWidth * (kBlockSize * 4 + kPositionLength + 6) + 2 * kHorizontalSpace,
fFontHeight * (((inView ? fSizeInView : fDataSize) + kBlockSize - 1) / kBlockSize)
+ 2 * kVerticalSpace);
}
int32
DataView::PositionAt(view_focus focus, BPoint point, view_focus *_newFocus)
{
BRect bounds = DataBounds(true);
if (point.x < bounds.left)
point.x = bounds.left;
else if (point.x > bounds.right)
point.x = bounds.right;
if (point.y < bounds.top)
point.y = bounds.top;
else if (point.y >= bounds.bottom - kVerticalSpace)
point.y = bounds.bottom - kVerticalSpace - 1;
float left = fCharWidth * (kPositionLength + kBlockSpace) + kHorizontalSpace;
float hexWidth = fCharWidth * kBlockSize * kHexByteWidth;
float width = fCharWidth;
if (focus == kNoFocus) {
if (point.x < left - width / 2)
return -1;
if (point.x > left + hexWidth)
focus = kAsciiFocus;
else
focus = kHexFocus;
if (_newFocus)
*_newFocus = focus;
}
if (focus == kHexFocus) {
left -= width / 2;
width *= kHexByteWidth;
} else
left += hexWidth + (kBlockSpace * width);
int32 row = int32((point.y - kVerticalSpace) / fFontHeight);
int32 column = int32((point.x - left) / width);
if (column >= (int32)kBlockSize)
column = (int32)kBlockSize - 1;
else if (column < 0)
column = 0;
return row * kBlockSize + column;
}
BRect
DataView::SelectionFrame(view_focus which, int32 start, int32 end)
{
float spacing = 0;
float width = fCharWidth;
float byteWidth = fCharWidth;
float left;
if (which == kHexFocus) {
spacing = fCharWidth / 2;
left = width * (kPositionLength + kBlockSpace);
width *= kHexByteWidth;
byteWidth *= 2;
} else
left = width * (kPositionLength + 2 * kBlockSpace + kHexByteWidth * kBlockSize);
left += kHorizontalSpace;
float startInLine = (start % kBlockSize) * width;
float endInLine = (end % kBlockSize) * width + byteWidth - 1;
return BRect(left + startInLine - spacing,
kVerticalSpace + (start / kBlockSize) * fFontHeight,
left + endInLine + spacing,
kVerticalSpace + (end / kBlockSize + 1) * fFontHeight - 1);
}
void
DataView::DrawSelectionFrame(view_focus which)
{
if (fFileSize == 0)
return;
bool drawBlock = false;
bool drawLastLine = false;
BRect block, lastLine;
int32 spacing = 0;
if (which == kAsciiFocus)
spacing++;
int32 start = fStart % kBlockSize;
int32 first = (fStart / kBlockSize) * kBlockSize;
int32 end = fEnd;
if (end > first + (int32)kBlockSize - 1)
end = first + kBlockSize - 1;
BRect firstLine = SelectionFrame(which, first + start, end);
firstLine.right += spacing;
first += kBlockSize;
end = fEnd % kBlockSize;
int32 last = (fEnd / kBlockSize) * kBlockSize;
if (last >= first) {
if (end == kBlockSize - 1)
last += kBlockSize;
if (last > first) {
block = SelectionFrame(which, first, last - 1);
block.right += spacing;
drawBlock = true;
}
if (end != kBlockSize - 1) {
lastLine = SelectionFrame(which, last, last + end);
lastLine.right += spacing;
drawLastLine = true;
}
}
SetDrawingMode(B_OP_INVERT);
BeginLineArray(8);
const rgb_color color = {0, 0, 0};
float bottom;
if (drawBlock)
bottom = block.bottom;
else
bottom = firstLine.bottom;
AddLine(BPoint(firstLine.left + 1, firstLine.top), firstLine.RightTop(), color);
AddLine(BPoint(firstLine.right, firstLine.top + 1), BPoint(firstLine.right, bottom), color);
BRect rect;
if (start == 0 || (!drawBlock && !drawLastLine))
rect = firstLine;
else if (drawBlock)
rect = block;
else
rect = lastLine;
if (drawBlock)
rect.bottom = block.bottom;
if (drawLastLine) {
rect.bottom = lastLine.bottom;
rect.right = lastLine.right;
}
rect.bottom++;
AddLine(rect.LeftTop(), rect.LeftBottom(), color);
AddLine(BPoint(rect.left + 1, rect.bottom), rect.RightBottom(), color);
if (start && (drawLastLine || drawBlock)) {
AddLine(firstLine.LeftTop(), firstLine.LeftBottom(), color);
float right = firstLine.left;
if (!drawBlock && right > lastLine.right)
right = lastLine.right;
AddLine(BPoint(rect.left + 1, rect.top), BPoint(right, rect.top), color);
}
if (drawLastLine) {
AddLine(lastLine.RightBottom(), BPoint(lastLine.right, lastLine.top + 1), color);
if (!drawBlock && lastLine.right <= firstLine.left)
lastLine.right = firstLine.left + (lastLine.right < firstLine.left ? 0 : 1);
AddLine(BPoint(lastLine.right, lastLine.top), BPoint(firstLine.right, lastLine.top), color);
}
EndLineArray();
SetDrawingMode(B_OP_COPY);
}
void
DataView::DrawSelectionBlock(view_focus which, int32 blockStart, int32 blockEnd)
{
if (fFileSize == 0)
return;
SetDrawingMode(B_OP_INVERT);
int32 start = blockStart % kBlockSize;
int32 first = (blockStart / kBlockSize) * kBlockSize;
int32 end = blockEnd;
if (end > first + (int32)kBlockSize - 1)
end = first + kBlockSize - 1;
FillRect(SelectionFrame(which, first + start, end));
first += kBlockSize;
end = blockEnd % kBlockSize;
int32 last = (blockEnd / kBlockSize) * kBlockSize;
if (last >= first) {
if (end == kBlockSize - 1)
last += kBlockSize;
if (last > first)
FillRect(SelectionFrame(which, first, last - 1));
if (end != kBlockSize - 1)
FillRect(SelectionFrame(which, last, last + end));
}
SetDrawingMode(B_OP_COPY);
}
void
DataView::DrawSelectionBlock(view_focus which)
{
DrawSelectionBlock(which, fStart, fEnd);
}
void
DataView::DrawSelection(bool frameOnly)
{
if (IsFocus() && fIsActive) {
if (!frameOnly)
DrawSelectionBlock(fFocus);
DrawSelectionFrame(fFocus == kHexFocus ? kAsciiFocus : kHexFocus);
} else {
DrawSelectionFrame(kHexFocus);
DrawSelectionFrame(kAsciiFocus);
}
}
void
DataView::SetSelection(int32 start, int32 end, view_focus focus)
{
if (start > end) {
int32 temp = start;
start = end;
end = temp;
}
if (start > (int32)fSizeInView - 1)
start = (int32)fSizeInView - 1;
if (start < 0)
start = 0;
if (end > (int32)fSizeInView - 1)
end = (int32)fSizeInView - 1;
if (end < 0)
end = 0;
if (fStart == start && fEnd == end) {
return;
}
if (fStart != start) {
BMessage update;
update.AddInt64("position", start);
SendNotices(kDataViewCursorPosition, &update);
}
BMessage update;
update.AddInt64("start", start);
update.AddInt64("end", end);
SendNotices(kDataViewSelection, &update);
DrawSelection(focus == kNoFocus);
if (IsFocus() && fIsActive && focus == kNoFocus) {
if (start > fStart) {
DrawSelectionBlock(fFocus, fStart, start - 1);
} else if (start < fStart) {
DrawSelectionBlock(fFocus, start, fStart - 1);
}
if (end < fEnd) {
DrawSelectionBlock(fFocus, end + 1, fEnd);
} else if (end > fEnd) {
DrawSelectionBlock(fFocus, fEnd + 1, end);
}
}
if (focus != kNoFocus)
fFocus = focus;
fStart = start;
fEnd = end;
DrawSelection(focus == kNoFocus);
fBitPosition = 0;
}
void
DataView::GetSelection(int32 &start, int32 &end)
{
start = fStart;
end = fEnd;
}
void
DataView::InvalidateRange(int32 start, int32 end)
{
if (start <= 0 && end >= int32(fDataSize) - 1) {
Invalidate();
return;
}
int32 startLine = start / kBlockSize;
int32 endLine = end / kBlockSize;
if (endLine > startLine) {
start = startLine * kBlockSize;
end = (endLine + 1) * kBlockSize - 1;
}
BRect rect = SelectionFrame(fFocus, start, end);
rect.bottom++;
rect.right++;
Invalidate(rect);
rect = SelectionFrame(fFocus == kHexFocus ? kAsciiFocus : kHexFocus, start, end);
rect.bottom++;
rect.right++;
Invalidate(rect);
}
void
DataView::MakeVisible(int32 position)
{
if (position < 0 || position > int32(fDataSize) - 1)
return;
BRect frame = SelectionFrame(fFocus, position, position);
BRect bounds = Bounds();
if (bounds.Contains(frame))
return;
if ((position % kBlockSize) == 0)
frame.left -= kHorizontalSpace;
else if ((position % kBlockSize) == kBlockSize - 1)
frame.right += kHorizontalSpace;
if (position < int32(kBlockSize))
frame.top -= kVerticalSpace;
else if (position > int32(fDataSize - kBlockSize))
frame.bottom += kVerticalSpace;
BPoint point = bounds.LeftTop();
if (bounds.left > frame.left)
point.x = frame.left;
else if (bounds.right < frame.right)
point.x = frame.right - bounds.Width();
if (bounds.top > frame.top)
point.y = frame.top;
else if (bounds.bottom < frame.bottom)
point.y = frame.bottom - bounds.Height();
ScrollTo(point);
}
const uint8 *
DataView::DataAt(int32 start)
{
if (start < 0 || start >= int32(fSizeInView) || fData == NULL)
return NULL;
return fData + start;
}
int32
DataView::WidthForFontSize(float size)
{
BFont font = be_fixed_font;
font.SetSize(size);
float charWidth = font.StringWidth("w");
return (int32)ceilf(charWidth * (kBlockSize * 4 + kPositionLength + 6)
+ 2 * kHorizontalSpace);
}
void
DataView::SetBase(base_type type)
{
if (fBase == type)
return;
fBase = type;
Invalidate();
}
void
DataView::SetFocus(view_focus which)
{
if (which == fFocus)
return;
DrawSelection();
fFocus = which;
DrawSelection();
}
void
DataView::SetActive(bool active)
{
if (active == fIsActive)
return;
fIsActive = active;
if (IsFocus() && active) {
DrawSelectionFrame(fFocus);
DrawSelectionBlock(fFocus);
} else {
DrawSelectionBlock(fFocus);
DrawSelectionFrame(fFocus);
}
}
void
DataView::WindowActivated(bool active)
{
BView::WindowActivated(active);
SetActive(active);
}
void
DataView::MakeFocus(bool focus)
{
bool previous = IsFocus();
BView::MakeFocus(focus);
if (focus == previous)
return;
if (Window()->IsActive() && focus)
SetActive(true);
else if (!Window()->IsActive() || !focus)
SetActive(false);
}
void
DataView::UpdateScroller()
{
float width, height;
GetPreferredSize(&width, &height);
SetExplicitMinSize(BSize(250, 200));
SetExplicitMaxSize(BSize(B_SIZE_UNSET, height));
SetExplicitPreferredSize(BSize(width, height));
BScrollBar *bar;
if ((bar = ScrollBar(B_HORIZONTAL)) != NULL) {
float delta = width - Bounds().Width();
if (delta < 0)
delta = 0;
bar->SetRange(0, delta);
bar->SetSteps(fCharWidth, Bounds().Width());
bar->SetProportion(Bounds().Width() / width);
}
if ((bar = ScrollBar(B_VERTICAL)) != NULL) {
float delta = height - Bounds().Height();
if (delta < 0)
delta = 0;
bar->SetRange(0, delta);
bar->SetSteps(fFontHeight, Bounds().Height());
bar->SetProportion(Bounds().Height() / height);
}
}
void
DataView::FrameResized(float width, float height)
{
if (fFitFontSize) {
float oldSize = FontSize();
float steps = 0.5f;
float size;
for (size = 1.f; size < 100; size += steps) {
int32 preferredWidth = WidthForFontSize(size);
if (preferredWidth > width)
break;
if (size > 6)
steps = 1.0f;
}
size -= steps;
if (oldSize != size) {
BFont font = be_fixed_font;
font.SetSize(size);
SetFont(&font);
Invalidate();
}
}
UpdateScroller();
}
void
DataView::InitiateDrag(view_focus focus)
{
BMessage *drag = new BMessage(B_MIME_DATA);
drag->AddPointer("be:originator", this);
uint8 *data = fData + fStart;
size_t length = fEnd + 1 - fStart;
drag->AddData(B_FILE_MIME_TYPE, B_MIME_TYPE, data, length);
if (is_valid_utf8(data, length))
drag->AddData("text/plain", B_MIME_TYPE, data, length);
BRect frame;
const int32 width = kBlockSize - 1;
int32 first = fStart & ~width;
int32 last = ((fEnd + width) & ~width) - 1;
if (first == (last & ~width))
frame = SelectionFrame(focus, fStart, fEnd);
else
frame = SelectionFrame(focus, first, last);
BRect bounds = Bounds();
if (!bounds.Contains(frame))
frame = bounds & frame;
DragMessage(drag, frame, NULL);
fStoredStart = fStart;
fStoredEnd = fEnd;
fDragMessageSize = length;
}
void
DataView::MouseDown(BPoint where)
{
MakeFocus(true);
BMessage *message = Looper()->CurrentMessage();
int32 buttons;
if (message == NULL || message->FindInt32("buttons", &buttons) != B_OK)
return;
view_focus newFocus;
int32 position = PositionAt(kNoFocus, where, &newFocus);
if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0
&& position >= fStart && position <= fEnd) {
InitiateDrag(newFocus);
return;
}
if ((buttons & B_PRIMARY_MOUSE_BUTTON) == 0)
return;
int32 modifiers = message->FindInt32("modifiers");
fMouseSelectionStart = position;
if (fMouseSelectionStart == -1) {
return;
}
int32 selectionEnd = fMouseSelectionStart;
if (modifiers & B_SHIFT_KEY) {
if (fStart < selectionEnd)
fMouseSelectionStart = fStart;
else if (fEnd > selectionEnd)
fMouseSelectionStart = fEnd;
}
SetSelection(fMouseSelectionStart, selectionEnd, newFocus);
SetMouseEventMask(B_POINTER_EVENTS,
B_NO_POINTER_HISTORY | B_SUSPEND_VIEW_FOCUS | B_LOCK_WINDOW_FOCUS);
}
void
DataView::MouseMoved(BPoint where, uint32 transit, const BMessage *dragMessage)
{
if (transit == B_EXITED_VIEW && fDragMessageSize > 0) {
SetSelection(fStoredStart, fStoredEnd);
fDragMessageSize = -1;
}
if (dragMessage && AcceptsDrop(dragMessage)) {
if (transit == B_ENTERED_VIEW) {
fStoredStart = fStart;
fStoredEnd = fEnd;
const void *data;
ssize_t size;
if (dragMessage->FindData("text/plain", B_MIME_TYPE, &data, &size) == B_OK
|| dragMessage->FindData("text/plain", B_MIME_TYPE, &data, &size) == B_OK)
fDragMessageSize = size;
} else if (fDragMessageSize > 0) {
view_focus newFocus;
int32 start = PositionAt(kNoFocus, where, &newFocus);
int32 end = start + fDragMessageSize - 1;
SetSelection(start, end);
MakeVisible(start);
}
return;
}
if (fMouseSelectionStart == -1)
return;
int32 end = PositionAt(fFocus, where);
if (end == -1)
return;
SetSelection(fMouseSelectionStart, end);
MakeVisible(end);
}
void
DataView::MouseUp(BPoint where)
{
fMouseSelectionStart = fKeySelectionStart = -1;
}
void
DataView::KeyDown(const char *bytes, int32 numBytes)
{
int32 modifiers;
if (Looper()->CurrentMessage() == NULL
|| Looper()->CurrentMessage()->FindInt32("modifiers", &modifiers) != B_OK)
modifiers = ::modifiers();
switch (bytes[0]) {
case B_LEFT_ARROW:
case B_RIGHT_ARROW:
case B_UP_ARROW:
case B_DOWN_ARROW:
if (modifiers & B_SHIFT_KEY) {
if (fKeySelectionStart == -1)
fKeySelectionStart = fStart;
} else
fKeySelectionStart = -1;
break;
}
switch (bytes[0]) {
case B_LEFT_ARROW:
{
int32 position = fStart - 1;
if (modifiers & B_SHIFT_KEY) {
if (fKeySelectionStart == fEnd)
SetSelection(fStart - 1, fEnd);
else {
SetSelection(fStart, fEnd - 1);
position = fEnd;
}
} else
SetSelection(fStart - 1, fStart - 1);
MakeVisible(position);
break;
}
case B_RIGHT_ARROW:
{
int32 position = fEnd + 1;
if (modifiers & B_SHIFT_KEY) {
if (fKeySelectionStart == fStart)
SetSelection(fStart, fEnd + 1);
else
SetSelection(fStart + 1, fEnd);
} else
SetSelection(fEnd + 1, fEnd + 1);
MakeVisible(position);
break;
}
case B_UP_ARROW:
{
int32 start, end;
if (modifiers & B_SHIFT_KEY) {
if (fKeySelectionStart == fStart) {
start = fEnd - int32(kBlockSize);
end = fStart;
} else {
start = fStart - int32(kBlockSize);
end = fEnd;
}
if (start < 0)
start = 0;
} else {
start = fStart - int32(kBlockSize);
if (start < 0)
start = fStart;
end = start;
}
SetSelection(start, end);
MakeVisible(start);
break;
}
case B_DOWN_ARROW:
{
int32 start, end;
if (modifiers & B_SHIFT_KEY) {
if (fKeySelectionStart == fEnd) {
start = fEnd;
end = fStart + int32(kBlockSize);
} else {
start = fStart;
end = fEnd + int32(kBlockSize);
}
if (end >= int32(fSizeInView))
end = int32(fSizeInView) - 1;
} else {
end = fEnd + int32(kBlockSize);
if (end >= int32(fSizeInView))
start = fEnd;
start = end;
}
SetSelection(start, end);
MakeVisible(end);
break;
}
case B_PAGE_UP:
{
BRect frame = SelectionFrame(fFocus, fStart, fStart);
frame.OffsetBy(0, -Bounds().Height());
if (frame.top <= kVerticalSpace)
frame.top = kVerticalSpace + 1;
ScrollBy(0, -Bounds().Height());
int32 position = PositionAt(fFocus, frame.LeftTop());
SetSelection(position, position);
break;
}
case B_PAGE_DOWN:
{
BRect frame = SelectionFrame(fFocus, fStart, fStart);
frame.OffsetBy(0, Bounds().Height());
float lastLine = DataBounds().Height() - 1 - kVerticalSpace;
if (frame.top > lastLine)
frame.top = lastLine;
ScrollBy(0, Bounds().Height());
int32 position = PositionAt(fFocus, frame.LeftTop());
SetSelection(position, position);
break;
}
case B_HOME:
SetSelection(0, 0);
MakeVisible(fStart);
break;
case B_END:
SetSelection(fDataSize - 1, fDataSize - 1);
MakeVisible(fStart);
break;
case B_TAB:
SetFocus(fFocus == kHexFocus ? kAsciiFocus : kHexFocus);
MakeVisible(fStart);
break;
case B_FUNCTION_KEY:
break;
case B_BACKSPACE:
if (fBitPosition == 0)
SetSelection(fStart - 1, fStart - 1);
if (fFocus == kHexFocus)
fBitPosition = (fBitPosition + 4) % 8;
case B_DELETE:
SetSelection(fStart, fStart);
if (fFocus == kHexFocus) {
const uint8 *data = DataAt(fStart);
if (data == NULL)
break;
uint8 c = data[0] & (fBitPosition == 0 ? 0x0f : 0xf0);
fEditor.Replace(fOffset + fStart, &c, 1);
} else
fEditor.Replace(fOffset + fStart, (const uint8 *)"", 1);
break;
default:
if (fFocus == kHexFocus) {
const uint8 *data = DataAt(fStart);
uint8 c = bytes[0];
if (c >= 'A' && c <= 'F')
c += 'A' - 'a';
const char *hexNumbers = "0123456789abcdef";
addr_t number;
if (data == NULL
|| (number = (addr_t)strchr(hexNumbers, c)) == 0)
break;
SetSelection(fStart, fStart);
number -= (addr_t)hexNumbers;
fBitPosition = (fBitPosition + 4) % 8;
c = (data[0] & (fBitPosition ? 0x0f : 0xf0))
| (number << fBitPosition);
if (fEditor.Replace(fOffset + fStart, &c, 1) == B_OK
&& fBitPosition == 0)
SetSelection(fStart + 1, fStart + 1);
} else {
if (fEditor.Replace(fOffset + fStart, (const uint8 *)bytes,
numBytes) == B_OK)
SetSelection(fStart + 1, fStart + 1);
}
break;
}
}
void
DataView::SetFont(const BFont *font, uint32 properties)
{
if (!font->IsFixed() && !font->IsFullAndHalfFixed())
return;
BView::SetFont(font, properties);
font_height fontHeight;
font->GetHeight(&fontHeight);
fFontHeight = int32(fontHeight.ascent + fontHeight.descent
+ fontHeight.leading);
fAscent = fontHeight.ascent;
fCharWidth = font->StringWidth("w");
}
float
DataView::FontSize() const
{
BFont font;
GetFont(&font);
return font.Size();
}
void
DataView::SetFontSize(float point)
{
bool fit = (point == 0.0f);
if (fit) {
if (!fFitFontSize)
SendNotices(kDataViewPreferredSize);
fFitFontSize = fit;
FrameResized(Bounds().Width(), Bounds().Height());
return;
}
fFitFontSize = false;
BFont font = be_fixed_font;
font.SetSize(point);
SetFont(&font);
UpdateScroller();
Invalidate();
SendNotices(kDataViewPreferredSize);
}
void
DataView::GetPreferredSize(float *_width, float *_height)
{
BRect bounds = DataBounds();
if (_width)
*_width = bounds.Width();
if (_height)
*_height = bounds.Height();
}