* 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

View File

@ -8,41 +8,18 @@
#include <stdio.h>
#include <Bitmap.h>
#include <LayoutUtils.h>
#include <ScrollBar.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)
: BView(name, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS),
fTargetCommand(0),
fClickPoint(-1, 0),
fHasCharacter(false),
fShowPrivateBlocks(false),
fShowContainedBlocksOnly(false)
{
@ -57,6 +34,14 @@ CharacterView::~CharacterView()
}
void
CharacterView::SetTarget(BMessenger target, uint32 command)
{
fTarget = target;
fTargetCommand = command;
}
void
CharacterView::SetCharacterFont(const BFont& font)
{
@ -91,6 +76,9 @@ CharacterView::ShowContainedBlocksOnly(bool show)
bool
CharacterView::IsShowingBlock(int32 blockIndex) const
{
if (blockIndex < 0 || blockIndex >= (int32)kNumUnicodeBlocks)
return false;
if (!fShowPrivateBlocks && kUnicodeBlocks[blockIndex].private_block)
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
CharacterView::AttachedToWindow()
{
@ -139,7 +172,6 @@ CharacterView::MinSize()
void
CharacterView::FrameResized(float width, float height)
{
puts("hey");
_UpdateSize();
}
@ -147,12 +179,14 @@ CharacterView::FrameResized(float width, float height)
void
CharacterView::MouseDown(BPoint where)
{
fClickPoint = where;
}
void
CharacterView::MouseUp(BPoint where)
{
fClickPoint.x = -1;
}
@ -160,6 +194,65 @@ void
CharacterView::MouseMoved(BPoint where, uint32 transit,
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
CharacterView::Draw(BRect updateRect)
{
const int32 kStartX = fGap / 2;
const int32 kXGap = fGap / 2;
BFont font;
GetFont(&font);
int32 y = 2;
// TODO: jump to the correct start
for (uint32 i = 0; i < kNumUnicodeBlocks; i++) {
for (int32 i = _BlockAt(updateRect.LeftTop()); i < (int32)kNumUnicodeBlocks;
i++) {
if (!IsShowingBlock(i))
continue;
if (fTitleTops[i] > updateRect.bottom)
int32 y = fTitleTops[i];
if (y > updateRect.bottom)
break;
SetHighColor(0, 0, 0);
SetHighColor(0, 0, 0);
DrawString(kUnicodeBlocks[i].name, BPoint(3, y + fTitleBase));
y += fTitleHeight;
int32 x = kStartX;
int32 x = kXGap;
SetFont(&fCharacterFont);
char character[16];
for (uint32 c = kUnicodeBlocks[i].start; c <= kUnicodeBlocks[i].end;
c++) {
if (y + fCharacterHeight > updateRect.top
&& y < updateRect.bottom) {
#if 0
// Stroke frame around the charater
SetHighColor(tint_color(ViewColor(), B_DARKEN_1_TINT));
StrokeRect(BRect(x, y, x + fCharacterWidth,
y + fCharacterHeight - fGap));
// Stroke frame around the active character
if (fHasCharacter && fCurrentCharacter == c) {
SetHighColor(tint_color(ViewColor(), 1.05f));
FillRect(BRect(x, y, x + fCharacterWidth,
y + fCharacterHeight - fGap));
SetHighColor(0, 0, 0);
#endif
SetHighColor(0, 0, 0);
}
// Draw character
char* end = character;
unicode_to_utf8(c, &end);
end[0] = '\0';
char character[16];
UnicodeToUTF8(c, character, sizeof(character));
DrawString(character,
BPoint(x + (fCharacterWidth - StringWidth(character)) / 2,
@ -218,13 +310,13 @@ CharacterView::Draw(BRect updateRect)
}
x += fCharacterWidth + fGap;
if (x + fCharacterWidth + fGap / 2 >= fDataRect.right) {
if (x + fCharacterWidth + kXGap >= fDataRect.right) {
y += fCharacterHeight;
x = kStartX;
x = kXGap;
}
}
if (x != kStartX)
if (x != kXGap)
y += fCharacterHeight;
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
CharacterView::_UpdateSize()
{
@ -276,9 +430,9 @@ CharacterView::_UpdateSize()
fDataRect.right = bounds.Width();
fDataRect.bottom = 0;
int32 charactersPerLine = int32(bounds.Width() / (fGap + fCharacterWidth));
if (charactersPerLine == 0)
charactersPerLine = 1;
fCharactersPerLine = int32(bounds.Width() / (fGap + fCharacterWidth));
if (fCharactersPerLine == 0)
fCharactersPerLine = 1;
for (uint32 i = 0; i < kNumUnicodeBlocks; i++) {
fTitleTops[i] = (int32)ceilf(fDataRect.bottom);
@ -286,8 +440,8 @@ CharacterView::_UpdateSize()
if (!IsShowingBlock(i))
continue;
int32 lines = (kUnicodeBlocks[i].Count() + charactersPerLine - 1)
/ charactersPerLine;
int32 lines = (kUnicodeBlocks[i].Count() + fCharactersPerLine - 1)
/ fCharactersPerLine;
fDataRect.bottom += lines * fCharacterHeight + fTitleHeight + fTitleGap;
}

View File

@ -6,6 +6,7 @@
#define CHARACTER_VIEW_H
#include <Messenger.h>
#include <View.h>
@ -14,6 +15,8 @@ public:
CharacterView(const char* name);
virtual ~CharacterView();
void SetTarget(BMessenger target, uint32 command);
void SetCharacterFont(const BFont& font);
const BFont& CharacterFont() { return fCharacterFont; }
@ -29,6 +32,11 @@ public:
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:
virtual void AttachedToWindow();
virtual void DetachedFromWindow();
@ -48,15 +56,25 @@ protected:
virtual void DoLayout();
private:
bool _IncludeBlock(int32 index);
int32 _BlockAt(BPoint point);
bool _GetCharacterAt(BPoint point, uint32& character,
BRect* _frame = NULL);
void _UpdateSize();
private:
BMessenger fTarget;
uint32 fTargetCommand;
BPoint fClickPoint;
bool fHasCharacter;
uint32 fCurrentCharacter;
BRect fCurrentCharacterFrame;
bool fShowPrivateBlocks;
bool fShowContainedBlocksOnly;
BRect fDataRect;
BFont fCharacterFont;
int32 fCharactersPerLine;
int32 fCharacterWidth;
int32 fCharacterHeight;
int32 fCharacterBase;

View File

@ -21,12 +21,14 @@
#include <ScrollView.h>
#include <Slider.h>
#include <SplitLayoutBuilder.h>
#include <StringView.h>
#include "CharacterView.h"
#include "UnicodeBlocks.h"
static const uint32 kMsgUnicodeBlockSelected = 'unbs';
static const uint32 kMsgCharacterChanged = 'chch';
static const uint32 kMsgFontSelected = 'fnts';
static const uint32 kMsgFontSizeChanged = 'fsch';
static const uint32 kMsgPrivateBlocks = 'prbl';
@ -84,6 +86,7 @@ CharacterWindow::CharacterWindow()
fUnicodeBlockView, 0, false, true);
fCharacterView = new CharacterView("characters");
fCharacterView->SetTarget(this, kMsgCharacterChanged);
bool show;
if (settings.FindBool("show private blocks", &show) == B_OK)
@ -118,13 +121,18 @@ CharacterWindow::CharacterWindow()
new BMessage(kMsgFontSizeChanged), kMinFontSize, kMaxFontSize);
fFontSizeSlider->SetValue(fontSize);
fCodeView = new BStringView("code", "-");
fCodeView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED,
fCodeView->PreferredSize().Height()));
AddChild(BGroupLayoutBuilder(B_VERTICAL)
.Add(menuBar)
.Add(BGroupLayoutBuilder(B_HORIZONTAL, 10)//BSplitLayoutBuilder()
.Add(unicodeScroller)
.Add(BGroupLayoutBuilder(B_VERTICAL, 10)
.Add(characterScroller)
.Add(fFontSizeSlider))
.Add(fFontSizeSlider)
.Add(fCodeView))
.SetInsets(10, 10, 10, 10)));
// Add menu
@ -182,6 +190,24 @@ CharacterWindow::MessageReceived(BMessage* message)
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:
{
BMenuItem* item;

View File

@ -14,6 +14,7 @@ class BListView;
class BMenu;
class BMenuItem;
class BSlider;
class BStringView;
class CharacterView;
@ -40,6 +41,7 @@ private:
CharacterView* fCharacterView;
BMenuItem* fSelectedFontItem;
BSlider* fFontSizeSlider;
BStringView* fCodeView;
};
#endif // CHARACTER_WINDOW_H