diff --git a/src/apps/diskprobe/DataView.cpp b/src/apps/diskprobe/DataView.cpp index 28088428d8..9109401924 100644 --- a/src/apps/diskprobe/DataView.cpp +++ b/src/apps/diskprobe/DataView.cpp @@ -8,26 +8,35 @@ #include "DataEditor.h" #include +#include static const uint32 kBlockSize = 16; static const uint32 kHorizontalSpace = 8; static const uint32 kVerticalSpace = 4; +static const uint32 kBlockSpace = 3; +static const uint32 kHexByteWidth = 3; + // these are determined by the implementation of DataView::ConvertLine() + DataView::DataView(BRect rect, DataEditor &editor) : BView(rect, "dataView", B_FOLLOW_ALL, B_WILL_DRAW | B_NAVIGABLE | B_FRAME_EVENTS), - fEditor(editor) + fEditor(editor), + fFocus(kHexFocus), + fIsActive(true) { fPositionLength = 4; + fStart = fEnd = 0; + fMouseSelectionStart = -1; + fDataSize = fEditor.ViewSize(); fData = (uint8 *)malloc(fDataSize); SetFontSize(12.0); + // also sets the fixed font UpdateFromEditor(); - - SetFont(be_fixed_font); } @@ -141,10 +150,385 @@ DataView::Draw(BRect updateRect) location.y += fFontHeight; } // ToDo: clear unused space! + + DrawSelection(); +} + + +int32 +DataView::PositionAt(view_focus focus, BPoint point, view_focus *_newFocus) +{ + float left = fCharWidth * (fPositionLength + kBlockSpace) + kHorizontalSpace; + float hexWidth = fCharWidth * kBlockSize * kHexByteWidth; + float width = fCharWidth; + + if (focus == kNoFocus) { + // find in which part the point is in + 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 * (fPositionLength + kBlockSpace); + width *= kHexByteWidth; + byteWidth *= 2; + } else + left = width * (fPositionLength + 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) +{ + bool drawBlock = false; + bool drawLastLine = false; + BRect block, lastLine; + int32 spacing = 0; + if (which == kAsciiFocus) + spacing++; + + // draw first line + + 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; + + // draw block (and last line) if necessary + + 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) +{ + // draw first line + + SetDrawingMode(B_OP_INVERT); + + int32 start = fStart % kBlockSize; + int32 first = (fStart / kBlockSize) * kBlockSize; + + int32 end = fEnd; + if (end > first + (int32)kBlockSize - 1) + end = first + kBlockSize - 1; + + FillRect(SelectionFrame(which, first + start, end)); + first += kBlockSize; + + // draw block (and last line) if necessary + + end = fEnd % kBlockSize; + int32 last = (fEnd / 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::DrawSelection() +{ + if (fIsActive) { + 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 < 0) + start = 0; + if (end > (int32)fDataSize - 1) + end = (int32)fDataSize - 1; + + if (fStart == start && fEnd == end) { + // nothing has changed, no need to update + return; + } + + // remove old selection + // ToDo: this could be drastically improved if only the changed + // parts are drawn! Of course, this would only work for the + // selection block, not the frame. + DrawSelection(); + + if (focus != kNoFocus) + fFocus = focus; + fStart = start; + fEnd = end; + + DrawSelection(); +} + + +void +DataView::GetSelection(int32 &start, int32 &end) +{ + start = fStart; + end = fEnd; +} + + +void +DataView::InvalidateRange(int32 start, int32 end) +{ + int32 startLine = start / kBlockSize; + int32 endLine = end / kBlockSize; + + if (endLine > startLine) { + start = startLine * kBlockSize; + end = (endLine + 1) * kBlockSize - 1; + } + + // the part with focus + BRect rect = SelectionFrame(fFocus, start, end); + rect.bottom++; + rect.right++; + Invalidate(rect); + + // the part without focus + rect = SelectionFrame(fFocus == kHexFocus ? kAsciiFocus : kHexFocus, start, end); + rect.bottom++; + rect.right++; + Invalidate(rect); } void +DataView::SetFocus(view_focus which) +{ + if (which == fFocus) + return; + + DrawSelection(); + fFocus = which; + DrawSelection(); +} + + +void +DataView::WindowActivated(bool active) +{ + fIsActive = active; + + // only redraw the focussed part + + if (fIsActive) { + DrawSelectionFrame(fFocus); + DrawSelectionBlock(fFocus); + } else { + DrawSelectionBlock(fFocus); + DrawSelectionFrame(fFocus); + } +} + + +void +DataView::MouseDown(BPoint where) +{ + BMessage *message = Looper()->CurrentMessage(); + int32 buttons; + if (message == NULL || message->FindInt32("buttons", &buttons) != B_OK + || (buttons & B_PRIMARY_MOUSE_BUTTON) == 0) + return; + + int32 modifiers = message->FindInt32("modifiers"); + + view_focus newFocus; + fMouseSelectionStart = PositionAt(kNoFocus, where, &newFocus); + if (fMouseSelectionStart == -1) { + // "where" is outside the valid frame + return; + } + + int32 selectionEnd = fMouseSelectionStart; + if (modifiers & B_SHIFT_KEY) { + // enlarge the current selection + 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 (fMouseSelectionStart == -1) + return; + + SetSelection(fMouseSelectionStart, PositionAt(fFocus, where)); +} + + +void +DataView::MouseUp(BPoint where) +{ + fMouseSelectionStart = -1; +} + + +void DataView::SetFont(const BFont *font, uint32 properties) { if (!font->IsFixed()) @@ -157,6 +541,7 @@ DataView::SetFont(const BFont *font, uint32 properties) fFontHeight = int32(fontHeight.ascent + fontHeight.descent + fontHeight.leading); fAscent = fontHeight.ascent; + fCharWidth = font->StringWidth("w"); } @@ -176,7 +561,7 @@ DataView::GetPreferredSize(float *_width, float *_height) if (_width) { BFont font; GetFont(&font); - *_width = font.StringWidth("w") * (kBlockSize * 4 + fPositionLength + 6) + *_width = fCharWidth * (kBlockSize * 4 + fPositionLength + 6) + 2 * kHorizontalSpace; } diff --git a/src/apps/diskprobe/DataView.h b/src/apps/diskprobe/DataView.h index 0d7b70949c..cb7b18a7f8 100644 --- a/src/apps/diskprobe/DataView.h +++ b/src/apps/diskprobe/DataView.h @@ -13,6 +13,11 @@ class DataEditor; +enum view_focus { + kNoFocus, + kHexFocus, + kAsciiFocus +}; class DataView : public BView { public: @@ -24,22 +29,44 @@ class DataView : public BView { virtual void MessageReceived(BMessage *message); virtual void Draw(BRect updateRect); + virtual void MouseDown(BPoint where); + virtual void MouseMoved(BPoint where, uint32 transit, const BMessage *message); + virtual void MouseUp(BPoint where); + + virtual void WindowActivated(bool active); virtual void SetFont(const BFont *font, uint32 properties = B_FONT_ALL); virtual void GetPreferredSize(float *_width, float *_height); void SetFontSize(float point); + void SetSelection(int32 start, int32 end, view_focus focus = kNoFocus); + void GetSelection(int32 &start, int32 &end); + void InvalidateRange(int32 start, int32 end); + private: + BRect SelectionFrame(view_focus which, int32 start, int32 end); + int32 PositionAt(view_focus focus, BPoint point, view_focus *_newFocus = NULL); + + void DrawSelectionFrame(view_focus which); + void DrawSelectionBlock(view_focus which); + void DrawSelection(); + void SetFocus(view_focus which); + void UpdateFromEditor(BMessage *message = NULL); void ConvertLine(char *line, off_t offset, const uint8 *buffer, size_t size); DataEditor &fEditor; int32 fPositionLength; uint8 *fData; - uint32 fDataSize; + size_t fDataSize; off_t fOffset; float fAscent; int32 fFontHeight; + float fCharWidth; + view_focus fFocus; + bool fIsActive; + int32 fStart, fEnd; + int32 fMouseSelectionStart; }; #endif /* DATA_VIEW_H */