491 lines
15 KiB
C++
491 lines
15 KiB
C++
/**
|
|
* @file lltexteditor.h
|
|
* @brief LLTextEditor base class
|
|
*
|
|
* Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
|
|
* $License$
|
|
*/
|
|
|
|
// Text editor widget to let users enter a a multi-line ASCII document//
|
|
|
|
#ifndef LL_LLTEXTEDITOR_H
|
|
#define LL_LLTEXTEDITOR_H
|
|
|
|
#include "llrect.h"
|
|
#include "llkeywords.h"
|
|
#include "lluictrl.h"
|
|
#include "llframetimer.h"
|
|
#include "lldarray.h"
|
|
#include "llstyle.h"
|
|
#include "lleditmenuhandler.h"
|
|
#include "lldarray.h"
|
|
|
|
class LLFontGL;
|
|
class LLScrollbar;
|
|
class LLViewBorder;
|
|
class LLKeywordToken;
|
|
class LLTextCmd;
|
|
class LLUICtrlFactory;
|
|
|
|
//
|
|
// Constants
|
|
//
|
|
|
|
const llwchar FIRST_EMBEDDED_CHAR = 0x100000;
|
|
const llwchar LAST_EMBEDDED_CHAR = 0x10ffff;
|
|
const S32 MAX_EMBEDDED_ITEMS = LAST_EMBEDDED_CHAR - FIRST_EMBEDDED_CHAR + 1;
|
|
|
|
//
|
|
// Classes
|
|
//
|
|
class LLTextSegment;
|
|
class LLTextCmd;
|
|
|
|
class LLTextEditor : public LLUICtrl, LLEditMenuHandler
|
|
{
|
|
friend class LLTextCmd;
|
|
public:
|
|
LLTextEditor(const LLString& name,
|
|
const LLRect& rect,
|
|
S32 max_length,
|
|
const LLString &default_text,
|
|
const LLFontGL* glfont = NULL,
|
|
BOOL allow_embedded_items = FALSE);
|
|
|
|
virtual ~LLTextEditor();
|
|
|
|
virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_TEXT_EDITOR; }
|
|
virtual LLString getWidgetTag() const;
|
|
|
|
virtual LLXMLNodePtr getXML(bool save_children = true) const;
|
|
static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
|
|
void setTextEditorParameters(LLXMLNodePtr node);
|
|
void setParseHTML(BOOL parsing) {mParseHTML=parsing;}
|
|
|
|
// mousehandler overrides
|
|
virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
|
|
virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
|
|
virtual BOOL handleHover(S32 x, S32 y, MASK mask);
|
|
virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
|
|
virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask );
|
|
virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent );
|
|
virtual BOOL handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent);
|
|
|
|
virtual BOOL handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect);
|
|
virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
|
|
EDragAndDropType cargo_type, void *cargo_data,
|
|
EAcceptance *accept, LLString& tooltip_msg);
|
|
virtual void onMouseCaptureLost();
|
|
|
|
|
|
// view overrides
|
|
virtual void reshape(S32 width, S32 height, BOOL called_from_parent);
|
|
virtual void draw();
|
|
virtual void onFocusLost();
|
|
virtual void setEnabled(BOOL enabled);
|
|
|
|
// uictrl overrides
|
|
virtual void onTabInto();
|
|
virtual void clear();
|
|
virtual void setFocus( BOOL b );
|
|
virtual BOOL acceptsTextInput() const;
|
|
|
|
// LLEditMenuHandler interface
|
|
virtual void undo();
|
|
virtual BOOL canUndo();
|
|
|
|
virtual void redo();
|
|
virtual BOOL canRedo();
|
|
|
|
virtual void cut();
|
|
virtual BOOL canCut();
|
|
|
|
virtual void copy();
|
|
virtual BOOL canCopy();
|
|
|
|
virtual void paste();
|
|
virtual BOOL canPaste();
|
|
|
|
virtual void doDelete();
|
|
virtual BOOL canDoDelete();
|
|
|
|
virtual void selectAll();
|
|
virtual BOOL canSelectAll();
|
|
|
|
virtual void deselect();
|
|
virtual BOOL canDeselect();
|
|
|
|
void selectNext(const LLString& search_text_in, BOOL case_insensitive, BOOL wrap = TRUE);
|
|
BOOL replaceText(const LLString& search_text, const LLString& replace_text, BOOL case_insensitive, BOOL wrap = TRUE);
|
|
void replaceTextAll(const LLString& search_text, const LLString& replace_text, BOOL case_insensitive);
|
|
|
|
// Undo/redo stack
|
|
void blockUndo();
|
|
|
|
// Text editing
|
|
virtual void makePristine();
|
|
BOOL isPristine() const;
|
|
|
|
// inserts text at cursor
|
|
void insertText(const LLString &text);
|
|
// appends text at end
|
|
void appendText(const LLString &wtext, bool allow_undo, bool prepend_newline,
|
|
const LLStyle* segment_style = NULL);
|
|
|
|
void appendColoredText(const LLString &wtext, bool allow_undo,
|
|
bool prepend_newline,
|
|
const LLColor4 &color,
|
|
const LLString& font_name = LLString::null);
|
|
// if styled text starts a line, you need to prepend a newline.
|
|
void appendStyledText(const LLString &new_text, bool allow_undo,
|
|
bool prepend_newline,
|
|
const LLStyle* style);
|
|
|
|
// Removes text from the end of document
|
|
// Does not change highlight or cursor position.
|
|
void removeTextFromEnd(S32 num_chars);
|
|
|
|
BOOL tryToRevertToPristineState();
|
|
|
|
void setCursor(S32 row, S32 column);
|
|
void setCursorPos(S32 offset);
|
|
void setCursorAndScrollToEnd();
|
|
|
|
void getCurrentLineAndColumn( S32* line, S32* col, BOOL include_wordwrap );
|
|
|
|
void loadKeywords(const LLString& filename,
|
|
const LLDynamicArray<const char*>& funcs,
|
|
const LLDynamicArray<const char*>& tooltips,
|
|
const LLColor3& func_color);
|
|
|
|
void setCursorColor(const LLColor4& c) { mCursorColor = c; }
|
|
void setFgColor( const LLColor4& c ) { mFgColor = c; }
|
|
void setReadOnlyFgColor( const LLColor4& c ) { mReadOnlyFgColor = c; }
|
|
void setWriteableBgColor( const LLColor4& c ) { mWriteableBgColor = c; }
|
|
void setReadOnlyBgColor( const LLColor4& c ) { mReadOnlyBgColor = c; }
|
|
|
|
void setTrackColor( const LLColor4& color );
|
|
void setThumbColor( const LLColor4& color );
|
|
void setHighlightColor( const LLColor4& color );
|
|
void setShadowColor( const LLColor4& color );
|
|
|
|
// Hacky methods to make it into a word-wrapping, potentially scrolling,
|
|
// read-only text box.
|
|
void setBorderVisible(BOOL b);
|
|
void setTakesNonScrollClicks(BOOL b);
|
|
void setHideScrollbarForShortDocs(BOOL b);
|
|
|
|
void setWordWrap( BOOL b );
|
|
void setTabToNextField(BOOL b) { mTabToNextField = b; }
|
|
void setCommitOnFocusLost(BOOL b) { mCommitOnFocusLost = b; }
|
|
|
|
// If takes focus, will take keyboard focus on click.
|
|
void setTakesFocus(BOOL b) { mTakesFocus = b; }
|
|
|
|
// Hack to handle Notecards
|
|
virtual BOOL importBuffer(const LLString& buffer );
|
|
virtual BOOL exportBuffer(LLString& buffer );
|
|
|
|
void setSourceID(const LLUUID& id) { mSourceID = id; }
|
|
void setAcceptCallingCardNames(BOOL enable) { mAcceptCallingCardNames = enable; }
|
|
|
|
void setHandleEditKeysDirectly( BOOL b ) { mHandleEditKeysDirectly = b; }
|
|
|
|
// Callbacks
|
|
static void setLinkColor(LLColor4 color) { mLinkColor = color; }
|
|
static void setURLCallbacks( void (*callback1) (const char* url),
|
|
BOOL (*callback2) (LLString url) )
|
|
{ mURLcallback = callback1; mSecondlifeURLcallback = callback2;}
|
|
|
|
void setOnScrollEndCallback(void (*callback)(void*), void* userdata);
|
|
|
|
// new methods
|
|
void setValue(const LLSD& value);
|
|
LLSD getValue() const;
|
|
|
|
const LLString& getText() const;
|
|
|
|
// Non-undoable
|
|
void setText(const LLString &utf8str);
|
|
void setWText(const LLWString &wtext);
|
|
|
|
S32 getMaxLength() const { return mMaxTextLength; }
|
|
|
|
// Change cursor
|
|
void startOfLine();
|
|
void endOfLine();
|
|
void endOfDoc();
|
|
|
|
// Getters
|
|
const LLWString& getWText() const;
|
|
llwchar getWChar(S32 pos);
|
|
LLWString getWSubString(S32 pos, S32 len);
|
|
|
|
LLTextSegment* getCurrentSegment();
|
|
LLTextSegment* getPreviousSegment();
|
|
void getSelectedSegments(std::vector<LLTextSegment*>& segments);
|
|
|
|
protected:
|
|
S32 getLength() const;
|
|
void getSegmentAndOffset( S32 startpos, S32* segidxp, S32* offsetp );
|
|
|
|
void drawBackground();
|
|
void drawSelectionBackground();
|
|
void drawCursor();
|
|
void drawText();
|
|
void drawClippedSegment(const LLWString &wtext, S32 seg_start, S32 seg_end, F32 x, F32 y, S32 selection_left, S32 selection_right, const LLStyle& color, F32* right_x);
|
|
|
|
void updateLineStartList(S32 startpos = 0);
|
|
void updateScrollFromCursor();
|
|
void updateTextRect();
|
|
void updateSegments();
|
|
void pruneSegments();
|
|
|
|
void assignEmbedded(const LLString &s);
|
|
void truncate();
|
|
|
|
static BOOL isPartOfWord(llwchar c);
|
|
|
|
void removeCharOrTab();
|
|
void setCursorAtLocalPos(S32 x, S32 y, BOOL round);
|
|
S32 getCursorPosFromLocalCoord( S32 local_x, S32 local_y, BOOL round );
|
|
|
|
void indentSelectedLines( S32 spaces );
|
|
S32 indentLine( S32 pos, S32 spaces );
|
|
void unindentLineBeforeCloseBrace();
|
|
|
|
S32 getSegmentIdxAtOffset(S32 offset);
|
|
LLTextSegment* getSegmentAtLocalPos(S32 x, S32 y);
|
|
LLTextSegment* getSegmentAtOffset(S32 offset);
|
|
|
|
void reportBadKeystroke();
|
|
|
|
BOOL handleNavigationKey(const KEY key, const MASK mask);
|
|
BOOL handleSpecialKey(const KEY key, const MASK mask, BOOL* return_key_hit);
|
|
BOOL handleSelectionKey(const KEY key, const MASK mask);
|
|
BOOL handleControlKey(const KEY key, const MASK mask);
|
|
BOOL handleEditKey(const KEY key, const MASK mask);
|
|
|
|
BOOL hasSelection() { return (mSelectionStart !=mSelectionEnd); }
|
|
BOOL selectionContainsLineBreaks();
|
|
void startSelection();
|
|
void endSelection();
|
|
void deleteSelection(BOOL transient_operation);
|
|
|
|
S32 prevWordPos(S32 cursorPos) const;
|
|
S32 nextWordPos(S32 cursorPos) const;
|
|
|
|
S32 getLineCount();
|
|
S32 getLineStart( S32 line );
|
|
void getLineAndOffset(S32 pos, S32* linep, S32* offsetp);
|
|
S32 getPos(S32 line, S32 offset);
|
|
|
|
void changePage(S32 delta);
|
|
void changeLine(S32 delta);
|
|
|
|
void autoIndent();
|
|
|
|
S32 execute(LLTextCmd* cmd);
|
|
|
|
void findEmbeddedItemSegments();
|
|
|
|
virtual BOOL handleMouseUpOverSegment(S32 x, S32 y, MASK mask);
|
|
virtual llwchar pasteEmbeddedItem(llwchar ext_char);
|
|
virtual void bindEmbeddedChars(const LLFontGL* font);
|
|
virtual void unbindEmbeddedChars(const LLFontGL* font);
|
|
|
|
S32 findHTMLToken(const LLString &line, S32 pos, BOOL reverse);
|
|
BOOL findHTML(const LLString &line, S32 *begin, S32 *end);
|
|
|
|
protected:
|
|
// Undoable operations
|
|
void addChar(llwchar c); // at mCursorPos
|
|
S32 addChar(S32 pos, llwchar wc);
|
|
S32 overwriteChar(S32 pos, llwchar wc);
|
|
void removeChar();
|
|
S32 removeChar(S32 pos);
|
|
S32 insert(const S32 pos, const LLWString &wstr, const BOOL group_with_next_op);
|
|
S32 remove(const S32 pos, const S32 length, const BOOL group_with_next_op);
|
|
S32 append(const LLWString &wstr, const BOOL group_with_next_op);
|
|
|
|
// direct operations
|
|
S32 insertStringNoUndo(S32 pos, const LLWString &utf8str); // returns num of chars actually inserted
|
|
S32 removeStringNoUndo(S32 pos, S32 length);
|
|
S32 overwriteCharNoUndo(S32 pos, llwchar wc);
|
|
|
|
public:
|
|
LLKeywords mKeywords;
|
|
static LLColor4 mLinkColor;
|
|
static void (*mURLcallback) (const char* url);
|
|
static BOOL (*mSecondlifeURLcallback) (LLString url);
|
|
protected:
|
|
LLWString mWText;
|
|
mutable LLString mUTF8Text;
|
|
mutable BOOL mTextIsUpToDate;
|
|
|
|
S32 mMaxTextLength; // Maximum length mText is allowed to be
|
|
|
|
const LLFontGL* mGLFont;
|
|
|
|
LLScrollbar* mScrollbar;
|
|
LLViewBorder* mBorder;
|
|
|
|
BOOL mBaseDocIsPristine;
|
|
LLTextCmd* mPristineCmd;
|
|
|
|
LLTextCmd* mLastCmd;
|
|
|
|
typedef std::deque<LLTextCmd*> undo_stack_t;
|
|
undo_stack_t mUndoStack;
|
|
|
|
S32 mCursorPos; // I-beam is just after the mCursorPos-th character.
|
|
S32 mDesiredXPixel; // X pixel position where the user wants the cursor to be
|
|
LLRect mTextRect; // The rect in which text is drawn. Excludes borders.
|
|
// List of offsets and segment index of the start of each line. Always has at least one node (0).
|
|
struct line_info
|
|
{
|
|
line_info(S32 segment, S32 offset) : mSegment(segment), mOffset(offset) {}
|
|
S32 mSegment;
|
|
S32 mOffset;
|
|
};
|
|
struct line_info_compare
|
|
{
|
|
bool operator()(const line_info& a, const line_info& b) const
|
|
{
|
|
if (a.mSegment < b.mSegment)
|
|
return true;
|
|
else if (a.mSegment > b.mSegment)
|
|
return false;
|
|
else
|
|
return a.mOffset < b.mOffset;
|
|
}
|
|
};
|
|
typedef std::vector<line_info> line_list_t;
|
|
line_list_t mLineStartList;
|
|
|
|
// Are we in the middle of a drag-select? To figure out if there is a current
|
|
// selection, call hasSelection().
|
|
BOOL mIsSelecting;
|
|
|
|
S32 mSelectionStart;
|
|
S32 mSelectionEnd;
|
|
|
|
void (*mOnScrollEndCallback)(void*);
|
|
void *mOnScrollEndData;
|
|
|
|
typedef std::vector<LLTextSegment *> segment_list_t;
|
|
segment_list_t mSegments;
|
|
LLTextSegment* mHoverSegment;
|
|
LLFrameTimer mKeystrokeTimer;
|
|
|
|
LLColor4 mCursorColor;
|
|
|
|
LLColor4 mFgColor;
|
|
LLColor4 mReadOnlyFgColor;
|
|
LLColor4 mWriteableBgColor;
|
|
LLColor4 mReadOnlyBgColor;
|
|
LLColor4 mFocusBgColor;
|
|
|
|
BOOL mReadOnly;
|
|
BOOL mWordWrap;
|
|
|
|
BOOL mTabToNextField; // if true, tab moves focus to next field, else inserts spaces
|
|
BOOL mCommitOnFocusLost;
|
|
BOOL mTakesFocus;
|
|
BOOL mHideScrollbarForShortDocs;
|
|
BOOL mTakesNonScrollClicks;
|
|
|
|
BOOL mAllowEmbeddedItems;
|
|
|
|
BOOL mAcceptCallingCardNames;
|
|
|
|
LLUUID mSourceID;
|
|
|
|
BOOL mHandleEditKeysDirectly; // If true, the standard edit keys (Ctrl-X, Delete, etc,) are handled here instead of routed by the menu system
|
|
|
|
// Use these to determine if a click on an embedded item is a drag
|
|
// or not.
|
|
S32 mMouseDownX;
|
|
S32 mMouseDownY;
|
|
|
|
S32 mLastSelectionX;
|
|
S32 mLastSelectionY;
|
|
|
|
BOOL mParseHTML;
|
|
LLString mHTML;
|
|
};
|
|
|
|
class LLTextSegment
|
|
{
|
|
public:
|
|
// for creating a compare value
|
|
LLTextSegment(S32 start);
|
|
LLTextSegment( const LLStyle& style, S32 start, S32 end );
|
|
LLTextSegment( const LLColor4& color, S32 start, S32 end, BOOL is_visible);
|
|
LLTextSegment( const LLColor4& color, S32 start, S32 end );
|
|
LLTextSegment( const LLColor3& color, S32 start, S32 end );
|
|
|
|
S32 getStart() { return mStart; }
|
|
S32 getEnd() { return mEnd; }
|
|
void setEnd( S32 end ) { mEnd = end; }
|
|
const LLColor4& getColor() { return mStyle.getColor(); }
|
|
void setColor(const LLColor4 &color) { mStyle.setColor(color); }
|
|
const LLStyle& getStyle() { return mStyle; }
|
|
void setStyle(const LLStyle &style) { mStyle = style; }
|
|
void setIsDefault(BOOL b) { mIsDefault = b; }
|
|
BOOL getIsDefault() { return mIsDefault; }
|
|
|
|
void setToken( LLKeywordToken* token ) { mToken = token; }
|
|
LLKeywordToken* getToken() { return mToken; }
|
|
BOOL getToolTip( LLString& msg );
|
|
|
|
void dump();
|
|
|
|
struct compare
|
|
{
|
|
bool operator()(const LLTextSegment* a, const LLTextSegment* b) const
|
|
{
|
|
return a->mStart < b->mStart;
|
|
}
|
|
};
|
|
|
|
private:
|
|
LLStyle mStyle;
|
|
S32 mStart;
|
|
S32 mEnd;
|
|
LLKeywordToken* mToken;
|
|
BOOL mIsDefault;
|
|
};
|
|
|
|
class LLTextCmd
|
|
{
|
|
public:
|
|
LLTextCmd( S32 pos, BOOL group_with_next )
|
|
: mPos(pos),
|
|
mGroupWithNext(group_with_next)
|
|
{
|
|
}
|
|
virtual ~LLTextCmd() {}
|
|
virtual BOOL execute(LLTextEditor* editor, S32* delta) = 0;
|
|
virtual S32 undo(LLTextEditor* editor) = 0;
|
|
virtual S32 redo(LLTextEditor* editor) = 0;
|
|
virtual BOOL canExtend(S32 pos);
|
|
virtual void blockExtensions();
|
|
virtual BOOL extendAndExecute( LLTextEditor* editor, S32 pos, llwchar c, S32* delta );
|
|
virtual BOOL hasExtCharValue( llwchar value );
|
|
|
|
// Define these here so they can access LLTextEditor through the friend relationship
|
|
S32 insert(LLTextEditor* editor, S32 pos, const LLWString &utf8str);
|
|
S32 remove(LLTextEditor* editor, S32 pos, S32 length);
|
|
S32 overwrite(LLTextEditor* editor, S32 pos, llwchar wc);
|
|
|
|
BOOL groupWithNext() { return mGroupWithNext; }
|
|
|
|
protected:
|
|
S32 mPos;
|
|
BOOL mGroupWithNext;
|
|
};
|
|
|
|
|
|
#endif // LL_TEXTEDITOR_
|