Files
SingularityViewer/indra/newview/dohexeditor.cpp
2012-01-23 18:30:54 -06:00

1247 lines
31 KiB
C++

/**
* @file dohexeditor.cpp
* @brief DOHexEditor Widget
* @author Day Oh
*
* $LicenseInfo:firstyear=2009&license=WTFPLV2$
*
*/
// <edit>
#include "llviewerprecompiledheaders.h"
#include "linden_common.h"
#include "dohexeditor.h"
#include "llfocusmgr.h"
#include "llkeyboard.h"
#include "llclipboard.h"
#include "lllocalcliprect.h"
#include "llwindow.h" // setCursor
#include "lllocalinventory.h"
static LLRegisterWidget<DOHexEditor> r("hex_editor");
DOHexEditor::DOHexEditor(const std::string& name, const LLRect& rect)
: LLUICtrl(name, rect, TRUE, NULL, NULL),
LLEditMenuHandler(),
mColumns(16),
mCursorPos(0),
mSecondNibble(FALSE),
mSelecting(FALSE),
mHasSelection(FALSE),
mInData(FALSE),
mSelectionStart(0),
mSelectionEnd(0)
{
mGLFont = LLFontGL::getFontMonospace();
mTextRect.setOriginAndSize(
5, // border + padding
1, // border
getRect().getWidth() - SCROLLBAR_SIZE - 6,
getRect().getHeight() - 5);
S32 line_height = llround( mGLFont->getLineHeight() );
S32 page_size = mTextRect.getHeight() / line_height;
// Init the scrollbar
LLRect scroll_rect;
scroll_rect.setOriginAndSize(
getRect().getWidth() - SCROLLBAR_SIZE,
1,
SCROLLBAR_SIZE,
getRect().getHeight() - 1);
S32 lines_in_doc = getLineCount();
mScrollbar = new LLScrollbar( std::string("Scrollbar"), scroll_rect,
LLScrollbar::VERTICAL,
lines_in_doc,
0,
page_size,
NULL, this );
mScrollbar->setFollowsRight();
mScrollbar->setFollowsTop();
mScrollbar->setFollowsBottom();
mScrollbar->setEnabled( TRUE );
mScrollbar->setVisible( TRUE );
mScrollbar->setOnScrollEndCallback(NULL, NULL);
addChild(mScrollbar);
mBorder = new LLViewBorder( std::string("text ed border"),
LLRect(0, getRect().getHeight(), getRect().getWidth(), 0),
LLViewBorder::BEVEL_IN,
LLViewBorder::STYLE_LINE,
1 ); // border width
addChild( mBorder );
changedLength();
mUndoBuffer = new LLUndoBuffer(DOUndoHex::create, 128);
}
DOHexEditor::~DOHexEditor()
{
gFocusMgr.releaseFocusIfNeeded(this);
if(gEditMenuHandler == this)
{
gEditMenuHandler = NULL;
}
}
LLView* DOHexEditor::fromXML(LLXMLNodePtr node, LLView *parent, class LLUICtrlFactory *factory)
{
std::string name("text_editor");
node->getAttributeString("name", name);
LLRect rect;
createRect(node, rect, parent, LLRect());
DOHexEditor* editor = new DOHexEditor(name, rect);
editor->initFromXML(node, parent);
return editor;
}
void DOHexEditor::setValue(const LLSD& value)
{
mValue = value.asBinary();
changedLength();
}
LLSD DOHexEditor::getValue() const
{
return LLSD(mValue);
}
void DOHexEditor::setColumns(U8 columns)
{
//mColumns = columns;
mColumns = llclamp<U8>(llfloor(columns), 8, 64); //clamp this ffs
changedLength();
}
U32 DOHexEditor::getLineCount()
{
U32 lines = mValue.size();
lines /= mColumns;
lines++; // incomplete or extra line at bottom
return lines;
}
void DOHexEditor::getPosAndContext(S32 x, S32 y, BOOL force_context, U32& pos, BOOL& in_data, BOOL& second_nibble)
{
pos = 0;
F32 line_height = mGLFont->getLineHeight();
F32 char_width = mGLFont->getWidthF32(".");
F32 data_column_width = char_width * 3; // " 00";
F32 text_x = mTextRect.mLeft;
F32 text_x_data = text_x + (char_width * 10.1f); // "00000000 ", dunno why it's a fraction off
F32 text_x_ascii = text_x_data + (data_column_width * mColumns) + (char_width * 2);
F32 text_y = (F32)(mTextRect.mTop - line_height);
U32 first_line = mScrollbar->getDocPos();
//U32 last_line = first_line + mScrollbar->getPageSize(); // don't -2 from scrollbar sizes
S32 first_line_y = text_y - line_height;
S32 ly = -(y - first_line_y); // negative vector from first line to y
ly -= 5; // slight skew
S32 line = ly / line_height;
if(line < 0) line = 0;
line += first_line;
if(!force_context)
{
in_data = (x < (text_x_ascii - char_width)); // char width for less annoying
}
S32 lx = x;
S32 offset;
if(in_data)
{
lx -= char_width; // subtracting char width because idk
lx -= text_x_data;
offset = lx / data_column_width;
// Now, which character
S32 rem = S32( (F32)lx - (data_column_width * offset) - (char_width * 0.25) );
if(rem > 0)
{
if(rem > char_width)
{
offset++; // next byte
second_nibble = FALSE;
}
else second_nibble = TRUE;
}
else second_nibble = FALSE;
}
else
{
second_nibble = FALSE;
lx += char_width; // adding char width because idk
lx -= text_x_ascii;
offset = lx / char_width;
}
if(offset < 0) offset = 0;
if(offset >= mColumns)//offset = mColumns - 1;
{
offset = 0;
line++;
second_nibble = FALSE;
}
pos = (line * mColumns) + offset;
if(pos < 0) pos = 0;
if(pos > mValue.size()) pos = mValue.size();
if(pos == mValue.size())
{
second_nibble = FALSE;
}
}
void DOHexEditor::changedLength()
{
S32 line_height = llround( mGLFont->getLineHeight() );
S32 page_size = mTextRect.getHeight() / line_height;
page_size -= 2; // don't count the spacer and header
mScrollbar->setDocSize(getLineCount());
mScrollbar->setPageSize(page_size);
moveCursor(mCursorPos, mSecondNibble); // cursor was off end after undo of paste
}
void DOHexEditor::reshape(S32 width, S32 height, BOOL called_from_parent)
{
LLView::reshape( width, height, called_from_parent );
mTextRect.setOriginAndSize(
5, // border + padding
1, // border
getRect().getWidth() - SCROLLBAR_SIZE - 6,
getRect().getHeight() - 5);
LLRect scrollrect;
scrollrect.setOriginAndSize(
getRect().getWidth() - SCROLLBAR_SIZE,
1,
SCROLLBAR_SIZE,
getRect().getHeight() - 1);
mScrollbar->setRect(scrollrect);
mBorder->setRect(LLRect(0, getRect().getHeight(), getRect().getWidth(), 0));
changedLength();
}
void DOHexEditor::setFocus(BOOL b)
{
if(b)
{
LLEditMenuHandler::gEditMenuHandler = this;
}
else
{
mSelecting = FALSE;
gFocusMgr.releaseFocusIfNeeded(this);
if(gEditMenuHandler == this)
{
gEditMenuHandler = NULL;
}
}
LLUICtrl::setFocus(b);
}
F32 DOHexEditor::getSuggestedWidth(U8 cols)
{
cols = cols>1?cols:mColumns;
F32 char_width = mGLFont->getWidthF32(".");
F32 data_column_width = char_width * 3; // " 00";
F32 text_x = mTextRect.mLeft;
F32 text_x_data = text_x + (char_width * 10.1f); // "00000000 ", dunno why it's a fraction off
F32 text_x_ascii = text_x_data + (data_column_width * cols) + (char_width * 2);
F32 suggested_width = text_x_ascii + (char_width * cols);
suggested_width += mScrollbar->getRect().getWidth();
suggested_width += 10.0f;
return suggested_width;
}
U32 DOHexEditor::getProperSelectionStart()
{
return (mSelectionStart < mSelectionEnd) ? mSelectionStart : mSelectionEnd;
}
U32 DOHexEditor::getProperSelectionEnd()
{
return (mSelectionStart < mSelectionEnd) ? mSelectionEnd : mSelectionStart;
}
BOOL DOHexEditor::handleScrollWheel(S32 x, S32 y, S32 clicks)
{
return mScrollbar->handleScrollWheel( 0, 0, clicks );
}
BOOL DOHexEditor::handleMouseDown(S32 x, S32 y, MASK mask)
{
BOOL handled = FALSE;
handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL;
if(!handled)
{
setFocus(TRUE);
gFocusMgr.setMouseCapture(this);
handled = TRUE;
if(!mSelecting)
{
if(mask & MASK_SHIFT)
{
// extend a selection
getPosAndContext(x, y, FALSE, mCursorPos, mInData, mSecondNibble);
mSelectionEnd = mCursorPos;
mHasSelection = (mSelectionStart != mSelectionEnd);
mSelecting = TRUE;
}
else
{
// start selecting
getPosAndContext(x, y, FALSE, mCursorPos, mInData, mSecondNibble);
mSelectionStart = mCursorPos;
mSelectionEnd = mCursorPos;
mHasSelection = FALSE;
mSelecting = TRUE;
}
}
}
return handled;
}
BOOL DOHexEditor::handleHover(S32 x, S32 y, MASK mask)
{
BOOL handled = FALSE;
if(!hasMouseCapture())
{
handled = childrenHandleHover(x, y, mask) != NULL;
}
if(!handled && mSelecting && hasMouseCapture())
{
// continuation of selecting
getPosAndContext(x, y, TRUE, mCursorPos, mInData, mSecondNibble);
mSelectionEnd = mCursorPos;
mHasSelection = (mSelectionStart != mSelectionEnd);
handled = TRUE;
}
return handled;
}
BOOL DOHexEditor::handleMouseUp(S32 x, S32 y, MASK mask)
{
BOOL handled = FALSE;
handled = LLView::childrenHandleMouseUp(x, y, mask) != NULL;
if(!handled && mSelecting && hasMouseCapture())
{
gFocusMgr.setMouseCapture(NULL);
mSelecting = FALSE;
}
return handled;
}
BOOL DOHexEditor::handleKeyHere(KEY key, MASK mask)
{
return FALSE;
}
BOOL DOHexEditor::handleKey(KEY key, MASK mask, BOOL called_from_parent)
{
BOOL handled = FALSE;
BOOL moved_cursor = FALSE;
U32 old_cursor = mCursorPos;
U32 cursor_line = mCursorPos / mColumns;
U32 doc_first_line = 0;
U32 doc_last_line = mValue.size() / mColumns;
//U32 first_line = mScrollbar->getDocPos();
//U32 last_line = first_line + mScrollbar->getPageSize(); // don't -2 from scrollbar sizes
U32 beginning_of_line = mCursorPos - (mCursorPos % mColumns);
U32 end_of_line = beginning_of_line + mColumns - 1;
handled = TRUE;
switch( key )
{
// Movement keys
case KEY_UP:
if(cursor_line > doc_first_line)
{
moveCursor(mCursorPos - mColumns, mSecondNibble);
moved_cursor = TRUE;
}
break;
case KEY_DOWN:
if(cursor_line < doc_last_line)
{
moveCursor(mCursorPos + mColumns, mSecondNibble);
moved_cursor = TRUE;
}
break;
case KEY_LEFT:
if(mCursorPos)
{
if(!mSecondNibble) moveCursor(mCursorPos - 1, FALSE);
else moveCursor(mCursorPos, FALSE);
moved_cursor = TRUE;
}
break;
case KEY_RIGHT:
moveCursor(mCursorPos + 1, FALSE);
moved_cursor = TRUE;
break;
case KEY_PAGE_UP:
mScrollbar->pageUp(1);
break;
case KEY_PAGE_DOWN:
mScrollbar->pageDown(1);
break;
case KEY_HOME:
if(mask & MASK_CONTROL)
moveCursor(0, FALSE);
else
moveCursor(beginning_of_line, FALSE);
moved_cursor = TRUE;
break;
case KEY_END:
if(mask & MASK_CONTROL)
moveCursor(mValue.size(), FALSE);
else
moveCursor(end_of_line, FALSE);
moved_cursor = TRUE;
break;
// Special
case KEY_INSERT:
gKeyboard->toggleInsertMode();
break;
case KEY_ESCAPE:
gFocusMgr.releaseFocusIfNeeded(this);
break;
// Editing
case KEY_BACKSPACE:
if(mHasSelection)
{
U32 start = getProperSelectionStart();
U32 end = getProperSelectionEnd();
del(start, end - 1, TRUE);
moveCursor(start, FALSE);
}
else if(mCursorPos && (!mSecondNibble))
{
del(mCursorPos - 1, mCursorPos - 1, TRUE);
moveCursor(mCursorPos - 1, FALSE);
}
break;
case KEY_DELETE:
if(mHasSelection)
{
U32 start = getProperSelectionStart();
U32 end = getProperSelectionEnd();
del(start, end - 1, TRUE);
moveCursor(start, FALSE);
}
else if((mCursorPos != mValue.size()) && (!mSecondNibble))
{
del(mCursorPos, mCursorPos, TRUE);
}
break;
default:
handled = FALSE;
break;
}
if(moved_cursor)
{
// Selecting and deselecting
if(mask & MASK_SHIFT)
{
if(!mHasSelection) mSelectionStart = old_cursor;
mSelectionEnd = mCursorPos;
}
else
{
mSelectionStart = mCursorPos;
mSelectionEnd = mCursorPos;
}
mHasSelection = mSelectionStart != mSelectionEnd;
}
return handled;
}
BOOL DOHexEditor::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent)
{
U8 c = uni_char & 0xff;
if(mInData)
{
if(c > 0x39)
{
if(c > 0x46) c -= 0x20;
if(c >= 0x41 && c <= 0x46) c = (c & 0x0f) + 0x09;
else return TRUE;
}
else if(c < 0x30) return TRUE;
else c &= 0x0f;
}
if(uni_char < 0x20) return FALSE;
if( (LL_KIM_INSERT == gKeyboard->getInsertMode() && (!mHasSelection))
|| (!mHasSelection && (mCursorPos == mValue.size())) )// last byte? always insert
{
// Todo: this should overwrite if there's a selection
if(!mInData)
{
std::vector<U8> new_data;
new_data.push_back(c);
insert(mCursorPos, new_data, TRUE);
moveCursor(mCursorPos + 1, FALSE);
}
else if(!mSecondNibble)
{
c <<= 4;
std::vector<U8> new_data;
new_data.push_back(c);
insert(mCursorPos, new_data, TRUE);
moveCursor(mCursorPos, TRUE);
}
else
{
c |= (mValue[mCursorPos] & 0xF0);
std::vector<U8> new_data;
new_data.push_back(c);
overwrite(mCursorPos, mCursorPos, new_data, TRUE);
moveCursor(mCursorPos + 1, FALSE);
}
}
else // overwrite mode
{
if(mHasSelection)
{
if(mInData) c <<= 4;
std::vector<U8> new_data;
new_data.push_back(c);
U8 start = getProperSelectionStart();
overwrite(start, getProperSelectionEnd() - 1, new_data, TRUE);
if(mInData) moveCursor(start, TRUE); // we only entered a nibble
else moveCursor(start + 1, FALSE); // we only entered a byte
}
else if(!mInData)
{
std::vector<U8> new_data;
new_data.push_back(c);
overwrite(mCursorPos, mCursorPos, new_data, TRUE);
moveCursor(mCursorPos + 1, FALSE);
}
else if(!mSecondNibble)
{
c <<= 4;
c |= (mValue[mCursorPos] & 0x0F);
std::vector<U8> new_data;
new_data.push_back(c);
overwrite(mCursorPos, mCursorPos, new_data, TRUE);
moveCursor(mCursorPos, TRUE);
}
else
{
c |= (mValue[mCursorPos] & 0xF0);
std::vector<U8> new_data;
new_data.push_back(c);
overwrite(mCursorPos, mCursorPos, new_data, TRUE);
moveCursor(mCursorPos + 1, FALSE);
}
}
return TRUE;
}
BOOL DOHexEditor::handleUnicodeCharHere(llwchar uni_char)
{
return FALSE;
}
void DOHexEditor::draw()
{
S32 left = 0;
S32 top = getRect().getHeight();
S32 right = getRect().getWidth();
S32 bottom = 0;
BOOL has_focus = gFocusMgr.getKeyboardFocus() == this;
F32 line_height = mGLFont->getLineHeight();
F32 char_width = mGLFont->getWidthF32(".");
F32 data_column_width = char_width * 3; // " 00";
F32 text_x = mTextRect.mLeft;
F32 text_x_data = text_x + (char_width * 10.1f); // "00000000 ", dunno why it's a fraction off
#ifdef COLUMN_SPAN
mColumns = (right - char_width * 2 - text_x_data - mScrollbar->getRect().getWidth()) / (char_width * 4); // touch this if you dare...
#endif
F32 text_x_ascii = text_x_data + (data_column_width * mColumns) + (char_width * 2);
F32 text_y = (F32)(mTextRect.mTop - line_height);
U32 data_length = mValue.size();
U32 line_count = getLineCount();
U32 first_line = mScrollbar->getDocPos();
U32 last_line = first_line + mScrollbar->getPageSize(); // don't -2 from scrollbar sizes
LLRect clip(getRect());
clip.mRight = mScrollbar->getRect().mRight;
clip.mLeft -= 10;
clip.mBottom -= 10;
LLLocalClipRect bgclip(clip);
// Background
gl_rect_2d(left, top, right, bottom, LLColor4::white);
// Let's try drawing some helpful guides
LLColor4 guide_color_light = LLColor4(1.0f, 1.0f, 0.9f);
LLColor4 guide_color_dark = LLColor4(1.0f, 1.0f, 0.8f);
for(U32 col = 0; col < mColumns; col += 2)
{
// Behind hex
F32 box_left = text_x_data + (col * data_column_width) + 2; // skew 2
F32 box_right = box_left + data_column_width;
gl_rect_2d(box_left, top, box_right, bottom, (col & 3) ? guide_color_light : guide_color_dark);
// Behind ASCII
//box_left = text_x_ascii + (col * char_width) - 1; // skew 1
//box_right = box_left + char_width;
//gl_rect_2d(box_left, top, box_right, bottom, guide_color);
}
// Scrollbar & border (drawn twice?)
mBorder->setKeyboardFocusHighlight(has_focus);
LLView::draw();
LLLocalClipRect textrect_clip(mTextRect);
// Selection stuff is reused
U32 selection_start = getProperSelectionStart();
U32 selection_end = getProperSelectionEnd();
U32 selection_first_line = selection_start / mColumns;
U32 selection_last_line = selection_end / mColumns;
U32 selection_start_column = selection_start % mColumns;
U32 selection_end_column = selection_end % mColumns;
// Don't pretend a selection there is visible
if(!selection_end_column)
{
selection_last_line--;
selection_end_column = mColumns;
}
if(mHasSelection)
{
LLColor4 selection_color_context(LLColor4::black);
LLColor4 selection_color_not_context(LLColor4::grey3);
LLColor4 selection_color_data(selection_color_not_context);
LLColor4 selection_color_ascii(selection_color_not_context);
if(mInData) selection_color_data = selection_color_context;
else selection_color_ascii = selection_color_context;
// Setup for selection in data
F32 selection_pixel_x_base = text_x_data + char_width - 3; // skew 3
F32 selection_pixel_x_right_base = selection_pixel_x_base + (data_column_width * mColumns) - char_width + 4;
F32 selection_pixel_x;
F32 selection_pixel_x_right;
F32 selection_pixel_y = (F32)(mTextRect.mTop - line_height) - 3; // skew 3;
selection_pixel_y -= line_height * 2;
selection_pixel_y -= line_height * (S32(selection_first_line) - S32(first_line));
// Selection in data, First line
if(selection_first_line >= first_line && selection_first_line <= last_line)
{
selection_pixel_x = selection_pixel_x_base;
selection_pixel_x += (data_column_width * selection_start_column);
if(selection_first_line == selection_last_line)
{
// Select to last character
selection_pixel_x_right = selection_pixel_x_base + (data_column_width * selection_end_column);
selection_pixel_x_right -= (char_width - 4);
}
else
{
// Select to end of line
selection_pixel_x_right = selection_pixel_x_right_base;
}
gl_rect_2d(selection_pixel_x, selection_pixel_y + line_height, selection_pixel_x_right, selection_pixel_y, selection_color_data);
}
// Selection in data, Middle lines
for(U32 line = selection_first_line + 1; line < selection_last_line; line++)
{
selection_pixel_y -= line_height;
if(line >= first_line && line <= last_line)
{
gl_rect_2d(selection_pixel_x_base, selection_pixel_y + line_height, selection_pixel_x_right_base, selection_pixel_y, selection_color_data);
}
}
// Selection in data, Last line
if(selection_first_line != selection_last_line
&& selection_last_line >= first_line && selection_last_line <= last_line)
{
selection_pixel_x_right = selection_pixel_x_base + (data_column_width * selection_end_column);
selection_pixel_x_right -= (char_width - 4);
selection_pixel_y -= line_height;
gl_rect_2d(selection_pixel_x_base, selection_pixel_y + line_height, selection_pixel_x_right, selection_pixel_y, selection_color_data);
}
selection_pixel_y = (F32)(mTextRect.mTop - line_height) - 3; // skew 3;
selection_pixel_y -= line_height * 2;
selection_pixel_y -= line_height * (S32(selection_first_line) - S32(first_line));
// Setup for selection in ASCII
selection_pixel_x_base = text_x_ascii - 1;
selection_pixel_x_right_base = selection_pixel_x_base + (char_width * mColumns);
// Selection in ASCII, First line
if(selection_first_line >= first_line && selection_first_line <= last_line)
{
selection_pixel_x = selection_pixel_x_base;
selection_pixel_x += (char_width * selection_start_column);
if(selection_first_line == selection_last_line)
{
// Select to last character
selection_pixel_x_right = selection_pixel_x_base + (char_width * selection_end_column);
}
else
{
// Select to end of line
selection_pixel_x_right = selection_pixel_x_right_base;
}
gl_rect_2d(selection_pixel_x, selection_pixel_y + line_height, selection_pixel_x_right, selection_pixel_y, selection_color_ascii);
}
// Selection in ASCII, Middle lines
for(U32 line = selection_first_line + 1; line < selection_last_line; line++)
{
selection_pixel_y -= line_height;
if(line >= first_line && line <= last_line)
{
gl_rect_2d(selection_pixel_x_base, selection_pixel_y + line_height, selection_pixel_x_right_base, selection_pixel_y, selection_color_ascii);
}
}
// Selection in ASCII, Last line
if(selection_first_line != selection_last_line
&& selection_last_line >= first_line && selection_last_line <= last_line)
{
selection_pixel_x_right = selection_pixel_x_base + (char_width * selection_end_column);
selection_pixel_y -= line_height;
gl_rect_2d(selection_pixel_x_base, selection_pixel_y + line_height, selection_pixel_x_right, selection_pixel_y, selection_color_ascii);
}
}
// Insert/Overwrite
std::string text = (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) ? "OVERWRITE" : "INSERT";
mGLFont->renderUTF8(text, 0, text_x, text_y, LLColor4::purple);
// Offset on top
text = "";
for(U32 i = 0; i < mColumns; i++)
{
text.append(llformat(" %02X", i));
}
mGLFont->renderUTF8(text, 0, text_x_data, text_y, LLColor4::blue);
// Size
{
S32 size = mValue.size();
std::string size_desc;
if(size < 1000) size_desc = llformat("%d bytes", size);
else
{
if(size < 1000000)
{
size_desc = llformat("%f", F32(size) / 1000.0f);
int i = size_desc.length() - 1;
for(; i && size_desc.substr(i, 1) == "0"; i--);
if(size_desc.substr(i, 1) == ".") i--;
size_desc = size_desc.substr(0, i + 1);
size_desc.append(" KB");
}
else
{
size_desc = llformat("%f", F32(size) / 1000000.0f);
int i = size_desc.length() - 1;
for(; i && size_desc.substr(i, 1) == "0"; i--);
if(size_desc.substr(i, 1) == ".") i--;
size_desc = size_desc.substr(0, i + 1);
size_desc.append(" MB");
}
}
F32 x = text_x_ascii;
x += (char_width * (mColumns - size_desc.length()));
mGLFont->renderUTF8(size_desc, 0, x, text_y, LLColor4::purple);
}
// Leave a blank line
text_y -= (line_height * 2);
// Everything below "header"
for(U32 line = first_line; line <= last_line; line++)
{
if(line >= line_count) break;
// Offset on left
text = llformat("%08X", line * mColumns); // offset on left
mGLFont->renderUTF8(text, 0, text_x, text_y, LLColor4::blue);
// Setup for rendering hex and ascii
U32 line_char_offset = mColumns * line;
U32 colstart0 = 0;
U32 colend0 = mColumns;
U32 colstart1 = mColumns;
U32 colend1 = mColumns;
U32 colstart2 = mColumns;
U32 colend2 = mColumns;
if(mHasSelection)
{
if(line == selection_first_line)
{
colend0 = selection_start_column;
colstart1 = selection_start_column;
if(selection_first_line == selection_last_line)
{
colend1 = selection_end_column;
colstart2 = selection_end_column;
colend2 = mColumns;
}
}
else if(line > selection_first_line && line < selection_last_line)
{
colend0 = 0;
colstart1 = 0;
colend1 = mColumns;
}
else if(line == selection_last_line)
{
colend0 = 0;
colstart1 = 0;
colend1 = selection_end_column;
colstart2 = selection_end_column;
colend2 = mColumns;
}
}
// Data in hex
text = "";
for(U32 c = colstart0; c < colend0; c++)
{
U32 o = line_char_offset + c;
if(o >= data_length) text.append(" ");
else text.append(llformat(" %02X", mValue[o]));
}
mGLFont->renderUTF8(text, 0, text_x_data + (colstart0 * data_column_width), text_y, LLColor4::black);
text = "";
for(U32 c = colstart1; c < colend1; c++)
{
U32 o = line_char_offset + c;
if(o >= data_length) text.append(" ");
else text.append(llformat(" %02X", mValue[o]));
}
mGLFont->renderUTF8(text, 0, text_x_data + (colstart1 * data_column_width), text_y, LLColor4::white);
text = "";
for(U32 c = colstart2; c < colend2; c++)
{
U32 o = line_char_offset + c;
if(o >= data_length) text.append(" ");
else text.append(llformat(" %02X", mValue[o]));
}
mGLFont->renderUTF8(text, 0, text_x_data + (colstart2 * data_column_width), text_y, LLColor4::black);
// ASCII
text = "";
for(U32 c = colstart0; c < colend0; c++)
{
U32 o = line_char_offset + c;
if(o >= data_length) break;
if((mValue[o] < 0x20) || (mValue[o] >= 0x7F)) text.append(".");
else text.append(llformat("%c", mValue[o]));
}
mGLFont->renderUTF8(text, 0, text_x_ascii + (colstart0 * char_width), text_y, LLColor4::black);
text = "";
for(U32 c = colstart1; c < colend1; c++)
{
U32 o = line_char_offset + c;
if(o >= data_length) break;
if((mValue[o] < 0x20) || (mValue[o] >= 0x7F)) text.append(".");
else text.append(llformat("%c", mValue[o]));
}
mGLFont->renderUTF8(text, 0, text_x_ascii + (colstart1 * char_width), text_y, LLColor4::white);
text = "";
for(U32 c = colstart2; c < colend2; c++)
{
U32 o = line_char_offset + c;
if(o >= data_length) break;
if((mValue[o] < 0x20) || (mValue[o] >= 0x7F)) text.append(".");
else text.append(llformat("%c", mValue[o]));
}
mGLFont->renderUTF8(text, 0, text_x_ascii + (colstart2 * char_width), text_y, LLColor4::black);
text_y -= line_height;
}
// Cursor
if(has_focus && !mHasSelection && (U32(LLTimer::getElapsedSeconds() * 2.0f) & 0x1))
{
U32 cursor_line = mCursorPos / mColumns;
if((cursor_line >= first_line) && (cursor_line <= last_line))
{
F32 pixel_y = (F32)(mTextRect.mTop - line_height);
pixel_y -= line_height * (2 + (cursor_line - first_line));
U32 cursor_offset = mCursorPos % mColumns; // bytes
F32 pixel_x = mInData ? text_x_data : text_x_ascii;
if(mInData)
{
pixel_x += data_column_width * cursor_offset;
pixel_x += char_width;
if(mSecondNibble) pixel_x += char_width;
}
else
{
pixel_x += char_width * cursor_offset;
}
pixel_x -= 2.0f;
pixel_y -= 2.0f;
gl_rect_2d(pixel_x, pixel_y + line_height, pixel_x + 2, pixel_y, LLColor4::black);
}
}
}
void DOHexEditor::deselect()
{
mSelectionStart = mCursorPos;
mSelectionEnd = mCursorPos;
mHasSelection = FALSE;
mSelecting = FALSE;
}
BOOL DOHexEditor::canUndo() const
{
return mUndoBuffer->canUndo();
}
void DOHexEditor::undo()
{
mUndoBuffer->undoAction();
}
BOOL DOHexEditor::canRedo() const
{
return mUndoBuffer->canRedo();
}
void DOHexEditor::redo()
{
mUndoBuffer->redoAction();
}
void DOHexEditor::moveCursor(U32 pos, BOOL second_nibble)
{
mCursorPos = pos;
// Clamp and handle second nibble
if(mCursorPos >= mValue.size())
{
mCursorPos = mValue.size();
mSecondNibble = FALSE;
}
else
{
mSecondNibble = mInData ? second_nibble : FALSE;
}
// Change selection
mSelectionEnd = mCursorPos;
if(!mHasSelection) mSelectionStart = mCursorPos;
// Scroll
U32 line = mCursorPos / mColumns;
U32 first_line = mScrollbar->getDocPos();
U32 last_line = first_line + mScrollbar->getPageSize(); // don't -2 from scrollbar sizes
if(line < first_line) mScrollbar->setDocPos(line);
if(line > (last_line - 2)) mScrollbar->setDocPos(line - mScrollbar->getPageSize() + 1);
}
BOOL DOHexEditor::canCut() const
{
return mHasSelection;
}
void DOHexEditor::cut()
{
if(!canCut()) return;
copy();
U32 start = getProperSelectionStart();
del(start, getProperSelectionEnd() - 1, TRUE);
moveCursor(start, FALSE);
}
BOOL DOHexEditor::canCopy() const
{
return mHasSelection;
}
void DOHexEditor::copy()
{
if(!canCopy()) return;
std::string text;
if(mInData)
{
U32 start = getProperSelectionStart();
U32 end = getProperSelectionEnd();
for(U32 i = start; i < end; i++)
text.append(llformat("%02X", mValue[i]));
}
else
{
U32 start = getProperSelectionStart();
U32 end = getProperSelectionEnd();
for(U32 i = start; i < end; i++)
text.append(llformat("%c", mValue[i]));
}
LLWString wtext = utf8str_to_wstring(text);
gClipboard.copyFromSubstring(wtext, 0, wtext.length());
}
BOOL DOHexEditor::canPaste() const
{
return TRUE;
}
void DOHexEditor::paste()
{
if(!canPaste()) return;
std::string clipstr = wstring_to_utf8str(gClipboard.getPasteWString());
const char* clip = clipstr.c_str();
std::vector<U8> new_data;
if(mInData)
{
int len = strlen(clip);
for(int i = 0; (i + 1) < len; i += 2)
{
int c = 0;
if(sscanf(&(clip[i]), "%02X", &c) != 1) break;
new_data.push_back(U8(c));
}
}
else
{
int len = strlen(clip);
for(int i = 0; i < len; i++)
{
U8 c = 0;
if(sscanf(&(clip[i]), "%c", &c) != 1) break;
new_data.push_back(c);
}
}
U32 start = mCursorPos;
if(!mHasSelection)
insert(start, new_data, TRUE);
else
{
start = getProperSelectionStart();
overwrite(start, getProperSelectionEnd() - 1, new_data, TRUE);
}
moveCursor(start + new_data.size(), FALSE);
}
BOOL DOHexEditor::canDoDelete() const
{
return mValue.size() > 0;
}
void DOHexEditor::doDelete()
{
if(!canDoDelete()) return;
U32 start = getProperSelectionStart();
del(start, getProperSelectionEnd(), TRUE);
moveCursor(start, FALSE);
}
BOOL DOHexEditor::canSelectAll() const
{
return mValue.size() > 0;
}
void DOHexEditor::selectAll()
{
if(!canSelectAll()) return;
mSelectionStart = 0;
mSelectionEnd = mValue.size();
mHasSelection = mSelectionStart != mSelectionEnd;
}
BOOL DOHexEditor::canDeselect() const
{
return mHasSelection;
}
void DOHexEditor::insert(U32 pos, std::vector<U8> new_data, BOOL undoable)
{
if(pos > mValue.size())
{
llwarns << "pos outside data!" << llendl;
return;
}
deselect();
if(undoable)
{
DOUndoHex* action = (DOUndoHex*)(mUndoBuffer->getNextAction());
action->set(this, &(DOUndoHex::undoInsert), &(DOUndoHex::redoInsert), pos, pos, std::vector<U8>(), new_data);
}
std::vector<U8>::iterator wheres = mValue.begin() + pos;
mValue.insert(wheres, new_data.begin(), new_data.end());
changedLength();
}
void DOHexEditor::overwrite(U32 first_pos, U32 last_pos, std::vector<U8> new_data, BOOL undoable)
{
if(first_pos > mValue.size() || last_pos > mValue.size())
{
llwarns << "pos outside data!" << llendl;
return;
}
deselect();
std::vector<U8>::iterator first = mValue.begin() + first_pos;
std::vector<U8>::iterator last = mValue.begin() + last_pos;
std::vector<U8> old_data;
if(last_pos > 0) old_data = std::vector<U8>(first, last + 1);
if(undoable)
{
DOUndoHex* action = (DOUndoHex*)(mUndoBuffer->getNextAction());
action->set(this, &(DOUndoHex::undoOverwrite), &(DOUndoHex::redoOverwrite), first_pos, last_pos, old_data, new_data);
}
mValue.erase(first, last + 1);
first = mValue.begin() + first_pos;
mValue.insert(first, new_data.begin(), new_data.end());
changedLength();
}
void DOHexEditor::del(U32 first_pos, U32 last_pos, BOOL undoable)
{
if(first_pos > mValue.size() || last_pos > mValue.size())
{
llwarns << "pos outside data!" << llendl;
return;
}
deselect();
std::vector<U8>::iterator first = mValue.begin() + first_pos;
std::vector<U8>::iterator last = mValue.begin() + last_pos;
std::vector<U8> old_data;
if(last_pos > 0) old_data = std::vector<U8>(first, last + 1);
if(undoable)
{
DOUndoHex* action = (DOUndoHex*)(mUndoBuffer->getNextAction());
action->set(this, &(DOUndoHex::undoDel), &(DOUndoHex::redoDel), first_pos, last_pos, old_data, std::vector<U8>());
}
mValue.erase(first, last + 1);
changedLength();
}
void DOUndoHex::set(DOHexEditor* hex_editor,
void (*undo_action)(DOUndoHex*),
void (*redo_action)(DOUndoHex*),
U32 first_pos,
U32 last_pos,
std::vector<U8> old_data,
std::vector<U8> new_data)
{
mHexEditor = hex_editor;
mUndoAction = undo_action;
mRedoAction = redo_action;
mFirstPos = first_pos;
mLastPos = last_pos;
mOldData = old_data;
mNewData = new_data;
}
void DOUndoHex::undo()
{
mUndoAction(this);
}
void DOUndoHex::redo()
{
mRedoAction(this);
}
void DOUndoHex::undoInsert(DOUndoHex* action)
{
//action->mHexEditor->del(action->mFirstPos, action->mLastPos, FALSE);
action->mHexEditor->del(action->mFirstPos, action->mFirstPos + action->mNewData.size() - 1, FALSE);
}
void DOUndoHex::redoInsert(DOUndoHex* action)
{
action->mHexEditor->insert(action->mFirstPos, action->mNewData, FALSE);
}
void DOUndoHex::undoOverwrite(DOUndoHex* action)
{
//action->mHexEditor->overwrite(action->mFirstPos, action->mLastPos, action->mOldData, FALSE);
action->mHexEditor->overwrite(action->mFirstPos, action->mFirstPos + action->mNewData.size() - 1, action->mOldData, FALSE);
}
void DOUndoHex::redoOverwrite(DOUndoHex* action)
{
action->mHexEditor->overwrite(action->mFirstPos, action->mLastPos, action->mNewData, FALSE);
}
void DOUndoHex::undoDel(DOUndoHex* action)
{
action->mHexEditor->insert(action->mFirstPos, action->mOldData, FALSE);
}
void DOUndoHex::redoDel(DOUndoHex* action)
{
action->mHexEditor->del(action->mFirstPos, action->mLastPos, FALSE);
}
// </edit>