From beedda4c2c643e23c97dcb6917662d57df6a9b0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Tue, 31 Mar 2009 14:42:06 +0000 Subject: [PATCH] * 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 --- src/apps/charactermap/CharacterView.cpp | 262 +++++++++++++++++----- src/apps/charactermap/CharacterView.h | 20 +- src/apps/charactermap/CharacterWindow.cpp | 28 ++- src/apps/charactermap/CharacterWindow.h | 2 + 4 files changed, 256 insertions(+), 56 deletions(-) diff --git a/src/apps/charactermap/CharacterView.cpp b/src/apps/charactermap/CharacterView.cpp index b72136dc9c..5c1bc08d0f 100644 --- a/src/apps/charactermap/CharacterView.cpp +++ b/src/apps/charactermap/CharacterView.cpp @@ -8,41 +8,18 @@ #include +#include #include #include #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; } diff --git a/src/apps/charactermap/CharacterView.h b/src/apps/charactermap/CharacterView.h index 487b0e7e65..af6693e536 100644 --- a/src/apps/charactermap/CharacterView.h +++ b/src/apps/charactermap/CharacterView.h @@ -6,6 +6,7 @@ #define CHARACTER_VIEW_H +#include #include @@ -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; diff --git a/src/apps/charactermap/CharacterWindow.cpp b/src/apps/charactermap/CharacterWindow.cpp index 84740211c4..fe7ad87852 100644 --- a/src/apps/charactermap/CharacterWindow.cpp +++ b/src/apps/charactermap/CharacterWindow.cpp @@ -21,12 +21,14 @@ #include #include #include +#include #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; diff --git a/src/apps/charactermap/CharacterWindow.h b/src/apps/charactermap/CharacterWindow.h index e8e724fea4..a7a498b512 100644 --- a/src/apps/charactermap/CharacterWindow.h +++ b/src/apps/charactermap/CharacterWindow.h @@ -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