2 * Copyright 2004-2015, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
14 #include <Clipboard.h>
16 #include <ScrollBar.h>
19 #include "DataEditor.h"
22 static const uint32 kBlockSize
= 16;
23 // TODO: use variable spacing
24 static const uint32 kHorizontalSpace
= 8;
25 static const uint32 kVerticalSpace
= 4;
26 static const uint32 kPositionLength
= 4;
28 static const uint32 kBlockSpace
= 3;
29 static const uint32 kHexByteWidth
= 3;
30 // these are determined by the implementation of DataView::ConvertLine()
33 /*! This function checks if the buffer contains a valid UTF-8
34 string, following the convention from the DataView::ConvertLine()
35 method: everything that's not replaced by a '.' will be accepted.
38 is_valid_utf8(uint8
*data
, size_t size
)
40 for (size_t i
= 0; i
< size
; i
++) {
41 // accept a terminating null byte
42 if (i
== size
- 1 && data
[i
] == '\0')
45 if ((data
[i
] & 0x80) == 0) {
46 // a single byte character
48 && data
[i
] != 0x0d && data
[i
] != 0x09 && data
[i
] != 0x0a)
55 if ((data
[i
] & 0xc0) == 0x80) {
56 // not a proper multibyte start
60 // start of a multibyte character
62 uint32 result
= (uint32
)(data
[i
++] & 0xff);
64 while (result
& mask
) {
66 // seven byte char - invalid
74 while (i
< size
&& (data
[i
] & 0xc0) == 0x80) {
76 result
+= data
[i
++] & 0x3f;
84 // not enough bytes in multibyte char
96 DataView::DataView(DataEditor
&editor
)
97 : BView("dataView", B_WILL_DRAW
| B_NAVIGABLE
| B_FRAME_EVENTS
),
102 fMouseSelectionStart(-1),
103 fKeySelectionStart(-1),
110 if (fEditor
.Lock()) {
111 fDataSize
= fEditor
.ViewSize();
112 fOffset
= fEditor
.ViewOffset();
116 fData
= (uint8
*)malloc(fDataSize
);
119 // also sets the fixed font
125 DataView::~DataView()
131 DataView::DetachedFromWindow()
133 fEditor
.StopWatching(this);
138 DataView::AttachedToWindow()
140 fEditor
.StartWatching(this);
142 // this seems to be necessary - if we don't do this here,
143 // the view is sometimes focus, but IsFocus() returns false...
145 SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR
);
146 SetLowUIColor(ViewUIColor());
147 SetHighUIColor(B_DOCUMENT_TEXT_COLOR
);
152 DataView::UpdateFromEditor(BMessage
*message
)
157 BAutolock
locker(fEditor
);
159 fFileSize
= fEditor
.FileSize();
161 // get the range of the changes
163 int32 start
= 0, end
= fDataSize
- 1;
166 && message
->FindInt64("offset", &offset
) == B_OK
167 && message
->FindInt64("size", &size
) == B_OK
) {
168 if (offset
> fOffset
+ (off_t
)fDataSize
169 || offset
+ (off_t
)size
< fOffset
) {
170 // the changes are not within our scope, so we can ignore them
174 if (offset
> fOffset
)
175 start
= offset
- fOffset
;
176 if (offset
+ (off_t
)size
< fOffset
+ (off_t
)fDataSize
)
177 end
= offset
+ size
- fOffset
;
180 if (fOffset
+ (off_t
)fDataSize
> fFileSize
)
181 fSizeInView
= fFileSize
- fOffset
;
183 fSizeInView
= fDataSize
;
186 if (fEditor
.GetViewBuffer(&data
) == B_OK
)
187 // ToDo: copy only the relevant part
188 memcpy(fData
, data
, fDataSize
);
190 InvalidateRange(start
, end
);
192 // we notify our selection listeners also if the
193 // data in the selection has changed
194 if (start
<= fEnd
&& end
>= fStart
) {
196 update
.AddInt64("start", fStart
);
197 update
.AddInt64("end", fEnd
);
198 SendNotices(kDataViewSelection
, &update
);
204 DataView::AcceptsDrop(const BMessage
*message
)
206 return !fEditor
.IsReadOnly()
207 && (message
->HasData("text/plain", B_MIME_TYPE
)
208 || message
->HasData(B_FILE_MIME_TYPE
, B_MIME_TYPE
));
213 DataView::MessageReceived(BMessage
*message
)
215 switch (message
->what
) {
217 case kMsgDataEditorUpdate
:
218 UpdateFromEditor(message
);
221 case kMsgDataEditorParameterChange
:
225 if (message
->FindInt64("offset", &offset
) == B_OK
) {
230 if (message
->FindInt32("view_size", &viewSize
) == B_OK
) {
231 fDataSize
= viewSize
;
232 fData
= (uint8
*)realloc(fData
, fDataSize
);
234 SendNotices(kDataViewPreferredSize
);
236 if (message
->FindInt64("file_size", &offset
) == B_OK
)
244 if (message
->FindInt32("base", &type
) != B_OK
)
247 SetBase((base_type
)type
);
251 case kMsgSetSelection
:
254 if (message
->FindInt64("start", &start
) != B_OK
255 || message
->FindInt64("end", &end
) != B_OK
)
258 SetSelection(start
, end
);
263 SetSelection(0, fDataSize
- 1);
283 if (AcceptsDrop(message
)) {
286 if (message
->FindData("text/plain", B_MIME_TYPE
, &data
, &size
) == B_OK
287 || message
->FindData(B_FILE_MIME_TYPE
, B_MIME_TYPE
, &data
, &size
) == B_OK
) {
288 if (fEditor
.Replace(fOffset
+ fStart
, (const uint8
*)data
, size
) != B_OK
)
289 SetSelection(fStoredStart
, fStoredEnd
);
291 fDragMessageSize
= -1;
297 BView::MessageReceived(message
);
305 if (!be_clipboard
->Lock())
308 be_clipboard
->Clear();
311 if ((clip
= be_clipboard
->Data()) != NULL
) {
312 uint8
*data
= fData
+ fStart
;
313 size_t length
= fEnd
+ 1 - fStart
;
315 clip
->AddData(B_FILE_MIME_TYPE
, B_MIME_TYPE
, data
, length
);
317 if (is_valid_utf8(data
, length
))
318 clip
->AddData("text/plain", B_MIME_TYPE
, data
, length
);
320 be_clipboard
->Commit();
323 be_clipboard
->Unlock();
330 if (!be_clipboard
->Lock())
336 if ((clip
= be_clipboard
->Data()) != NULL
337 && (clip
->FindData(B_FILE_MIME_TYPE
, B_MIME_TYPE
, &data
, &length
) == B_OK
338 || clip
->FindData("text/plain", B_MIME_TYPE
, &data
, &length
) == B_OK
)) {
339 // we have valid data, but it could still be too
340 // large to to fit in the file
341 if (fOffset
+ fStart
+ length
> fFileSize
)
342 length
= fFileSize
- fOffset
;
344 if (fEditor
.Replace(fOffset
+ fStart
, (const uint8
*)data
, length
) == B_OK
)
345 SetSelection(fStart
+ length
, fStart
+ length
);
349 be_clipboard
->Unlock();
354 DataView::ConvertLine(char *line
, off_t offset
, const uint8
*buffer
, size_t size
)
361 line
+= sprintf(line
, fBase
== kHexBase
? "%0*" B_PRIxOFF
": " : "%0*"
362 B_PRIdOFF
": ", (int)kPositionLength
, offset
);
364 for (uint32 i
= 0; i
< kBlockSize
; i
++) {
367 line
+= kHexByteWidth
;
369 line
+= sprintf(line
, "%02x ", *(unsigned char *)(buffer
+ i
));
375 for (uint32 i
= 0; i
< kBlockSize
; i
++) {
379 if (c
< ' ' || c
== 0x7f)
392 DataView::Draw(BRect updateRect
)
394 if (fData
== NULL
|| fFileSize
== 0)
397 // ToDo: take "updateRect" into account!
400 BPoint
location(kHorizontalSpace
, kVerticalSpace
+ fAscent
);
402 for (uint32 i
= 0; i
< fSizeInView
; i
+= kBlockSize
) {
403 ConvertLine(line
, i
, fData
+ i
, fSizeInView
- i
);
404 DrawString(line
, location
);
406 location
.y
+= fFontHeight
;
414 DataView::DataBounds(bool inView
) const
417 fCharWidth
* (kBlockSize
* 4 + kPositionLength
+ 6) + 2 * kHorizontalSpace
,
418 fFontHeight
* (((inView
? fSizeInView
: fDataSize
) + kBlockSize
- 1) / kBlockSize
)
419 + 2 * kVerticalSpace
);
424 DataView::PositionAt(view_focus focus
, BPoint point
, view_focus
*_newFocus
)
426 // clip the point into our data bounds
428 BRect bounds
= DataBounds(true);
429 if (point
.x
< bounds
.left
)
430 point
.x
= bounds
.left
;
431 else if (point
.x
> bounds
.right
)
432 point
.x
= bounds
.right
;
434 if (point
.y
< bounds
.top
)
435 point
.y
= bounds
.top
;
436 else if (point
.y
>= bounds
.bottom
- kVerticalSpace
)
437 point
.y
= bounds
.bottom
- kVerticalSpace
- 1;
439 float left
= fCharWidth
* (kPositionLength
+ kBlockSpace
) + kHorizontalSpace
;
440 float hexWidth
= fCharWidth
* kBlockSize
* kHexByteWidth
;
441 float width
= fCharWidth
;
443 if (focus
== kNoFocus
) {
444 // find in which part the point is in
445 if (point
.x
< left
- width
/ 2)
448 if (point
.x
> left
+ hexWidth
)
456 if (focus
== kHexFocus
) {
458 width
*= kHexByteWidth
;
460 left
+= hexWidth
+ (kBlockSpace
* width
);
462 int32 row
= int32((point
.y
- kVerticalSpace
) / fFontHeight
);
463 int32 column
= int32((point
.x
- left
) / width
);
464 if (column
>= (int32
)kBlockSize
)
465 column
= (int32
)kBlockSize
- 1;
469 return row
* kBlockSize
+ column
;
474 DataView::SelectionFrame(view_focus which
, int32 start
, int32 end
)
477 float width
= fCharWidth
;
478 float byteWidth
= fCharWidth
;
481 if (which
== kHexFocus
) {
482 spacing
= fCharWidth
/ 2;
483 left
= width
* (kPositionLength
+ kBlockSpace
);
484 width
*= kHexByteWidth
;
487 left
= width
* (kPositionLength
+ 2 * kBlockSpace
+ kHexByteWidth
* kBlockSize
);
489 left
+= kHorizontalSpace
;
490 float startInLine
= (start
% kBlockSize
) * width
;
491 float endInLine
= (end
% kBlockSize
) * width
+ byteWidth
- 1;
493 return BRect(left
+ startInLine
- spacing
,
494 kVerticalSpace
+ (start
/ kBlockSize
) * fFontHeight
,
495 left
+ endInLine
+ spacing
,
496 kVerticalSpace
+ (end
/ kBlockSize
+ 1) * fFontHeight
- 1);
501 DataView::DrawSelectionFrame(view_focus which
)
506 bool drawBlock
= false;
507 bool drawLastLine
= false;
508 BRect block
, lastLine
;
510 if (which
== kAsciiFocus
)
515 int32 start
= fStart
% kBlockSize
;
516 int32 first
= (fStart
/ kBlockSize
) * kBlockSize
;
519 if (end
> first
+ (int32
)kBlockSize
- 1)
520 end
= first
+ kBlockSize
- 1;
522 BRect firstLine
= SelectionFrame(which
, first
+ start
, end
);
523 firstLine
.right
+= spacing
;
526 // draw block (and last line) if necessary
528 end
= fEnd
% kBlockSize
;
529 int32 last
= (fEnd
/ kBlockSize
) * kBlockSize
;
532 if (end
== kBlockSize
- 1)
535 block
= SelectionFrame(which
, first
, last
- 1);
536 block
.right
+= spacing
;
539 if (end
!= kBlockSize
- 1) {
540 lastLine
= SelectionFrame(which
, last
, last
+ end
);
541 lastLine
.right
+= spacing
;
546 SetDrawingMode(B_OP_INVERT
);
553 const rgb_color color
= {0, 0, 0};
556 bottom
= block
.bottom
;
558 bottom
= firstLine
.bottom
;
560 AddLine(BPoint(firstLine
.left
+ 1, firstLine
.top
), firstLine
.RightTop(), color
);
561 AddLine(BPoint(firstLine
.right
, firstLine
.top
+ 1), BPoint(firstLine
.right
, bottom
), color
);
568 if (start
== 0 || (!drawBlock
&& !drawLastLine
))
576 rect
.bottom
= block
.bottom
;
578 rect
.bottom
= lastLine
.bottom
;
579 rect
.right
= lastLine
.right
;
583 AddLine(rect
.LeftTop(), rect
.LeftBottom(), color
);
584 AddLine(BPoint(rect
.left
+ 1, rect
.bottom
), rect
.RightBottom(), color
);
591 if (start
&& (drawLastLine
|| drawBlock
)) {
592 AddLine(firstLine
.LeftTop(), firstLine
.LeftBottom(), color
);
594 float right
= firstLine
.left
;
595 if (!drawBlock
&& right
> lastLine
.right
)
596 right
= lastLine
.right
;
597 AddLine(BPoint(rect
.left
+ 1, rect
.top
), BPoint(right
, rect
.top
), color
);
606 AddLine(lastLine
.RightBottom(), BPoint(lastLine
.right
, lastLine
.top
+ 1), color
);
607 if (!drawBlock
&& lastLine
.right
<= firstLine
.left
)
608 lastLine
.right
= firstLine
.left
+ (lastLine
.right
< firstLine
.left
? 0 : 1);
609 AddLine(BPoint(lastLine
.right
, lastLine
.top
), BPoint(firstLine
.right
, lastLine
.top
), color
);
613 SetDrawingMode(B_OP_COPY
);
618 DataView::DrawSelectionBlock(view_focus which
, int32 blockStart
, int32 blockEnd
)
625 SetDrawingMode(B_OP_INVERT
);
627 int32 start
= blockStart
% kBlockSize
;
628 int32 first
= (blockStart
/ kBlockSize
) * kBlockSize
;
630 int32 end
= blockEnd
;
631 if (end
> first
+ (int32
)kBlockSize
- 1)
632 end
= first
+ kBlockSize
- 1;
634 FillRect(SelectionFrame(which
, first
+ start
, end
));
637 // draw block (and last line) if necessary
639 end
= blockEnd
% kBlockSize
;
640 int32 last
= (blockEnd
/ kBlockSize
) * kBlockSize
;
643 if (end
== kBlockSize
- 1)
647 FillRect(SelectionFrame(which
, first
, last
- 1));
648 if (end
!= kBlockSize
- 1)
649 FillRect(SelectionFrame(which
, last
, last
+ end
));
652 SetDrawingMode(B_OP_COPY
);
657 DataView::DrawSelectionBlock(view_focus which
)
659 DrawSelectionBlock(which
, fStart
, fEnd
);
664 DataView::DrawSelection(bool frameOnly
)
666 if (IsFocus() && fIsActive
) {
668 DrawSelectionBlock(fFocus
);
669 DrawSelectionFrame(fFocus
== kHexFocus
? kAsciiFocus
: kHexFocus
);
671 DrawSelectionFrame(kHexFocus
);
672 DrawSelectionFrame(kAsciiFocus
);
678 DataView::SetSelection(int32 start
, int32 end
, view_focus focus
)
680 // correct the values if necessary
688 if (start
> (int32
)fSizeInView
- 1)
689 start
= (int32
)fSizeInView
- 1;
693 if (end
> (int32
)fSizeInView
- 1)
694 end
= (int32
)fSizeInView
- 1;
698 if (fStart
== start
&& fEnd
== end
) {
699 // nothing has changed, no need to update
703 // notify our listeners
704 if (fStart
!= start
) {
706 update
.AddInt64("position", start
);
707 SendNotices(kDataViewCursorPosition
, &update
);
711 update
.AddInt64("start", start
);
712 update
.AddInt64("end", end
);
713 SendNotices(kDataViewSelection
, &update
);
715 // Update selection - first, we need to remove the old selection, then
716 // we redraw the selection with the current values.
718 DrawSelection(focus
== kNoFocus
);
719 // From the block selection, only the parts that need updating are
720 // actually updated, if there is no focus change.
722 if (IsFocus() && fIsActive
&& focus
== kNoFocus
) {
723 // Update the selection block incrementally
725 if (start
> fStart
) {
726 // remove from the top
727 DrawSelectionBlock(fFocus
, fStart
, start
- 1);
728 } else if (start
< fStart
) {
730 DrawSelectionBlock(fFocus
, start
, fStart
- 1);
734 // remove from bottom
735 DrawSelectionBlock(fFocus
, end
+ 1, fEnd
);
736 } else if (end
> fEnd
) {
738 DrawSelectionBlock(fFocus
, fEnd
+ 1, end
);
742 if (focus
!= kNoFocus
)
747 DrawSelection(focus
== kNoFocus
);
754 DataView::GetSelection(int32
&start
, int32
&end
)
762 DataView::InvalidateRange(int32 start
, int32 end
)
764 if (start
<= 0 && end
>= int32(fDataSize
) - 1) {
769 int32 startLine
= start
/ kBlockSize
;
770 int32 endLine
= end
/ kBlockSize
;
772 if (endLine
> startLine
) {
773 start
= startLine
* kBlockSize
;
774 end
= (endLine
+ 1) * kBlockSize
- 1;
777 // the part with focus
778 BRect rect
= SelectionFrame(fFocus
, start
, end
);
783 // the part without focus
784 rect
= SelectionFrame(fFocus
== kHexFocus
? kAsciiFocus
: kHexFocus
, start
, end
);
792 DataView::MakeVisible(int32 position
)
794 if (position
< 0 || position
> int32(fDataSize
) - 1)
797 BRect frame
= SelectionFrame(fFocus
, position
, position
);
798 BRect bounds
= Bounds();
799 if (bounds
.Contains(frame
))
802 // special case the first and the last line and column, so that
803 // we can take kHorizontalSpace & kVerticalSpace into account
805 if ((position
% kBlockSize
) == 0)
806 frame
.left
-= kHorizontalSpace
;
807 else if ((position
% kBlockSize
) == kBlockSize
- 1)
808 frame
.right
+= kHorizontalSpace
;
810 if (position
< int32(kBlockSize
))
811 frame
.top
-= kVerticalSpace
;
812 else if (position
> int32(fDataSize
- kBlockSize
))
813 frame
.bottom
+= kVerticalSpace
;
815 // compute the scroll point
817 BPoint point
= bounds
.LeftTop();
818 if (bounds
.left
> frame
.left
)
819 point
.x
= frame
.left
;
820 else if (bounds
.right
< frame
.right
)
821 point
.x
= frame
.right
- bounds
.Width();
823 if (bounds
.top
> frame
.top
)
825 else if (bounds
.bottom
< frame
.bottom
)
826 point
.y
= frame
.bottom
- bounds
.Height();
833 DataView::DataAt(int32 start
)
835 if (start
< 0 || start
>= int32(fSizeInView
) || fData
== NULL
)
838 return fData
+ start
;
843 DataView::WidthForFontSize(float size
)
845 BFont font
= be_fixed_font
;
848 float charWidth
= font
.StringWidth("w");
849 return (int32
)ceilf(charWidth
* (kBlockSize
* 4 + kPositionLength
+ 6)
850 + 2 * kHorizontalSpace
);
855 DataView::SetBase(base_type type
)
866 DataView::SetFocus(view_focus which
)
878 DataView::SetActive(bool active
)
880 if (active
== fIsActive
)
885 // only redraw the focussed part
887 if (IsFocus() && active
) {
888 DrawSelectionFrame(fFocus
);
889 DrawSelectionBlock(fFocus
);
891 DrawSelectionBlock(fFocus
);
892 DrawSelectionFrame(fFocus
);
898 DataView::WindowActivated(bool active
)
900 BView::WindowActivated(active
);
906 DataView::MakeFocus(bool focus
)
908 bool previous
= IsFocus();
909 BView::MakeFocus(focus
);
911 if (focus
== previous
)
914 if (Window()->IsActive() && focus
)
916 else if (!Window()->IsActive() || !focus
)
922 DataView::UpdateScroller()
925 GetPreferredSize(&width
, &height
);
927 SetExplicitMinSize(BSize(250, 200));
928 SetExplicitMaxSize(BSize(B_SIZE_UNSET
, height
));
929 SetExplicitPreferredSize(BSize(width
, height
));
932 if ((bar
= ScrollBar(B_HORIZONTAL
)) != NULL
) {
933 float delta
= width
- Bounds().Width();
937 bar
->SetRange(0, delta
);
938 bar
->SetSteps(fCharWidth
, Bounds().Width());
939 bar
->SetProportion(Bounds().Width() / width
);
941 if ((bar
= ScrollBar(B_VERTICAL
)) != NULL
) {
942 float delta
= height
- Bounds().Height();
946 bar
->SetRange(0, delta
);
947 bar
->SetSteps(fFontHeight
, Bounds().Height());
948 bar
->SetProportion(Bounds().Height() / height
);
954 DataView::FrameResized(float width
, float height
)
957 // adapt the font size to fit in the view's bounds
958 float oldSize
= FontSize();
962 for (size
= 1.f
; size
< 100; size
+= steps
) {
963 int32 preferredWidth
= WidthForFontSize(size
);
964 if (preferredWidth
> width
)
972 if (oldSize
!= size
) {
973 BFont font
= be_fixed_font
;
986 DataView::InitiateDrag(view_focus focus
)
988 BMessage
*drag
= new BMessage(B_MIME_DATA
);
990 // Add originator and action
991 drag
->AddPointer("be:originator", this);
992 //drag->AddString("be:clip_name", "Byte Clipping");
993 //drag->AddInt32("be_actions", B_TRASH_TARGET);
995 // Add data (just like in Copy())
996 uint8
*data
= fData
+ fStart
;
997 size_t length
= fEnd
+ 1 - fStart
;
999 drag
->AddData(B_FILE_MIME_TYPE
, B_MIME_TYPE
, data
, length
);
1000 if (is_valid_utf8(data
, length
))
1001 drag
->AddData("text/plain", B_MIME_TYPE
, data
, length
);
1003 // get a frame that contains the whole selection - SelectionFrame()
1004 // only spans a rectangle between the start and the end point, so
1005 // we have to pass it the correct input values
1008 const int32 width
= kBlockSize
- 1;
1009 int32 first
= fStart
& ~width
;
1010 int32 last
= ((fEnd
+ width
) & ~width
) - 1;
1011 if (first
== (last
& ~width
))
1012 frame
= SelectionFrame(focus
, fStart
, fEnd
);
1014 frame
= SelectionFrame(focus
, first
, last
);
1016 BRect bounds
= Bounds();
1017 if (!bounds
.Contains(frame
))
1018 frame
= bounds
& frame
;
1020 DragMessage(drag
, frame
, NULL
);
1022 fStoredStart
= fStart
;
1024 fDragMessageSize
= length
;
1029 DataView::MouseDown(BPoint where
)
1033 BMessage
*message
= Looper()->CurrentMessage();
1035 if (message
== NULL
|| message
->FindInt32("buttons", &buttons
) != B_OK
)
1038 view_focus newFocus
;
1039 int32 position
= PositionAt(kNoFocus
, where
, &newFocus
);
1041 if ((buttons
& B_SECONDARY_MOUSE_BUTTON
) != 0
1042 && position
>= fStart
&& position
<= fEnd
) {
1043 InitiateDrag(newFocus
);
1047 if ((buttons
& B_PRIMARY_MOUSE_BUTTON
) == 0)
1050 int32 modifiers
= message
->FindInt32("modifiers");
1052 fMouseSelectionStart
= position
;
1053 if (fMouseSelectionStart
== -1) {
1054 // "where" is outside the valid frame
1058 int32 selectionEnd
= fMouseSelectionStart
;
1059 if (modifiers
& B_SHIFT_KEY
) {
1060 // enlarge the current selection
1061 if (fStart
< selectionEnd
)
1062 fMouseSelectionStart
= fStart
;
1063 else if (fEnd
> selectionEnd
)
1064 fMouseSelectionStart
= fEnd
;
1066 SetSelection(fMouseSelectionStart
, selectionEnd
, newFocus
);
1068 SetMouseEventMask(B_POINTER_EVENTS
,
1069 B_NO_POINTER_HISTORY
| B_SUSPEND_VIEW_FOCUS
| B_LOCK_WINDOW_FOCUS
);
1074 DataView::MouseMoved(BPoint where
, uint32 transit
, const BMessage
*dragMessage
)
1076 if (transit
== B_EXITED_VIEW
&& fDragMessageSize
> 0) {
1077 SetSelection(fStoredStart
, fStoredEnd
);
1078 fDragMessageSize
= -1;
1081 if (dragMessage
&& AcceptsDrop(dragMessage
)) {
1082 // handle drag message and tracking
1084 if (transit
== B_ENTERED_VIEW
) {
1085 fStoredStart
= fStart
;
1090 if (dragMessage
->FindData("text/plain", B_MIME_TYPE
, &data
, &size
) == B_OK
1091 || dragMessage
->FindData("text/plain", B_MIME_TYPE
, &data
, &size
) == B_OK
)
1092 fDragMessageSize
= size
;
1093 } else if (fDragMessageSize
> 0) {
1094 view_focus newFocus
;
1095 int32 start
= PositionAt(kNoFocus
, where
, &newFocus
);
1096 int32 end
= start
+ fDragMessageSize
- 1;
1098 SetSelection(start
, end
);
1104 if (fMouseSelectionStart
== -1)
1107 int32 end
= PositionAt(fFocus
, where
);
1111 SetSelection(fMouseSelectionStart
, end
);
1117 DataView::MouseUp(BPoint where
)
1119 fMouseSelectionStart
= fKeySelectionStart
= -1;
1124 DataView::KeyDown(const char *bytes
, int32 numBytes
)
1127 if (Looper()->CurrentMessage() == NULL
1128 || Looper()->CurrentMessage()->FindInt32("modifiers", &modifiers
) != B_OK
)
1129 modifiers
= ::modifiers();
1131 // check if the selection is going to be changed
1137 if (modifiers
& B_SHIFT_KEY
) {
1138 if (fKeySelectionStart
== -1)
1139 fKeySelectionStart
= fStart
;
1141 fKeySelectionStart
= -1;
1148 int32 position
= fStart
- 1;
1150 if (modifiers
& B_SHIFT_KEY
) {
1151 if (fKeySelectionStart
== fEnd
)
1152 SetSelection(fStart
- 1, fEnd
);
1154 SetSelection(fStart
, fEnd
- 1);
1158 SetSelection(fStart
- 1, fStart
- 1);
1160 MakeVisible(position
);
1165 int32 position
= fEnd
+ 1;
1167 if (modifiers
& B_SHIFT_KEY
) {
1168 if (fKeySelectionStart
== fStart
)
1169 SetSelection(fStart
, fEnd
+ 1);
1171 SetSelection(fStart
+ 1, fEnd
);
1173 SetSelection(fEnd
+ 1, fEnd
+ 1);
1175 MakeVisible(position
);
1181 if (modifiers
& B_SHIFT_KEY
) {
1182 if (fKeySelectionStart
== fStart
) {
1183 start
= fEnd
- int32(kBlockSize
);
1186 start
= fStart
- int32(kBlockSize
);
1192 start
= fStart
- int32(kBlockSize
);
1199 SetSelection(start
, end
);
1206 if (modifiers
& B_SHIFT_KEY
) {
1207 if (fKeySelectionStart
== fEnd
) {
1209 end
= fStart
+ int32(kBlockSize
);
1212 end
= fEnd
+ int32(kBlockSize
);
1214 if (end
>= int32(fSizeInView
))
1215 end
= int32(fSizeInView
) - 1;
1217 end
= fEnd
+ int32(kBlockSize
);
1218 if (end
>= int32(fSizeInView
))
1224 SetSelection(start
, end
);
1231 // scroll one page up, but keep the same cursor column
1233 BRect frame
= SelectionFrame(fFocus
, fStart
, fStart
);
1234 frame
.OffsetBy(0, -Bounds().Height());
1235 if (frame
.top
<= kVerticalSpace
)
1236 frame
.top
= kVerticalSpace
+ 1;
1237 ScrollBy(0, -Bounds().Height());
1239 int32 position
= PositionAt(fFocus
, frame
.LeftTop());
1240 SetSelection(position
, position
);
1245 // scroll one page down, but keep the same cursor column
1247 BRect frame
= SelectionFrame(fFocus
, fStart
, fStart
);
1248 frame
.OffsetBy(0, Bounds().Height());
1250 float lastLine
= DataBounds().Height() - 1 - kVerticalSpace
;
1251 if (frame
.top
> lastLine
)
1252 frame
.top
= lastLine
;
1253 ScrollBy(0, Bounds().Height());
1255 int32 position
= PositionAt(fFocus
, frame
.LeftTop());
1256 SetSelection(position
, position
);
1261 MakeVisible(fStart
);
1264 SetSelection(fDataSize
- 1, fDataSize
- 1);
1265 MakeVisible(fStart
);
1268 SetFocus(fFocus
== kHexFocus
? kAsciiFocus
: kHexFocus
);
1269 MakeVisible(fStart
);
1272 case B_FUNCTION_KEY
:
1277 if (fBitPosition
== 0)
1278 SetSelection(fStart
- 1, fStart
- 1);
1280 if (fFocus
== kHexFocus
)
1281 fBitPosition
= (fBitPosition
+ 4) % 8;
1283 // supposed to fall through
1285 SetSelection(fStart
, fStart
);
1286 // to make sure only the cursor is selected
1288 if (fFocus
== kHexFocus
) {
1289 const uint8
*data
= DataAt(fStart
);
1293 uint8 c
= data
[0] & (fBitPosition
== 0 ? 0x0f : 0xf0);
1294 // mask out region to be cleared
1296 fEditor
.Replace(fOffset
+ fStart
, &c
, 1);
1298 fEditor
.Replace(fOffset
+ fStart
, (const uint8
*)"", 1);
1302 if (fFocus
== kHexFocus
) {
1303 // only hexadecimal characters are allowed to be entered
1304 const uint8
*data
= DataAt(fStart
);
1306 if (c
>= 'A' && c
<= 'F')
1308 const char *hexNumbers
= "0123456789abcdef";
1310 if (data
== NULL
|| (number
= (addr_t
)strchr(hexNumbers
, c
)) == 0)
1313 SetSelection(fStart
, fStart
);
1314 // to make sure only the cursor is selected
1316 number
-= (addr_t
)hexNumbers
;
1317 fBitPosition
= (fBitPosition
+ 4) % 8;
1319 c
= (data
[0] & (fBitPosition
? 0x0f : 0xf0)) | (number
<< fBitPosition
);
1320 // mask out overwritten region and bit-wise or the number to be inserted
1322 if (fEditor
.Replace(fOffset
+ fStart
, &c
, 1) == B_OK
&& fBitPosition
== 0)
1323 SetSelection(fStart
+ 1, fStart
+ 1);
1325 if (fEditor
.Replace(fOffset
+ fStart
, (const uint8
*)bytes
, numBytes
) == B_OK
)
1326 SetSelection(fStart
+ 1, fStart
+ 1);
1334 DataView::SetFont(const BFont
*font
, uint32 properties
)
1336 if (!font
->IsFixed())
1339 BView::SetFont(font
, properties
);
1341 font_height fontHeight
;
1342 font
->GetHeight(&fontHeight
);
1344 fFontHeight
= int32(fontHeight
.ascent
+ fontHeight
.descent
+ fontHeight
.leading
);
1345 fAscent
= fontHeight
.ascent
;
1346 fCharWidth
= font
->StringWidth("w");
1351 DataView::FontSize() const
1361 DataView::SetFontSize(float point
)
1363 bool fit
= (point
== 0.0f
);
1366 SendNotices(kDataViewPreferredSize
);
1369 FrameResized(Bounds().Width(), Bounds().Height());
1373 fFitFontSize
= false;
1375 BFont font
= be_fixed_font
;
1376 font
.SetSize(point
);
1382 SendNotices(kDataViewPreferredSize
);
1387 DataView::GetPreferredSize(float *_width
, float *_height
)
1389 BRect bounds
= DataBounds();
1392 *_width
= bounds
.Width();
1395 *_height
= bounds
.Height();