* Added a string view that shows the character's code on mouse over, the

character gets also highlighted.
* Added support for dragging a character. Holding shift (or option) while
  dragging will copy an UTF-8 hex string instead.
* Minor cleanup.
* Is there any reason BStringView restricts its maximum width?


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@29825 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2009-03-31 14:42:06 +00:00
parent 751c91b7e1
commit beedda4c2c
4 changed files with 256 additions and 56 deletions
+208 -54
View File
@@ -8,41 +8,18 @@
#include <stdio.h> #include <stdio.h>
#include <Bitmap.h>
#include <LayoutUtils.h> #include <LayoutUtils.h>
#include <ScrollBar.h> #include <ScrollBar.h>
#include "UnicodeBlocks.h" #include "UnicodeBlocks.h"
void
unicode_to_utf8(uint32 c, char** out)
{
char* s = *out;
if (c < 0x80)
*(s++) = c;
else if (c < 0x800) {
*(s++) = 0xc0 | (c >> 6);
*(s++) = 0x80 | (c & 0x3f);
} else if (c < 0x10000) {
*(s++) = 0xe0 | (c >> 12);
*(s++) = 0x80 | ((c >> 6) & 0x3f);
*(s++) = 0x80 | (c & 0x3f);
} else if (c <= 0x10ffff) {
*(s++) = 0xf0 | (c >> 18);
*(s++) = 0x80 | ((c >> 12) & 0x3f);
*(s++) = 0x80 | ((c >> 6) & 0x3f);
*(s++) = 0x80 | (c & 0x3f);
}
*out = s;
}
// #pragma mark -
CharacterView::CharacterView(const char* name) CharacterView::CharacterView(const char* name)
: BView(name, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS), : BView(name, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS),
fTargetCommand(0),
fClickPoint(-1, 0),
fHasCharacter(false),
fShowPrivateBlocks(false), fShowPrivateBlocks(false),
fShowContainedBlocksOnly(false) fShowContainedBlocksOnly(false)
{ {
@@ -57,6 +34,14 @@ CharacterView::~CharacterView()
} }
void
CharacterView::SetTarget(BMessenger target, uint32 command)
{
fTarget = target;
fTargetCommand = command;
}
void void
CharacterView::SetCharacterFont(const BFont& font) CharacterView::SetCharacterFont(const BFont& font)
{ {
@@ -91,6 +76,9 @@ CharacterView::ShowContainedBlocksOnly(bool show)
bool bool
CharacterView::IsShowingBlock(int32 blockIndex) const CharacterView::IsShowingBlock(int32 blockIndex) const
{ {
if (blockIndex < 0 || blockIndex >= (int32)kNumUnicodeBlocks)
return false;
if (!fShowPrivateBlocks && kUnicodeBlocks[blockIndex].private_block) if (!fShowPrivateBlocks && kUnicodeBlocks[blockIndex].private_block)
return false; return false;
@@ -116,6 +104,51 @@ CharacterView::ScrollTo(int32 blockIndex)
} }
/*static*/ void
CharacterView::UnicodeToUTF8(uint32 c, char* text, size_t textSize)
{
if (textSize < 5) {
if (textSize > 0)
text[0] = '\0';
return;
}
char* s = text;
if (c < 0x80)
*(s++) = c;
else if (c < 0x800) {
*(s++) = 0xc0 | (c >> 6);
*(s++) = 0x80 | (c & 0x3f);
} else if (c < 0x10000) {
*(s++) = 0xe0 | (c >> 12);
*(s++) = 0x80 | ((c >> 6) & 0x3f);
*(s++) = 0x80 | (c & 0x3f);
} else if (c <= 0x10ffff) {
*(s++) = 0xf0 | (c >> 18);
*(s++) = 0x80 | ((c >> 12) & 0x3f);
*(s++) = 0x80 | ((c >> 6) & 0x3f);
*(s++) = 0x80 | (c & 0x3f);
}
s[0] = '\0';
}
/*static*/ void
CharacterView::UnicodeToUTF8Hex(uint32 c, char* text, size_t textSize)
{
char character[16];
CharacterView::UnicodeToUTF8(c, character, sizeof(character));
int size = 0;
for (int32 i = 0; character[i] && size < (int)textSize; i++) {
size += snprintf(text + size, textSize - size, "\\x%02x",
(uint8)character[i]);
}
}
void void
CharacterView::AttachedToWindow() CharacterView::AttachedToWindow()
{ {
@@ -139,7 +172,6 @@ CharacterView::MinSize()
void void
CharacterView::FrameResized(float width, float height) CharacterView::FrameResized(float width, float height)
{ {
puts("hey");
_UpdateSize(); _UpdateSize();
} }
@@ -147,12 +179,14 @@ CharacterView::FrameResized(float width, float height)
void void
CharacterView::MouseDown(BPoint where) CharacterView::MouseDown(BPoint where)
{ {
fClickPoint = where;
} }
void void
CharacterView::MouseUp(BPoint where) CharacterView::MouseUp(BPoint where)
{ {
fClickPoint.x = -1;
} }
@@ -160,6 +194,65 @@ void
CharacterView::MouseMoved(BPoint where, uint32 transit, CharacterView::MouseMoved(BPoint where, uint32 transit,
const BMessage* dragMessage) const BMessage* dragMessage)
{ {
if (dragMessage != NULL)
return;
BRect frame;
uint32 character;
bool hasCharacter = _GetCharacterAt(where, character, &frame);
if (fHasCharacter && (character != fCurrentCharacter || !hasCharacter))
Invalidate(fCurrentCharacterFrame);
if (hasCharacter && (character != fCurrentCharacter || !fHasCharacter)) {
BMessage update(fTargetCommand);
update.AddInt32("character", character);
fTarget.SendMessage(&update);
Invalidate(frame);
}
fHasCharacter = hasCharacter;
fCurrentCharacter = character;
fCurrentCharacterFrame = frame;
if (fClickPoint.x >= 0 && (fabs(where.x - fClickPoint.x) > 4
|| fabs(where.y - fClickPoint.y) > 4)) {
// start dragging
BPoint offset = fClickPoint - frame.LeftTop();
frame.OffsetTo(B_ORIGIN);
BBitmap* bitmap = new BBitmap(frame, B_BITMAP_ACCEPTS_VIEWS, B_RGBA32);
bitmap->Lock();
BView* view = new BView(frame, "drag", 0, 0);
bitmap->AddChild(view);
view->SetLowColor(B_TRANSPARENT_COLOR);
view->FillRect(frame, B_SOLID_LOW);
// Draw character
char text[16];
UnicodeToUTF8(character, text, sizeof(text));
view->SetFont(&fCharacterFont);
view->DrawString(text, BPoint((fCharacterWidth - StringWidth(text)) / 2,
fCharacterBase));
view->Sync();
bitmap->RemoveChild(view);
bitmap->Unlock();
BMessage drag(B_MIME_DATA);
if ((modifiers() & (B_SHIFT_KEY | B_OPTION_KEY)) != 0) {
// paste UTF-8 hex string
CharacterView::UnicodeToUTF8Hex(character, text, sizeof(text));
}
drag.AddData("text/plain", B_MIME_DATA, text, strlen(text));
DragMessage(&drag, bitmap, B_OP_ALPHA, offset);
fClickPoint.x = -1;
}
} }
@@ -173,44 +266,43 @@ CharacterView::MessageReceived(BMessage* message)
void void
CharacterView::Draw(BRect updateRect) CharacterView::Draw(BRect updateRect)
{ {
const int32 kStartX = fGap / 2; const int32 kXGap = fGap / 2;
BFont font; BFont font;
GetFont(&font); GetFont(&font);
int32 y = 2;
// TODO: jump to the correct start for (int32 i = _BlockAt(updateRect.LeftTop()); i < (int32)kNumUnicodeBlocks;
for (uint32 i = 0; i < kNumUnicodeBlocks; i++) { i++) {
if (!IsShowingBlock(i)) if (!IsShowingBlock(i))
continue; continue;
if (fTitleTops[i] > updateRect.bottom)
int32 y = fTitleTops[i];
if (y > updateRect.bottom)
break; break;
SetHighColor(0, 0, 0); SetHighColor(0, 0, 0);
DrawString(kUnicodeBlocks[i].name, BPoint(3, y + fTitleBase)); DrawString(kUnicodeBlocks[i].name, BPoint(3, y + fTitleBase));
y += fTitleHeight; y += fTitleHeight;
int32 x = kStartX; int32 x = kXGap;
SetFont(&fCharacterFont); SetFont(&fCharacterFont);
char character[16];
for (uint32 c = kUnicodeBlocks[i].start; c <= kUnicodeBlocks[i].end; for (uint32 c = kUnicodeBlocks[i].start; c <= kUnicodeBlocks[i].end;
c++) { c++) {
if (y + fCharacterHeight > updateRect.top if (y + fCharacterHeight > updateRect.top
&& y < updateRect.bottom) { && y < updateRect.bottom) {
#if 0 // Stroke frame around the active character
// Stroke frame around the charater if (fHasCharacter && fCurrentCharacter == c) {
SetHighColor(tint_color(ViewColor(), B_DARKEN_1_TINT)); SetHighColor(tint_color(ViewColor(), 1.05f));
StrokeRect(BRect(x, y, x + fCharacterWidth, FillRect(BRect(x, y, x + fCharacterWidth,
y + fCharacterHeight - fGap)); y + fCharacterHeight - fGap));
SetHighColor(0, 0, 0); SetHighColor(0, 0, 0);
#endif }
// Draw character // Draw character
char* end = character; char character[16];
unicode_to_utf8(c, &end); UnicodeToUTF8(c, character, sizeof(character));
end[0] = '\0';
DrawString(character, DrawString(character,
BPoint(x + (fCharacterWidth - StringWidth(character)) / 2, BPoint(x + (fCharacterWidth - StringWidth(character)) / 2,
@@ -218,13 +310,13 @@ CharacterView::Draw(BRect updateRect)
} }
x += fCharacterWidth + fGap; x += fCharacterWidth + fGap;
if (x + fCharacterWidth + fGap / 2 >= fDataRect.right) { if (x + fCharacterWidth + kXGap >= fDataRect.right) {
y += fCharacterHeight; y += fCharacterHeight;
x = kStartX; x = kXGap;
} }
} }
if (x != kStartX) if (x != kXGap)
y += fCharacterHeight; y += fCharacterHeight;
y += fTitleGap; y += fTitleGap;
@@ -240,6 +332,68 @@ CharacterView::DoLayout()
} }
int32
CharacterView::_BlockAt(BPoint point)
{
// TODO: use binary search
for (uint32 i = 0; i < kNumUnicodeBlocks; i++) {
if (!IsShowingBlock(i))
continue;
if (fTitleTops[i] < point.y
&& (i == kNumUnicodeBlocks - 1 || fTitleTops[i + 1] > point.y))
return i;
}
return -1;
}
bool
CharacterView::_GetCharacterAt(BPoint point, uint32& character, BRect* _frame)
{
int32 i = _BlockAt(point);
if (i == -1)
return false;
int32 y = fTitleTops[i] + fTitleHeight;
if (y > point.y)
return false;
const int32 startX = fGap / 2;
if (startX > point.x)
return false;
int32 endX = startX + fCharactersPerLine * (fCharacterWidth + fGap);
if (endX < point.x)
return false;
for (uint32 c = kUnicodeBlocks[i].start; c <= kUnicodeBlocks[i].end;
c += fCharactersPerLine, y += fCharacterHeight) {
if (y + fCharacterHeight <= point.y)
continue;
int32 pos = (int32)((point.x - startX) / (fCharacterWidth + fGap));
if (c + pos > kUnicodeBlocks[i].end)
return false;
// Found character at position
character = c + pos;
if (_frame != NULL) {
_frame->Set(startX + pos * (fCharacterWidth + fGap),
y, startX + (pos + 1) * (fCharacterWidth + fGap) - 1,
y + fCharacterHeight);
}
return true;
}
return false;
}
void void
CharacterView::_UpdateSize() CharacterView::_UpdateSize()
{ {
@@ -276,9 +430,9 @@ CharacterView::_UpdateSize()
fDataRect.right = bounds.Width(); fDataRect.right = bounds.Width();
fDataRect.bottom = 0; fDataRect.bottom = 0;
int32 charactersPerLine = int32(bounds.Width() / (fGap + fCharacterWidth)); fCharactersPerLine = int32(bounds.Width() / (fGap + fCharacterWidth));
if (charactersPerLine == 0) if (fCharactersPerLine == 0)
charactersPerLine = 1; fCharactersPerLine = 1;
for (uint32 i = 0; i < kNumUnicodeBlocks; i++) { for (uint32 i = 0; i < kNumUnicodeBlocks; i++) {
fTitleTops[i] = (int32)ceilf(fDataRect.bottom); fTitleTops[i] = (int32)ceilf(fDataRect.bottom);
@@ -286,8 +440,8 @@ CharacterView::_UpdateSize()
if (!IsShowingBlock(i)) if (!IsShowingBlock(i))
continue; continue;
int32 lines = (kUnicodeBlocks[i].Count() + charactersPerLine - 1) int32 lines = (kUnicodeBlocks[i].Count() + fCharactersPerLine - 1)
/ charactersPerLine; / fCharactersPerLine;
fDataRect.bottom += lines * fCharacterHeight + fTitleHeight + fTitleGap; fDataRect.bottom += lines * fCharacterHeight + fTitleHeight + fTitleGap;
} }
+19 -1
View File
@@ -6,6 +6,7 @@
#define CHARACTER_VIEW_H #define CHARACTER_VIEW_H
#include <Messenger.h>
#include <View.h> #include <View.h>
@@ -14,6 +15,8 @@ public:
CharacterView(const char* name); CharacterView(const char* name);
virtual ~CharacterView(); virtual ~CharacterView();
void SetTarget(BMessenger target, uint32 command);
void SetCharacterFont(const BFont& font); void SetCharacterFont(const BFont& font);
const BFont& CharacterFont() { return fCharacterFont; } const BFont& CharacterFont() { return fCharacterFont; }
@@ -29,6 +32,11 @@ public:
void ScrollTo(int32 blockIndex); void ScrollTo(int32 blockIndex);
static void UnicodeToUTF8(uint32 c, char* text,
size_t textSize);
static void UnicodeToUTF8Hex(uint32 c, char* text,
size_t textSize);
protected: protected:
virtual void AttachedToWindow(); virtual void AttachedToWindow();
virtual void DetachedFromWindow(); virtual void DetachedFromWindow();
@@ -48,15 +56,25 @@ protected:
virtual void DoLayout(); virtual void DoLayout();
private: private:
bool _IncludeBlock(int32 index); int32 _BlockAt(BPoint point);
bool _GetCharacterAt(BPoint point, uint32& character,
BRect* _frame = NULL);
void _UpdateSize(); void _UpdateSize();
private: private:
BMessenger fTarget;
uint32 fTargetCommand;
BPoint fClickPoint;
bool fHasCharacter;
uint32 fCurrentCharacter;
BRect fCurrentCharacterFrame;
bool fShowPrivateBlocks; bool fShowPrivateBlocks;
bool fShowContainedBlocksOnly; bool fShowContainedBlocksOnly;
BRect fDataRect; BRect fDataRect;
BFont fCharacterFont; BFont fCharacterFont;
int32 fCharactersPerLine;
int32 fCharacterWidth; int32 fCharacterWidth;
int32 fCharacterHeight; int32 fCharacterHeight;
int32 fCharacterBase; int32 fCharacterBase;
+27 -1
View File
@@ -21,12 +21,14 @@
#include <ScrollView.h> #include <ScrollView.h>
#include <Slider.h> #include <Slider.h>
#include <SplitLayoutBuilder.h> #include <SplitLayoutBuilder.h>
#include <StringView.h>
#include "CharacterView.h" #include "CharacterView.h"
#include "UnicodeBlocks.h" #include "UnicodeBlocks.h"
static const uint32 kMsgUnicodeBlockSelected = 'unbs'; static const uint32 kMsgUnicodeBlockSelected = 'unbs';
static const uint32 kMsgCharacterChanged = 'chch';
static const uint32 kMsgFontSelected = 'fnts'; static const uint32 kMsgFontSelected = 'fnts';
static const uint32 kMsgFontSizeChanged = 'fsch'; static const uint32 kMsgFontSizeChanged = 'fsch';
static const uint32 kMsgPrivateBlocks = 'prbl'; static const uint32 kMsgPrivateBlocks = 'prbl';
@@ -84,6 +86,7 @@ CharacterWindow::CharacterWindow()
fUnicodeBlockView, 0, false, true); fUnicodeBlockView, 0, false, true);
fCharacterView = new CharacterView("characters"); fCharacterView = new CharacterView("characters");
fCharacterView->SetTarget(this, kMsgCharacterChanged);
bool show; bool show;
if (settings.FindBool("show private blocks", &show) == B_OK) if (settings.FindBool("show private blocks", &show) == B_OK)
@@ -118,13 +121,18 @@ CharacterWindow::CharacterWindow()
new BMessage(kMsgFontSizeChanged), kMinFontSize, kMaxFontSize); new BMessage(kMsgFontSizeChanged), kMinFontSize, kMaxFontSize);
fFontSizeSlider->SetValue(fontSize); fFontSizeSlider->SetValue(fontSize);
fCodeView = new BStringView("code", "-");
fCodeView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED,
fCodeView->PreferredSize().Height()));
AddChild(BGroupLayoutBuilder(B_VERTICAL) AddChild(BGroupLayoutBuilder(B_VERTICAL)
.Add(menuBar) .Add(menuBar)
.Add(BGroupLayoutBuilder(B_HORIZONTAL, 10)//BSplitLayoutBuilder() .Add(BGroupLayoutBuilder(B_HORIZONTAL, 10)//BSplitLayoutBuilder()
.Add(unicodeScroller) .Add(unicodeScroller)
.Add(BGroupLayoutBuilder(B_VERTICAL, 10) .Add(BGroupLayoutBuilder(B_VERTICAL, 10)
.Add(characterScroller) .Add(characterScroller)
.Add(fFontSizeSlider)) .Add(fFontSizeSlider)
.Add(fCodeView))
.SetInsets(10, 10, 10, 10))); .SetInsets(10, 10, 10, 10)));
// Add menu // Add menu
@@ -182,6 +190,24 @@ CharacterWindow::MessageReceived(BMessage* message)
break; break;
} }
case kMsgCharacterChanged:
{
uint32 character;
if (message->FindInt32("character", (int32*)&character) != B_OK)
break;
char utf8Hex[32];
CharacterView::UnicodeToUTF8Hex(character, utf8Hex,
sizeof(utf8Hex));
char text[128];
snprintf(text, sizeof(text), "Code: %#lx (%ld), UTF-8: %s",
character, character, utf8Hex);
fCodeView->SetText(text);
break;
}
case kMsgFontSelected: case kMsgFontSelected:
{ {
BMenuItem* item; BMenuItem* item;
+2
View File
@@ -14,6 +14,7 @@ class BListView;
class BMenu; class BMenu;
class BMenuItem; class BMenuItem;
class BSlider; class BSlider;
class BStringView;
class CharacterView; class CharacterView;
@@ -40,6 +41,7 @@ private:
CharacterView* fCharacterView; CharacterView* fCharacterView;
BMenuItem* fSelectedFontItem; BMenuItem* fSelectedFontItem;
BSlider* fFontSizeSlider; BSlider* fFontSizeSlider;
BStringView* fCodeView;
}; };
#endif // CHARACTER_WINDOW_H #endif // CHARACTER_WINDOW_H