diff options
Diffstat (limited to 'indra/llui/lltexteditor.cpp')
| -rw-r--r-- | indra/llui/lltexteditor.cpp | 3022 | 
1 files changed, 589 insertions, 2433 deletions
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 34bced064e..94bf716e7d 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -1,32 +1,25 @@  /**    * @file lltexteditor.cpp - * @brief LLTextEditor base class   * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - *  - * Copyright (c) 2001-2009, Linden Research, Inc. - *  + * $LicenseInfo:firstyear=2001&license=viewerlgpl$   * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab.  Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * Copyright (C) 2010, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only.   *  - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details.   *  - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA   *  - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA   * $/LicenseInfo$   */ @@ -34,9 +27,13 @@  #include "linden_common.h" +#define LLTEXTEDITOR_CPP  #include "lltexteditor.h" +#include "llfontfreetype.h" // for LLFontFreetype::FIRST_CHAR  #include "llfontgl.h" +#include "llgl.h"			// LLGLSUIDefault() +#include "lllocalcliprect.h"  #include "llrender.h"  #include "llui.h"  #include "lluictrlfactory.h" @@ -45,7 +42,6 @@  #include "lltimer.h"  #include "llmath.h" -#include "audioengine.h"  #include "llclipboard.h"  #include "llscrollbar.h"  #include "llstl.h" @@ -55,55 +51,56 @@  #include "llundo.h"  #include "llviewborder.h"  #include "llcontrol.h" -#include "llimagegl.h"  #include "llwindow.h"  #include "lltextparser.h" +#include "llscrollcontainer.h" +#include "llpanel.h" +#include "llurlregistry.h" +#include "lltooltip.h" +#include "llmenugl.h" +  #include <queue> +#include "llcombobox.h"  //   // Globals  // -static LLDefaultWidgetRegistry::Register<LLTextEditor> r("simple_text_editor"); +static LLDefaultChildRegistry::Register<LLTextEditor> r("simple_text_editor"); + +// Compiler optimization, generate extern template +template class LLTextEditor* LLView::getChild<class LLTextEditor>( +	const std::string& name, BOOL recurse) const;  //  // Constants  //  const S32	UI_TEXTEDITOR_LINE_NUMBER_MARGIN = 32;  const S32	UI_TEXTEDITOR_LINE_NUMBER_DIGITS = 4; -const F32	CURSOR_FLASH_DELAY = 1.0f;  // in seconds -const S32	CURSOR_THICKNESS = 2;  const S32	SPACES_PER_TAB = 4; - -LLColor4 LLTextEditor::mLinkColor = LLColor4::blue; -void (* LLTextEditor::mURLcallback)(const std::string&)   = NULL; -bool (* LLTextEditor::mSecondlifeURLcallback)(const std::string&)   = NULL; -bool (* LLTextEditor::mSecondlifeURLcallbackRightClick)(const std::string&)   = NULL; - -  /////////////////////////////////////////////////////////////////// -class LLTextEditor::LLTextCmdInsert : public LLTextEditor::LLTextCmd +class LLTextEditor::TextCmdInsert : public LLTextBase::TextCmd  {  public: -	LLTextCmdInsert(S32 pos, BOOL group_with_next, const LLWString &ws) -		: LLTextCmd(pos, group_with_next), mWString(ws) +	TextCmdInsert(S32 pos, BOOL group_with_next, const LLWString &ws, LLTextSegmentPtr segment) +		: TextCmd(pos, group_with_next, segment), mWString(ws)  	{  	} -	virtual ~LLTextCmdInsert() {} -	virtual BOOL execute( LLTextEditor* editor, S32* delta ) +	virtual ~TextCmdInsert() {} +	virtual BOOL execute( LLTextBase* editor, S32* delta )  	{  		*delta = insert(editor, getPosition(), mWString );  		LLWStringUtil::truncate(mWString, *delta);  		//mWString = wstring_truncate(mWString, *delta);  		return (*delta != 0);  	}	 -	virtual S32 undo( LLTextEditor* editor ) +	virtual S32 undo( LLTextBase* editor )  	{  		remove(editor, getPosition(), mWString.length() );  		return getPosition();  	} -	virtual S32 redo( LLTextEditor* editor ) +	virtual S32 redo( LLTextBase* editor )  	{  		insert(editor, getPosition(), mWString );  		return getPosition() + mWString.length(); @@ -114,11 +111,11 @@ private:  };  /////////////////////////////////////////////////////////////////// -class LLTextEditor::LLTextCmdAddChar : public LLTextEditor::LLTextCmd +class LLTextEditor::TextCmdAddChar : public LLTextBase::TextCmd  {  public: -	LLTextCmdAddChar( S32 pos, BOOL group_with_next, llwchar wc) -		: LLTextCmd(pos, group_with_next), mWString(1, wc), mBlockExtensions(FALSE) +	TextCmdAddChar( S32 pos, BOOL group_with_next, llwchar wc, LLTextSegmentPtr segment) +		: TextCmd(pos, group_with_next, segment), mWString(1, wc), mBlockExtensions(FALSE)  	{  	}  	virtual void blockExtensions() @@ -127,16 +124,19 @@ public:  	}  	virtual BOOL canExtend(S32 pos) const  	{ +		// cannot extend text with custom segments +		if (!mSegments.empty()) return FALSE; +  		return !mBlockExtensions && (pos == getPosition() + (S32)mWString.length());  	} -	virtual BOOL execute( LLTextEditor* editor, S32* delta ) +	virtual BOOL execute( LLTextBase* editor, S32* delta )  	{  		*delta = insert(editor, getPosition(), mWString);  		LLWStringUtil::truncate(mWString, *delta);  		//mWString = wstring_truncate(mWString, *delta);  		return (*delta != 0);  	} -	virtual BOOL extendAndExecute( LLTextEditor* editor, S32 pos, llwchar wc, S32* delta )	 +	virtual BOOL extendAndExecute( LLTextBase* editor, S32 pos, llwchar wc, S32* delta )	  	{   		LLWString ws;  		ws += wc; @@ -148,12 +148,12 @@ public:  		}  		return (*delta != 0);  	} -	virtual S32 undo( LLTextEditor* editor ) +	virtual S32 undo( LLTextBase* editor )  	{  		remove(editor, getPosition(), mWString.length() );  		return getPosition();  	} -	virtual S32 redo( LLTextEditor* editor ) +	virtual S32 redo( LLTextBase* editor )  	{  		insert(editor, getPosition(), mWString );  		return getPosition() + mWString.length(); @@ -167,25 +167,25 @@ private:  /////////////////////////////////////////////////////////////////// -class LLTextEditor::LLTextCmdOverwriteChar : public LLTextEditor::LLTextCmd +class LLTextEditor::TextCmdOverwriteChar : public LLTextBase::TextCmd  {  public: -	LLTextCmdOverwriteChar( S32 pos, BOOL group_with_next, llwchar wc) -		: LLTextCmd(pos, group_with_next), mChar(wc), mOldChar(0) {} +	TextCmdOverwriteChar( S32 pos, BOOL group_with_next, llwchar wc) +		: TextCmd(pos, group_with_next), mChar(wc), mOldChar(0) {} -	virtual BOOL execute( LLTextEditor* editor, S32* delta ) +	virtual BOOL execute( LLTextBase* editor, S32* delta )  	{  -		mOldChar = editor->getWChar(getPosition()); +		mOldChar = editor->getWText()[getPosition()];  		overwrite(editor, getPosition(), mChar);  		*delta = 0;  		return TRUE;  	}	 -	virtual S32 undo( LLTextEditor* editor ) +	virtual S32 undo( LLTextBase* editor )  	{  		overwrite(editor, getPosition(), mOldChar);  		return getPosition();  	} -	virtual S32 redo( LLTextEditor* editor ) +	virtual S32 redo( LLTextBase* editor )  	{  		overwrite(editor, getPosition(), mChar);  		return getPosition()+1; @@ -198,25 +198,26 @@ private:  /////////////////////////////////////////////////////////////////// -class LLTextEditor::LLTextCmdRemove : public LLTextEditor::LLTextCmd +class LLTextEditor::TextCmdRemove : public LLTextBase::TextCmd  {  public: -	LLTextCmdRemove( S32 pos, BOOL group_with_next, S32 len ) : -		LLTextCmd(pos, group_with_next), mLen(len) +	TextCmdRemove( S32 pos, BOOL group_with_next, S32 len, segment_vec_t& segments ) : +		TextCmd(pos, group_with_next), mLen(len)  	{ +		std::swap(mSegments, segments);  	} -	virtual BOOL execute( LLTextEditor* editor, S32* delta ) +	virtual BOOL execute( LLTextBase* editor, S32* delta )  	{  -		mWString = editor->getWSubString(getPosition(), mLen); +		mWString = editor->getWText().substr(getPosition(), mLen);  		*delta = remove(editor, getPosition(), mLen );  		return (*delta != 0);  	} -	virtual S32 undo( LLTextEditor* editor ) +	virtual S32 undo( LLTextBase* editor )  	{ -		insert(editor, getPosition(), mWString ); +		insert(editor, getPosition(), mWString);  		return getPosition() + mWString.length();  	} -	virtual S32 redo( LLTextEditor* editor ) +	virtual S32 redo( LLTextBase* editor )  	{  		remove(editor, getPosition(), mLen );  		return getPosition(); @@ -228,352 +229,107 @@ private:  /////////////////////////////////////////////////////////////////// -LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) -	:	LLUICtrl(p, LLTextViewModelPtr(new LLTextViewModel)), -	mMaxTextByteLength( p.max_text_length ), +LLTextEditor::Params::Params() +:	default_text("default_text"), +	prevalidate_callback("prevalidate_callback"), +	embedded_items("embedded_items", false), +	ignore_tab("ignore_tab", true), +	show_line_numbers("show_line_numbers", false), +	default_color("default_color"), +    commit_on_focus_lost("commit_on_focus_lost", false), +	show_context_menu("show_context_menu") +{ +	addSynonym(prevalidate_callback, "text_type"); +} + +LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) : +	LLTextBase(p),  	mBaseDocIsPristine(TRUE),  	mPristineCmd( NULL ),  	mLastCmd( NULL ), -	mCursorPos( 0 ), -	mIsSelecting( FALSE ), -	mSelectionStart( 0 ), -	mSelectionEnd( 0 ), -	mScrolledToBottom( TRUE ), -	mOnScrollEndCallback( NULL ), -	mOnScrollEndData( NULL ), -	mCursorColor(		p.cursor_color() ), -	mFgColor(			p.text_color() ),  	mDefaultColor(		p.default_color() ), -	mReadOnlyFgColor(	p.text_readonly_color() ), -	mWriteableBgColor(	p.bg_writeable_color() ), -	mReadOnlyBgColor(	p.bg_readonly_color() ), -	mFocusBgColor(		p.bg_focus_color() ), -	mReadOnly(p.read_only), -	mWordWrap( p.word_wrap ), -	mShowLineNumbers ( FALSE ), -	mCommitOnFocusLost( FALSE ), -	mHideScrollbarForShortDocs( FALSE ), -	mTakesNonScrollClicks( p.takes_non_scroll_clicks ), -	mTrackBottom( p.track_bottom ), -	mAllowEmbeddedItems( p.allow_embedded_items ), -	mAcceptCallingCardNames(FALSE), -	mHandleEditKeysDirectly( FALSE ), +	mShowLineNumbers ( p.show_line_numbers ), +	mCommitOnFocusLost( p.commit_on_focus_lost), +	mAllowEmbeddedItems( p.embedded_items ),  	mMouseDownX(0),  	mMouseDownY(0), -	mLastSelectionX(-1), -	mReflowNeeded(FALSE), -	mScrollNeeded(FALSE), -	mLastSelectionY(-1),  	mTabsToNextField(p.ignore_tab), -	mGLFont(p.font), -	mGLFontStyle(LLFontGL::getStyleFromString(p.font.style)) +	mPrevalidateFunc(p.prevalidate_callback()), +	mContextMenu(NULL), +	mShowContextMenu(p.show_context_menu)  { -	static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0); -  	mSourceID.generate(); -	// reset desired x cursor position -	mDesiredXPixel = -1; - -	updateTextRect(); - -	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(); -	LLScrollbar::Params sbparams; -	sbparams.name("Scrollbar"); -	sbparams.rect(scroll_rect); -	sbparams.orientation(LLScrollbar::VERTICAL); -	sbparams.doc_size(lines_in_doc); -	sbparams.doc_pos(0); -	sbparams.page_size(page_size); -	sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM); -	mScrollbar = LLUICtrlFactory::create<LLScrollbar> (sbparams); -	mScrollbar->setOnScrollEndCallback(mOnScrollEndCallback, mOnScrollEndData); -	addChild(mScrollbar); - -	static LLUICachedControl<S32> text_editor_border ("UITextEditorBorder", 0); +	//FIXME: use image?  	LLViewBorder::Params params; -	params.name("text ed border"); -	params.rect(getLocalRect()); -	params.bevel_type(LLViewBorder::BEVEL_IN); -	params.border_thickness(text_editor_border); +	params.name = "text ed border"; +	params.rect = getLocalRect(); +	params.bevel_style = LLViewBorder::BEVEL_IN; +	params.border_thickness = 1; +	params.visible = p.border_visible;  	mBorder = LLUICtrlFactory::create<LLViewBorder> (params);  	addChild( mBorder ); -	mBorder->setVisible(!p.hide_border); -	appendText(p.default_text, FALSE, FALSE); +	setText(p.default_text()); -	setHideScrollbarForShortDocs(p.hide_scrollbar); - -	mParseHTML=FALSE; -	mHTML.clear(); +	if (mShowLineNumbers) +	{ +		mHPad += UI_TEXTEDITOR_LINE_NUMBER_MARGIN; +		updateRects(); +	}  }  void LLTextEditor::initFromParams( const LLTextEditor::Params& p)  { -	resetDirty();		// Update saved text state -	LLUICtrl::initFromParams(p); -	// HACK: work around enabled == readonly design bug -- RN -	// setEnabled will modify our read only status, so do this after -	// LLUICtrl::initFromParams -	if (p.read_only.isProvided()) -	{ -		mReadOnly = p.read_only; -		updateSegments(); -		updateAllowingLanguageInput(); -	} +	LLTextBase::initFromParams(p); +  	// HACK:  text editors always need to be enabled so that we can scroll  	LLView::setEnabled(true); -} -LLTextEditor::~LLTextEditor() -{ -	gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() - -	// Route menu back to the default -	if( gEditMenuHandler == this ) +	if (p.commit_on_focus_lost.isProvided())  	{ -		gEditMenuHandler = NULL; +		mCommitOnFocusLost = p.commit_on_focus_lost;  	} - -	// Scrollbar is deleted by LLView -	mHoverSegment = NULL; -	std::for_each(mSegments.begin(), mSegments.end(), DeletePointer()); - -	std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer()); -} - -LLTextViewModel* LLTextEditor::getViewModel() const -{ -	return (LLTextViewModel*)mViewModel.get(); -} - -void LLTextEditor::setTrackColor( const LLColor4& color ) -{  -	mScrollbar->setTrackColor(color);  -} - -void LLTextEditor::setThumbColor( const LLColor4& color )  -{  -	mScrollbar->setThumbColor(color);  +	 +	updateAllowingLanguageInput();  } -void LLTextEditor::updateLineStartList(S32 startpos) +LLTextEditor::~LLTextEditor()  { -	updateSegments(); -	 -	bindEmbeddedChars(mGLFont); - -	S32 seg_num = mSegments.size(); -	S32 seg_idx = 0; -	S32 seg_offset = 0; - -	if (!mLineStartList.empty()) -	{ -		getSegmentAndOffset(startpos, &seg_idx, &seg_offset); -		line_info t(seg_idx, seg_offset); -		line_list_t::iterator iter = std::upper_bound(mLineStartList.begin(), mLineStartList.end(), t, line_info_compare()); -		if (iter != mLineStartList.begin()) --iter; -		seg_idx = iter->mSegment; -		seg_offset = iter->mOffset; -		mLineStartList.erase(iter, mLineStartList.end()); -	} - -    LLWString text(getWText()); -	while( seg_idx < seg_num ) -	{ -		mLineStartList.push_back(line_info(seg_idx,seg_offset)); -		BOOL line_ended = FALSE; -		S32 start_x = mShowLineNumbers ? UI_TEXTEDITOR_LINE_NUMBER_MARGIN : 0; -		S32 line_width = start_x; -		while(!line_ended && seg_idx < seg_num) -		{ -			LLTextSegment* segment = mSegments[seg_idx]; -			S32 start_idx = segment->getStart() + seg_offset; -			S32 end_idx = start_idx; -			while (end_idx < segment->getEnd() && text[end_idx] != '\n') -			{ -				end_idx++; -			} -			if (start_idx == end_idx) -			{ -				if (end_idx >= segment->getEnd()) -				{ -					// empty segment -					seg_idx++; -					seg_offset = 0; -				} -				else -				{ -					// empty line -					line_ended = TRUE; -					seg_offset++; -				} -			} -			else -			{  -				const llwchar* str = text.c_str() + start_idx; -				S32 drawn = mGLFont->maxDrawableChars(str, (F32)abs(mTextRect.getWidth()) - line_width, -													  end_idx - start_idx, mWordWrap, mAllowEmbeddedItems ); -				if( 0 == drawn && line_width == start_x) -				{ -					// If at the beginning of a line, draw at least one character, even if it doesn't all fit. -					drawn = 1; -				} -				seg_offset += drawn; -				line_width += mGLFont->getWidth(str, 0, drawn, mAllowEmbeddedItems); -				end_idx = segment->getStart() + seg_offset; -				if (end_idx < segment->getEnd()) -				{ -					line_ended = TRUE; -					if (text[end_idx] == '\n') -					{ -						seg_offset++; // skip newline -					} -				} -				else -				{ -					// finished with segment -					seg_idx++; -					seg_offset = 0; -				} -			} -		} -	} -	 -	unbindEmbeddedChars(mGLFont); +	gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() while LLTextEditor still valid -	mScrollbar->setDocSize( getLineCount() ); - -	if (mHideScrollbarForShortDocs) -	{ -		BOOL short_doc = (mScrollbar->getDocSize() <= mScrollbar->getPageSize()); -		mScrollbar->setVisible(!short_doc); -	} +	// Scrollbar is deleted by LLView +	std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer()); -	// if scrolled to bottom, stay at bottom -	// unless user is selecting text -	// do this after updating page size -	if (mScrolledToBottom && mTrackBottom && !hasMouseCapture()) -	{ -		endOfDoc(); -	} +	// context menu is owned by menu holder, not us +	//delete mContextMenu;  }  ////////////////////////////////////////////////////////////  // LLTextEditor  // Public methods -BOOL LLTextEditor::truncate() +void LLTextEditor::setText(const LLStringExplicit &utf8str, const LLStyle::Params& input_params)  { -	BOOL did_truncate = FALSE; - -	// First rough check - if we're less than 1/4th the size, we're OK -	if (getLength() >= S32(mMaxTextByteLength / 4)) -	{	 -		// Have to check actual byte size -        LLWString text(getWText()); -		S32 utf8_byte_size = wstring_utf8_length(text); -		if ( utf8_byte_size > mMaxTextByteLength ) +	// validate incoming text if necessary +	if (mPrevalidateFunc) +	{ +		LLWString test_text = utf8str_to_wstring(utf8str); +		if (!mPrevalidateFunc(test_text))  		{ -			// Truncate safely in UTF-8 -			std::string temp_utf8_text = wstring_to_utf8str(text); -			temp_utf8_text = utf8str_truncate( temp_utf8_text, mMaxTextByteLength ); -			getViewModel()->setDisplay(utf8str_to_wstring( temp_utf8_text )); -			did_truncate = TRUE; +			// not valid text, nothing to do +			return;  		}  	} -	return did_truncate; -} - -void LLTextEditor::setText(const LLStringExplicit &utf8str) -{ -	// LLStringUtil::removeCRLF(utf8str); -	mViewModel->setValue(utf8str_removeCRLF(utf8str)); - -	truncate();  	blockUndo(); - -	setCursorPos(0); -	deselect(); - -	needsReflow(); - -	resetDirty(); -} - -void LLTextEditor::setWText(const LLWString &wtext) -{ -	getViewModel()->setDisplay(wtext); - -	truncate(); -	blockUndo(); - -	setCursorPos(0);  	deselect(); -	needsReflow(); +	LLTextBase::setText(utf8str, input_params);  	resetDirty();  } -// virtual -void LLTextEditor::setValue(const LLSD& value) -{ -	setText(value.asString()); -} - -std::string LLTextEditor::getText() const -{ -	if (mAllowEmbeddedItems) -	{ -		llwarns << "getText() called on text with embedded items (not supported)" << llendl; -	} -	return mViewModel->getValue().asString(); -} - -void LLTextEditor::setWordWrap(BOOL b) -{ -	mWordWrap = b;  - -	setCursorPos(0); -	deselect(); -	 -	needsReflow(); -} - - -void LLTextEditor::setBorderVisible(BOOL b) -{ -	mBorder->setVisible(b); -} - -BOOL LLTextEditor::isBorderVisible() const  -{ -	return mBorder->getVisible(); -} - -void LLTextEditor::setHideScrollbarForShortDocs(BOOL b) -{ -	mHideScrollbarForShortDocs = b; - -	if (mHideScrollbarForShortDocs) -	{ -		BOOL short_doc = (mScrollbar->getDocSize() <= mScrollbar->getPageSize()); -		mScrollbar->setVisible(!short_doc); -	} -} -  void LLTextEditor::selectNext(const std::string& search_text_in, BOOL case_insensitive, BOOL wrap)  {  	if (search_text_in.empty()) @@ -596,7 +352,7 @@ void LLTextEditor::selectNext(const std::string& search_text_in, BOOL case_insen  		if (selected_text == search_text)  		{  			// We already have this word selected, we are searching for the next. -			mCursorPos += search_text.size(); +			setCursorPos(mCursorPos + search_text.size());  		}  	} @@ -659,9 +415,7 @@ BOOL LLTextEditor::replaceText(const std::string& search_text_in, const std::str  void LLTextEditor::replaceTextAll(const std::string& search_text, const std::string& replace_text, BOOL case_insensitive)  { -	S32 cur_pos = mScrollbar->getDocPos(); - -	setCursorPos(0); +	startOfDoc();  	selectNext(search_text, case_insensitive, FALSE);  	BOOL replaced = TRUE; @@ -669,14 +423,6 @@ void LLTextEditor::replaceTextAll(const std::string& search_text, const std::str  	{  		replaced = replaceText(search_text,replace_text, case_insensitive, FALSE);  	} - -	mScrollbar->setDocPos(cur_pos); -} - -// Picks a new cursor position based on the screen size of text being drawn. -void LLTextEditor::setCursorAtLocalPos( S32 local_x, S32 local_y, BOOL round ) -{ -	setCursorPos(getCursorPosFromLocalCoord(local_x, local_y, round));  }  S32 LLTextEditor::prevWordPos(S32 cursorPos) const @@ -686,7 +432,7 @@ S32 LLTextEditor::prevWordPos(S32 cursorPos) const  	{  		cursorPos--;  	} -	while( (cursorPos > 0) && isPartOfWord( wtext[cursorPos-1] ) ) +	while( (cursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[cursorPos-1] ) )  	{  		cursorPos--;  	} @@ -696,7 +442,7 @@ S32 LLTextEditor::prevWordPos(S32 cursorPos) const  S32 LLTextEditor::nextWordPos(S32 cursorPos) const  {  	LLWString wtext(getWText()); -	while( (cursorPos < getLength()) && isPartOfWord( wtext[cursorPos] ) ) +	while( (cursorPos < getLength()) && LLWStringUtil::isPartOfWord( wtext[cursorPos] ) )  	{  		cursorPos++;  	}  @@ -707,198 +453,49 @@ S32 LLTextEditor::nextWordPos(S32 cursorPos) const  	return cursorPos;  } -S32 LLTextEditor::getLineStart( S32 line ) const +const LLTextSegmentPtr	LLTextEditor::getPreviousSegment() const  { -	S32 num_lines = getLineCount(); -	if (num_lines == 0) -    { -		return 0; -    } - -	line = llclamp(line, 0, num_lines-1); -	S32 segidx = mLineStartList[line].mSegment; -	S32 segoffset = mLineStartList[line].mOffset; -	LLTextSegment* seg = mSegments[segidx]; -	S32 res = seg->getStart() + segoffset; -	if (res > seg->getEnd()) llerrs << "wtf" << llendl; -	return res; -} +	static LLPointer<LLIndexSegment> index_segment = new LLIndexSegment; -// Given an offset into text (pos), find the corresponding line (from the start of the doc) and an offset into the line. -void LLTextEditor::getLineAndOffset( S32 startpos, S32* linep, S32* offsetp ) const -{ -	if (mLineStartList.empty()) +	index_segment->setStart(mCursorPos); +	index_segment->setEnd(mCursorPos); + +	// find segment index at character to left of cursor (or rightmost edge of selection) +	segment_set_t::const_iterator it = mSegments.lower_bound(index_segment); + +	if (it != mSegments.end())  	{ -		*linep = 0; -		*offsetp = startpos; +		return *it;  	}  	else  	{ -		S32 seg_idx, seg_offset; -		getSegmentAndOffset( startpos, &seg_idx, &seg_offset ); - -		line_info tline(seg_idx, seg_offset); -		line_list_t::const_iterator iter = std::upper_bound(mLineStartList.begin(), mLineStartList.end(), tline, line_info_compare()); -		if (iter != mLineStartList.begin()) --iter; -		*linep = iter - mLineStartList.begin(); -		S32 line_start = mSegments[iter->mSegment]->getStart() + iter->mOffset; -		*offsetp = startpos - line_start; +		return LLTextSegmentPtr();  	}  } -void LLTextEditor::getSegmentAndOffset( S32 startpos, S32* segidxp, S32* offsetp ) const -{ -	if (mSegments.empty()) -	{ -		*segidxp = -1; -		*offsetp = startpos; -	} -	 -	LLTextSegment tseg(startpos); -	segment_list_t::const_iterator seg_iter; -	seg_iter = std::upper_bound(mSegments.begin(), mSegments.end(), &tseg, LLTextSegment::compare()); -	if (seg_iter != mSegments.begin()) --seg_iter; -	*segidxp = seg_iter - mSegments.begin(); -	*offsetp = startpos - (*seg_iter)->getStart(); -} - -const LLTextSegment*	LLTextEditor::getPreviousSegment() const -{ -	// find segment index at character to left of cursor (or rightmost edge of selection) -	S32 idx = llmax(0, getSegmentIdxAtOffset(mCursorPos) - 1); -	return idx >= 0 ? mSegments[idx] : NULL; -} - -void LLTextEditor::getSelectedSegments(std::vector<const LLTextSegment*>& segments) const +void LLTextEditor::getSelectedSegments(LLTextEditor::segment_vec_t& segments) const  {  	S32 left = hasSelection() ? llmin(mSelectionStart, mSelectionEnd) : mCursorPos;  	S32 right = hasSelection() ? llmax(mSelectionStart, mSelectionEnd) : mCursorPos; -	S32 first_idx = llmax(0, getSegmentIdxAtOffset(left)); -	S32 last_idx = llmax(0, first_idx, getSegmentIdxAtOffset(right)); -	for (S32 idx = first_idx; idx <= last_idx; ++idx) -	{ -		segments.push_back(mSegments[idx]); -	} +	return getSegmentsInRange(segments, left, right, true);  } -S32 LLTextEditor::getCursorPosFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const +void LLTextEditor::getSegmentsInRange(LLTextEditor::segment_vec_t& segments_out, S32 start, S32 end, bool include_partial) const  { -	if(mShowLineNumbers) -	{ -		local_x -= UI_TEXTEDITOR_LINE_NUMBER_MARGIN; -	} - -	// If round is true, if the position is on the right half of a character, the cursor -	// will be put to its right.  If round is false, the cursor will always be put to the -	// character's left. - -	// Figure out which line we're nearest to. -	S32 total_lines = getLineCount(); -	S32 line_height = llround( mGLFont->getLineHeight() ); -	S32 max_visible_lines = mTextRect.getHeight() / line_height; -	S32 scroll_lines = mScrollbar->getDocPos(); -	S32 visible_lines = llmin( total_lines - scroll_lines, max_visible_lines );			// Lines currently visible  - -	//S32 line = S32( 0.5f + ((mTextRect.mTop - local_y) / mGLFont->getLineHeight()) ); -	S32 line = (mTextRect.mTop - 1 - local_y) / line_height; -	if (line >= total_lines) -	{ -		return getLength(); // past the end -	} -	 -	line = llclamp( line, 0, visible_lines ) + scroll_lines; - -	S32 line_start = getLineStart(line); -	S32 next_start = getLineStart(line+1); -	S32	line_end = (next_start != line_start) ? next_start - 1 : getLength(); +	segment_set_t::const_iterator first_it = getSegIterContaining(start); +	segment_set_t::const_iterator end_it = getSegIterContaining(end - 1); +	if (end_it != mSegments.end()) ++end_it; -	if(line_start == -1) +	for (segment_set_t::const_iterator it = first_it; it != end_it; ++it)  	{ -		return 0; -	} -	else -	{ -		S32 line_len = line_end - line_start; -		S32 pos; -        LLWString text(getWText()); - -		if (mAllowEmbeddedItems) -		{ -			// Figure out which character we're nearest to. -			bindEmbeddedChars(mGLFont); -			pos = mGLFont->charFromPixelOffset(text.c_str(), line_start, -											   (F32)(local_x - mTextRect.mLeft), -											   (F32)(mTextRect.getWidth()), -											   line_len, -											   round, TRUE); -			unbindEmbeddedChars(mGLFont); -		} -		else +		LLTextSegmentPtr segment = *it; +		if (include_partial +			||	(segment->getStart() >= start +				&& segment->getEnd() <= end))  		{ -			pos = mGLFont->charFromPixelOffset(text.c_str(), line_start, -											   (F32)(local_x - mTextRect.mLeft), -											   (F32)mTextRect.getWidth(), -											   line_len, -											   round); +			segments_out.push_back(segment);  		} - -		return line_start + pos; -	} -} - -void LLTextEditor::setCursor(S32 row, S32 column) -{ -    LLWString text(getWText()); -	const llwchar* doc = text.c_str(); -	const char CR = 10; -	while(row--) -	{ -		while (CR != *doc++); -	} -	doc += column; -	setCursorPos(doc - text.c_str()); -} - -void LLTextEditor::setCursorPos(S32 offset) -{ -	mCursorPos = llclamp(offset, 0, (S32)getLength()); -	needsScroll(); -	// reset desired x cursor position -	mDesiredXPixel = -1; -} - -// virtual -BOOL LLTextEditor::canDeselect() const -{ -	return hasSelection();  -} - - -void LLTextEditor::deselect() -{ -	mSelectionStart = 0; -	mSelectionEnd = 0; -	mIsSelecting = FALSE; -} - - -void LLTextEditor::startSelection() -{ -	if( !mIsSelecting ) -	{ -		mIsSelecting = TRUE; -		mSelectionStart = mCursorPos; -		mSelectionEnd = mCursorPos; -	} -} - -void LLTextEditor::endSelection() -{ -	if( mIsSelecting ) -	{ -		mIsSelecting = FALSE; -		mSelectionEnd = mCursorPos;  	}  } @@ -1006,7 +603,7 @@ void LLTextEditor::indentSelectedLines( S32 spaces )  			}  			right += delta_spaces; -			//text = mWText; +			text = getWText();  			// Find the next new line  			while( (cur < right) && (text[cur] != '\n') ) @@ -1032,7 +629,7 @@ void LLTextEditor::indentSelectedLines( S32 spaces )  			mSelectionStart = right;  			mSelectionEnd = left;  		} -		mCursorPos = mSelectionEnd; +		setCursorPos(mSelectionEnd);  	}  } @@ -1047,64 +644,25 @@ void LLTextEditor::selectAll()  {  	mSelectionStart = getLength();  	mSelectionEnd = 0; -	mCursorPos = mSelectionEnd; +	setCursorPos(mSelectionEnd); +	updatePrimary();  } - -BOOL LLTextEditor::handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect_screen) +BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)  { -	for ( child_list_const_iter_t child_it = getChildList()->begin(); -		  child_it != getChildList()->end(); ++child_it) -	{ -		LLView* viewp = *child_it; -		S32 local_x = x - viewp->getRect().mLeft; -		S32 local_y = y - viewp->getRect().mBottom; -		if( viewp->handleToolTip(local_x, local_y, msg, sticky_rect_screen ) ) -		{ -			return TRUE; -		} -	} - -	if( mSegments.empty() ) -	{ -		return TRUE; -	} +	BOOL	handled = FALSE; -	const LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y ); -	if( cur_segment ) +	// set focus first, in case click callbacks want to change it +	// RN: do we really need to have a tab stop? +	if (hasTabStop())  	{ -		BOOL has_tool_tip = FALSE; -		has_tool_tip = cur_segment->getToolTip( msg ); - -		if( has_tool_tip ) -		{ -			// Just use a slop area around the cursor -			// Convert rect local to screen coordinates -			S32 SLOP = 8; -			localPointToScreen(  -				x - SLOP, y - SLOP,  -				&(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) ); -			sticky_rect_screen->mRight = sticky_rect_screen->mLeft + 2 * SLOP; -			sticky_rect_screen->mTop = sticky_rect_screen->mBottom + 2 * SLOP; -		} +		setFocus( TRUE );  	} -	return TRUE; -} - -BOOL LLTextEditor::handleScrollWheel(S32 x, S32 y, S32 clicks) -{ -	// Pretend the mouse is over the scrollbar -	return mScrollbar->handleScrollWheel( 0, 0, clicks ); -} - -BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) -{ -	BOOL	handled = FALSE;  	// Let scrollbar have first dibs -	handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL; +	handled = LLTextBase::handleMouseDown(x, y, mask); -	if( !handled && mTakesNonScrollClicks) +	if( !handled )  	{  		if (!(mask & MASK_SHIFT))  		{ @@ -1118,31 +676,10 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)  			if (mask & MASK_SHIFT)  			{  				S32 old_cursor_pos = mCursorPos; -				setCursorAtLocalPos( x, y, TRUE ); +				setCursorAtLocalPos( x, y, true );  				if (hasSelection())  				{ -					/* Mac-like behavior - extend selection towards the cursor -					if (mCursorPos < mSelectionStart -						&& mCursorPos < mSelectionEnd) -					{ -						// ...left of selection -						mSelectionStart = llmax(mSelectionStart, mSelectionEnd); -						mSelectionEnd = mCursorPos; -					} -					else if (mCursorPos > mSelectionStart -						&& mCursorPos > mSelectionEnd) -					{ -						// ...right of selection -						mSelectionStart = llmin(mSelectionStart, mSelectionEnd); -						mSelectionEnd = mCursorPos; -					} -					else -					{ -						mSelectionEnd = mCursorPos; -					} -					*/ -					// Windows behavior  					mSelectionEnd = mCursorPos;  				}  				else @@ -1155,7 +692,7 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)  			}  			else  			{ -				setCursorAtLocalPos( x, y, TRUE ); +				setCursorAtLocalPos( x, y, true );  				startSelection();  			}  			gFocusMgr.setMouseCapture( this ); @@ -1164,26 +701,46 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)  		handled = TRUE;  	} -	if (hasTabStop()) -	{ -		setFocus( TRUE ); -		handled = TRUE; -	} -  	// Delay cursor flashing -	resetKeystrokeTimer(); +	resetCursorBlink();  	return handled;  } +BOOL LLTextEditor::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ +	if (hasTabStop()) +	{ +		setFocus(TRUE); +	} +	// Prefer editor menu if it has selection. See EXT-6806. +	if (hasSelection() || !LLTextBase::handleRightMouseDown(x, y, mask)) +	{ +		if(getShowContextMenu()) +		{ +			showContextMenu(x, y); +		} +	} +	return TRUE; +} + +  BOOL LLTextEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask)  { -	setFocus( TRUE ); -	if( canPastePrimary() ) +	if (hasTabStop())  	{ -		setCursorAtLocalPos( x, y, TRUE ); -		pastePrimary(); +		setFocus(TRUE); +	} + +	if (!LLTextBase::handleMouseDown(x, y, mask)) +	{ +		if( canPastePrimary() ) +		{ +			setCursorAtLocalPos( x, y, true ); +			// does not rely on focus being set +			pastePrimary(); +		}  	}  	return TRUE;  } @@ -1191,34 +748,21 @@ BOOL LLTextEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask)  BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask)  { -	static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);  	BOOL handled = FALSE; -	mHoverSegment = NULL;  	if(hasMouseCapture() )  	{  		if( mIsSelecting )   		{ -			if (x != mLastSelectionX || y != mLastSelectionY) -			{ -				mLastSelectionX = x; -				mLastSelectionY = y; -			} - -			if( y > mTextRect.mTop ) -			{ -				mScrollbar->setDocPos( mScrollbar->getDocPos() - 1 ); +			if(mScroller) +			{	 +				mScroller->autoScroll(x, y);  			} -			else -			if( y < mTextRect.mBottom ) -			{ -				mScrollbar->setDocPos( mScrollbar->getDocPos() + 1 ); -			} - -			setCursorAtLocalPos( x, y, TRUE ); +			S32 clamped_x = llclamp(x, mVisibleTextRect.mLeft, mVisibleTextRect.mRight); +			S32 clamped_y = llclamp(y, mVisibleTextRect.mBottom, mVisibleTextRect.mTop); +			setCursorAtLocalPos( clamped_x, clamped_y, true );  			mSelectionEnd = mCursorPos;  		} -  		lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl;		  		getWindow()->setCursor(UI_CURSOR_IBEAM);  		handled = TRUE; @@ -1227,61 +771,21 @@ BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask)  	if( !handled )  	{  		// Pass to children -		handled = LLView::childrenHandleHover(x, y, mask) != NULL; +		handled = LLTextBase::handleHover(x, y, mask);  	}  	if( handled )  	{  		// Delay cursor flashing -		resetKeystrokeTimer(); +		resetCursorBlink();  	} -	// Opaque -	if( !handled && mTakesNonScrollClicks) +	if( !handled )  	{ -		// Check to see if we're over an HTML-style link -		if( !mSegments.empty() ) -		{ -			const LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y ); -			if( cur_segment ) -			{ -				if(cur_segment->getStyle()->isLink()) -				{ -					lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (over link, inactive)" << llendl;		 -					getWindow()->setCursor(UI_CURSOR_HAND); -					handled = TRUE; -				} -				else -				if(cur_segment->getStyle()->getIsEmbeddedItem()) -				{ -					lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (over embedded item, inactive)" << llendl;		 -					getWindow()->setCursor(UI_CURSOR_HAND); -					//getWindow()->setCursor(UI_CURSOR_ARROW); -					handled = TRUE; -				} -				mHoverSegment = cur_segment; -			} -		} - -		if( !handled ) -		{ -			lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl;		 -			if (!mScrollbar->getVisible() || x < getRect().getWidth() - scrollbar_size) -			{ -				getWindow()->setCursor(UI_CURSOR_IBEAM); -			} -			else -			{ -				getWindow()->setCursor(UI_CURSOR_ARROW); -			} -			handled = TRUE; -		} +		getWindow()->setCursor(UI_CURSOR_IBEAM); +		handled = TRUE;  	} -	if (mOnScrollEndCallback && mOnScrollEndData && (mScrollbar->getDocPos() == mScrollbar->getDocPosMax())) -	{ -		mOnScrollEndCallback(mOnScrollEndData); -	}  	return handled;  } @@ -1290,33 +794,27 @@ BOOL LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask)  {  	BOOL	handled = FALSE; -	// let scrollbar have first dibs -	handled = LLView::childrenHandleMouseUp(x, y, mask) != NULL; +	// if I'm not currently selecting text +	if (!(hasSelection() && hasMouseCapture())) +	{ +		// let text segments handle mouse event +		handled = LLTextBase::handleMouseUp(x, y, mask); +	} -	if( !handled && mTakesNonScrollClicks) +	if( !handled )  	{  		if( mIsSelecting )  		{ -			// Finish selection -			if( y > mTextRect.mTop ) +			if(mScroller)  			{ -				mScrollbar->setDocPos( mScrollbar->getDocPos() - 1 ); +				mScroller->autoScroll(x, y);  			} -			else -			if( y < mTextRect.mBottom ) -			{ -				mScrollbar->setDocPos( mScrollbar->getDocPos() + 1 ); -			} -			 -			setCursorAtLocalPos( x, y, TRUE ); +			S32 clamped_x = llclamp(x, mVisibleTextRect.mLeft, mVisibleTextRect.mRight); +			S32 clamped_y = llclamp(y, mVisibleTextRect.mBottom, mVisibleTextRect.mTop); +			setCursorAtLocalPos( clamped_x, clamped_y, true );  			endSelection();  		} -		if( !hasSelection() ) -		{ -			handleMouseUpOverSegment( x, y, mask ); -		} -  		// take selection to 'primary' clipboard  		updatePrimary(); @@ -1324,7 +822,7 @@ BOOL LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask)  	}  	// Delay cursor flashing -	resetKeystrokeTimer(); +	resetCursorBlink();  	if( hasMouseCapture()  )  	{ @@ -1341,28 +839,28 @@ BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask)  {  	BOOL	handled = FALSE; -	// let scrollbar have first dibs -	handled = LLView::childrenHandleDoubleClick(x, y, mask) != NULL; +	// let scrollbar and text segments have first dibs +	handled = LLTextBase::handleDoubleClick(x, y, mask); -	if( !handled && mTakesNonScrollClicks) +	if( !handled )  	{ -		setCursorAtLocalPos( x, y, FALSE ); +		setCursorAtLocalPos( x, y, false );  		deselect();  		LLWString text = getWText(); -		if( isPartOfWord( text[mCursorPos] ) ) +		if( LLWStringUtil::isPartOfWord( text[mCursorPos] ) )  		{  			// Select word the cursor is over -			while ((mCursorPos > 0) && isPartOfWord(text[mCursorPos-1])) +			while ((mCursorPos > 0) && LLWStringUtil::isPartOfWord(text[mCursorPos-1]))  			{ -				mCursorPos--; +				if (!setCursorPos(mCursorPos - 1)) break;  			}  			startSelection(); -			while ((mCursorPos < (S32)text.length()) && isPartOfWord( text[mCursorPos] ) ) +			while ((mCursorPos < (S32)text.length()) && LLWStringUtil::isPartOfWord( text[mCursorPos] ) )  			{ -				mCursorPos++; +				if (!setCursorPos(mCursorPos + 1)) break;  			}  			mSelectionEnd = mCursorPos; @@ -1371,7 +869,7 @@ BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask)  		{  			// Select the character the cursor is over  			startSelection(); -			mCursorPos++; +			setCursorPos(mCursorPos + 1);  			mSelectionEnd = mCursorPos;  		} @@ -1380,7 +878,7 @@ BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask)  		mIsSelecting = FALSE;    		// delay cursor flashing -		resetKeystrokeTimer(); +		resetCursorBlink();  		// take selection to 'primary' clipboard  		updatePrimary(); @@ -1392,38 +890,36 @@ BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask)  } -// Allow calling cards to be dropped onto text fields.  Append the name and -// a carriage return. -// virtual -BOOL LLTextEditor::handleDragAndDrop(S32 x, S32 y, MASK mask, -					  BOOL drop, EDragAndDropType cargo_type, void *cargo_data, -					  EAcceptance *accept, -					  std::string& tooltip_msg) -{ -	*accept = ACCEPT_NO; - -	return TRUE; -} -  //----------------------------------------------------------------------------  // Returns change in number of characters in mText -S32 LLTextEditor::execute( LLTextCmd* cmd ) +S32 LLTextEditor::execute( TextCmd* cmd )  {  	S32 delta = 0;  	if( cmd->execute(this, &delta) )  	{  		// Delete top of undo stack  		undo_stack_t::iterator enditer = std::find(mUndoStack.begin(), mUndoStack.end(), mLastCmd); -		if (enditer != mUndoStack.begin()) -		{ -			--enditer; -			std::for_each(mUndoStack.begin(), enditer, DeletePointer()); -			mUndoStack.erase(mUndoStack.begin(), enditer); -		} +		std::for_each(mUndoStack.begin(), enditer, DeletePointer()); +		mUndoStack.erase(mUndoStack.begin(), enditer);  		// Push the new command is now on the top (front) of the undo stack.  		mUndoStack.push_front(cmd);  		mLastCmd = cmd; + +		bool need_to_rollback = mPrevalidateFunc  +								&& !mPrevalidateFunc(getViewModel()->getDisplay()); +		if (need_to_rollback) +		{ +			// get rid of this last command and clean up undo stack +			undo(); + +			// remove any evidence of this command from redo history +			mUndoStack.pop_front(); +			delete cmd; + +			// failure, nothing changed +			delta = 0; +		}  	}  	else  	{ @@ -1434,19 +930,20 @@ S32 LLTextEditor::execute( LLTextCmd* cmd )  	return delta;  } -S32 LLTextEditor::insert(const S32 pos, const LLWString &wstr, const BOOL group_with_next_op) +S32 LLTextEditor::insert(S32 pos, const LLWString &wstr, bool group_with_next_op, LLTextSegmentPtr segment)  { -	return execute( new LLTextCmdInsert( pos, group_with_next_op, wstr ) ); +	return execute( new TextCmdInsert( pos, group_with_next_op, wstr, segment ) );  } -S32 LLTextEditor::remove(const S32 pos, const S32 length, const BOOL group_with_next_op) +S32 LLTextEditor::remove(S32 pos, S32 length, bool group_with_next_op)  { -	return execute( new LLTextCmdRemove( pos, group_with_next_op, length ) ); -} +	S32 end_pos = getEditableIndex(pos + length, true); -S32 LLTextEditor::append(const LLWString &wstr, const BOOL group_with_next_op) -{ -	return insert(getLength(), wstr, group_with_next_op); +	segment_vec_t segments_to_remove; +	// store text segments +	getSegmentsInRange(segments_to_remove, pos, pos + length, false); + +	return execute( new TextCmdRemove( pos, group_with_next_op, end_pos - pos, segments_to_remove ) );  }  S32 LLTextEditor::overwriteChar(S32 pos, llwchar wc) @@ -1457,7 +954,7 @@ S32 LLTextEditor::overwriteChar(S32 pos, llwchar wc)  	}  	else  	{ -		return execute(new LLTextCmdOverwriteChar(pos, FALSE, wc)); +		return execute(new TextCmdOverwriteChar(pos, FALSE, wc));  	}  } @@ -1477,8 +974,7 @@ void LLTextEditor::removeCharOrTab()  		if (text[mCursorPos - 1] == ' ')  		{  			// Try to remove a "tab" -			S32 line, offset; -			getLineAndOffset(mCursorPos, &line, &offset); +			S32 offset = getLineOffsetFromDocIndex(mCursorPos);  			if (offset > 0)  			{  				chars_to_remove = offset % SPACES_PER_TAB; @@ -1508,7 +1004,7 @@ void LLTextEditor::removeCharOrTab()  	}  	else  	{ -		reportBadKeystroke(); +		LLUI::reportBadKeystroke();  	}  } @@ -1531,14 +1027,14 @@ void LLTextEditor::removeChar()  	}  	else  	{ -		reportBadKeystroke(); +		LLUI::reportBadKeystroke();  	}  }  // Add a single character to the text  S32 LLTextEditor::addChar(S32 pos, llwchar wc)  { -	if ( (wstring_utf8_length( getWText() ) + wchar_utf8_length( wc ))  >= mMaxTextByteLength) +	if ( (wstring_utf8_length( getWText() ) + wchar_utf8_length( wc ))  > mMaxTextByteLength)  	{  		make_ui_sound("UISndBadKeystroke");  		return 0; @@ -1547,12 +1043,26 @@ S32 LLTextEditor::addChar(S32 pos, llwchar wc)  	if (mLastCmd && mLastCmd->canExtend(pos))  	{  		S32 delta = 0; +		if (mPrevalidateFunc) +		{ +			// get a copy of current text contents +			LLWString test_string(getViewModel()->getDisplay()); + +			// modify text contents as if this addChar succeeded +			llassert(pos <= (S32)test_string.size()); +			test_string.insert(pos, 1, wc); +			if (!mPrevalidateFunc( test_string)) +			{ +				return 0; +			} +		}  		mLastCmd->extendAndExecute(this, pos, wc, &delta); +  		return delta;  	}  	else  	{ -		return execute(new LLTextCmdAddChar(pos, FALSE, wc)); +		return execute(new TextCmdAddChar(pos, FALSE, wc, LLTextSegmentPtr()));  	}  } @@ -1573,6 +1083,28 @@ void LLTextEditor::addChar(llwchar wc)  	setCursorPos(mCursorPos + addChar( mCursorPos, wc ));  } +void LLTextEditor::addLineBreakChar() +{ +	if( !getEnabled() ) +	{ +		return; +	} +	if( hasSelection() ) +	{ +		deleteSelection(TRUE); +	} +	else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) +	{ +		removeChar(mCursorPos); +	} + +	LLStyleConstSP sp(new LLStyle(LLStyle::Params())); +	LLTextSegmentPtr segment = new LLLineBreakTextSegment(sp, mCursorPos); + +	S32 pos = execute(new TextCmdAddChar(mCursorPos, FALSE, '\n', segment)); +	 +	setCursorPos(mCursorPos + pos); +}  BOOL LLTextEditor::handleSelectionKey(const KEY key, const MASK mask) @@ -1589,10 +1121,10 @@ BOOL LLTextEditor::handleSelectionKey(const KEY key, const MASK mask)  			if( 0 < mCursorPos )  			{  				startSelection(); -				mCursorPos--; +				setCursorPos(mCursorPos - 1);  				if( mask & MASK_CONTROL )  				{ -					mCursorPos = prevWordPos(mCursorPos); +					setCursorPos(prevWordPos(mCursorPos));  				}  				mSelectionEnd = mCursorPos;  			} @@ -1602,10 +1134,10 @@ BOOL LLTextEditor::handleSelectionKey(const KEY key, const MASK mask)  			if( mCursorPos < getLength() )  			{  				startSelection(); -				mCursorPos++; +				setCursorPos(mCursorPos + 1);  				if( mask & MASK_CONTROL )  				{ -					mCursorPos = nextWordPos(mCursorPos); +					setCursorPos(nextWordPos(mCursorPos));  				}  				mSelectionEnd = mCursorPos;  			} @@ -1627,7 +1159,7 @@ BOOL LLTextEditor::handleSelectionKey(const KEY key, const MASK mask)  			startSelection();  			if( mask & MASK_CONTROL )  			{ -				mCursorPos = 0; +				setCursorPos(0);  			}  			else  			{ @@ -1652,7 +1184,7 @@ BOOL LLTextEditor::handleSelectionKey(const KEY key, const MASK mask)  			startSelection();  			if( mask & MASK_CONTROL )  			{ -				mCursorPos = getLength(); +				setCursorPos(getLength());  			}  			else  			{ @@ -1667,22 +1199,6 @@ BOOL LLTextEditor::handleSelectionKey(const KEY key, const MASK mask)  		}  	} -	if( !handled && mHandleEditKeysDirectly ) -	{ -		if( (MASK_CONTROL & mask) && ('A' == key) ) -		{ -			if( canSelectAll() ) -			{ -				selectAll(); -			} -			else -			{ -				reportBadKeystroke(); -			} -			handled = TRUE; -		} -	} -  	if( handled )  	{  		// take selection to 'primary' clipboard @@ -1703,14 +1219,7 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)  		switch( key )  		{  		case KEY_UP: -			if (mReadOnly) -			{ -				mScrollbar->setDocPos(mScrollbar->getDocPos() - 1); -			} -			else -			{ -				changeLine( -1 ); -			} +			changeLine( -1 );  			break;  		case KEY_PAGE_UP: @@ -1718,25 +1227,12 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)  			break;  		case KEY_HOME: -			if (mReadOnly) -			{ -				mScrollbar->setDocPos(0); -			} -			else -			{ -				startOfLine(); -			} +			startOfLine();  			break;  		case KEY_DOWN: -			if (mReadOnly) -			{ -				mScrollbar->setDocPos(mScrollbar->getDocPos() + 1); -			} -			else -			{ -				changeLine( 1 ); -			} +			changeLine( 1 ); +			deselect();  			break;  		case KEY_PAGE_DOWN: @@ -1744,24 +1240,13 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)  			break;  		case KEY_END: -			if (mReadOnly) -			{ -				mScrollbar->setDocPos(mScrollbar->getDocPosMax()); -			} -			else -			{ -				endOfLine(); -			} +			endOfLine();  			break;  		case KEY_LEFT: -			if (mReadOnly) -			{ -				break; -			}  			if( hasSelection() )  			{ -				setCursorPos(llmin( mCursorPos - 1, mSelectionStart, mSelectionEnd )); +				setCursorPos(llmin( mSelectionStart, mSelectionEnd ));  			}  			else  			{ @@ -1771,19 +1256,15 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)  				}  				else  				{ -					reportBadKeystroke(); +					LLUI::reportBadKeystroke();  				}  			}  			break;  		case KEY_RIGHT: -			if (mReadOnly) -			{ -				break; -			}  			if( hasSelection() )  			{ -				setCursorPos(llmax( mCursorPos + 1, mSelectionStart, mSelectionEnd )); +				setCursorPos(llmax( mSelectionStart, mSelectionEnd ));  			}  			else  			{ @@ -1793,7 +1274,7 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)  				}  				else  				{ -					reportBadKeystroke(); +					LLUI::reportBadKeystroke();  				}  			}	  			break; @@ -1804,10 +1285,11 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)  		}  	} -	if (mOnScrollEndCallback && mOnScrollEndData && (mScrollbar->getDocPos() == mScrollbar->getDocPosMax())) +	if (handled)  	{ -		mOnScrollEndCallback(mOnScrollEndData); +		deselect();  	} +	  	return handled;  } @@ -1843,7 +1325,7 @@ void LLTextEditor::cut()  	gClipboard.copyFromSubstring( getWText(), left_pos, length, mSourceID );  	deleteSelection( FALSE ); -	needsReflow(); +	onKeyStroke();  }  BOOL LLTextEditor::canCopy() const @@ -1932,7 +1414,7 @@ void LLTextEditor::pasteHelper(bool is_primary)  		for( S32 i = 0; i < len; i++ )  		{  			llwchar wc = clean_string[i]; -			if( (wc < LLFont::FIRST_CHAR) && (wc != LF) ) +			if( (wc < LLFontFreetype::FIRST_CHAR) && (wc != LF) )  			{  				clean_string[i] = LL_UNKNOWN_CHAR;  			} @@ -1944,10 +1426,30 @@ void LLTextEditor::pasteHelper(bool is_primary)  	}  	// Insert the new text into the existing text. -	setCursorPos(mCursorPos + insert(mCursorPos, clean_string, FALSE)); + +	//paste text with linebreaks. +	std::basic_string<llwchar>::size_type start = 0; +	std::basic_string<llwchar>::size_type pos = clean_string.find('\n',start); +	 +	while(pos!=-1) +	{ +		if(pos!=start) +		{ +			std::basic_string<llwchar> str = std::basic_string<llwchar>(clean_string,start,pos-start); +			setCursorPos(mCursorPos + insert(mCursorPos, str, FALSE, LLTextSegmentPtr())); +		} +		addLineBreakChar(); +		 +		start = pos+1; +		pos = clean_string.find('\n',start); +	} + +	std::basic_string<llwchar> str = std::basic_string<llwchar>(clean_string,start,clean_string.length()-start); +	setCursorPos(mCursorPos + insert(mCursorPos, str, FALSE, LLTextSegmentPtr())); +  	deselect(); -	needsReflow(); +	onKeyStroke();  } @@ -1991,7 +1493,7 @@ BOOL LLTextEditor::handleControlKey(const KEY key, const MASK mask)  			if( mask & MASK_SHIFT )  			{  				startSelection(); -				mCursorPos = 0; +				setCursorPos(0);  				mSelectionEnd = mCursorPos;  			}  			else @@ -1999,7 +1501,7 @@ BOOL LLTextEditor::handleControlKey(const KEY key, const MASK mask)  				// Ctrl-Home, Ctrl-Left, Ctrl-Right, Ctrl-Down  				// all move the cursor as if clicking, so should deselect.  				deselect(); -				setCursorPos(0); +				startOfDoc();  			}  			break; @@ -2060,75 +1562,13 @@ BOOL LLTextEditor::handleControlKey(const KEY key, const MASK mask)  	return handled;  } -BOOL LLTextEditor::handleEditKey(const KEY key, const MASK mask) -{ -	BOOL handled = FALSE; -	// Standard edit keys (Ctrl-X, Delete, etc,) are handled here instead of routed by the menu system. -	if( KEY_DELETE == key ) -	{ -		if( canDoDelete() ) -		{ -			doDelete(); -		} -		else -		{ -			reportBadKeystroke(); -		} -		handled = TRUE; -	} -	else -	if( MASK_CONTROL & mask ) +BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask)	  	{ -		if( 'C' == key ) -		{ -			if( canCopy() ) -			{ -				copy(); -			} -			else -			{ -				reportBadKeystroke(); -			} -			handled = TRUE; -		} -		else -		if( 'V' == key ) -		{ -			if( canPaste() ) -			{ -				paste(); -			} -			else -			{ -				reportBadKeystroke(); -			} -			handled = TRUE; -		} -		else -		if( 'X' == key ) -		{ -			if( canCut() ) -			{ -				cut(); -			} -			else -			{ -				reportBadKeystroke(); -			} -			handled = TRUE; -		} -	} - -	return handled; -} - -	 -BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask, BOOL* return_key_hit)	 -{ -	*return_key_hit = FALSE;  	BOOL handled = TRUE; +	if (mReadOnly) return FALSE; +  	switch( key )  	{  	case KEY_INSERT: @@ -2150,7 +1590,7 @@ BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask, BOOL* return  		}  		else  		{ -			reportBadKeystroke(); +			LLUI::reportBadKeystroke();  		}  		break; @@ -2188,8 +1628,7 @@ BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask, BOOL* return  				deleteSelection(FALSE);  			} -			S32 line, offset; -			getLineAndOffset( mCursorPos, &line, &offset ); +			S32 offset = getLineOffsetFromDocIndex(mCursorPos);  			S32 spaces_needed = SPACES_PER_TAB - (offset % SPACES_PER_TAB);  			for( S32 i=0; i < spaces_needed; i++ ) @@ -2204,6 +1643,10 @@ BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask, BOOL* return  		break;  	} +	if (handled) +	{ +		onKeyStroke(); +	}  	return handled;  } @@ -2224,88 +1667,32 @@ void LLTextEditor::unindentLineBeforeCloseBrace()  BOOL LLTextEditor::handleKeyHere(KEY key, MASK mask )  {  	BOOL	handled = FALSE; -	BOOL	selection_modified = FALSE; -	BOOL	return_key_hit = FALSE; -	BOOL	text_may_have_changed = TRUE; -	if ( gFocusMgr.getKeyboardFocus() == this ) +	// Special case for TAB.  If want to move to next field, report +	// not handled and let the parent take care of field movement. +	if (KEY_TAB == key && mTabsToNextField)  	{ -		// Special case for TAB.  If want to move to next field, report -		// not handled and let the parent take care of field movement. -		if (KEY_TAB == key && mTabsToNextField) -		{ -			return FALSE; -		} - -		handled = handleNavigationKey( key, mask ); -		if( handled ) -		{ -			text_may_have_changed = FALSE; -		} -			 -		if( !handled ) -		{ -			handled = handleSelectionKey( key, mask ); -			if( handled ) -			{ -				selection_modified = TRUE; -			} -		} -	 -		if( !handled ) -		{ -			handled = handleControlKey( key, mask ); -			if( handled ) -			{ -				selection_modified = TRUE; -			} -		} - -		if( !handled && mHandleEditKeysDirectly ) -		{ -			handled = handleEditKey( key, mask ); -			if( handled ) -			{ -				selection_modified = TRUE; -				text_may_have_changed = TRUE; -			} -		} - -		// Handle most keys only if the text editor is writeable. -		if( !mReadOnly ) -		{ -			if( !handled ) -			{ -				handled = handleSpecialKey( key, mask, &return_key_hit ); -				if( handled ) -				{ -					selection_modified = TRUE; -					text_may_have_changed = TRUE; -				} -			} - +		return FALSE; +	} +		 +	if (mReadOnly && mScroller) +	{ +		handled = (mScroller && mScroller->handleKeyHere( key, mask )) +				|| handleSelectionKey(key, mask) +				|| handleControlKey(key, mask);  		} - -		if( handled ) +		else   		{ -			resetKeystrokeTimer(); - -			// Most keystrokes will make the selection box go away, but not all will. -			if( !selection_modified && -				KEY_SHIFT != key && -				KEY_CONTROL != key && -				KEY_ALT != key && -				KEY_CAPSLOCK ) -			{ -				deselect(); -			} +		handled = handleNavigationKey( key, mask ) +				|| handleSelectionKey(key, mask) +				|| handleControlKey(key, mask) +				|| handleSpecialKey(key, mask); +	} -			if(text_may_have_changed) -			{ -				needsReflow(); -			} -			needsScroll(); -		} +	if( handled ) +	{ +		resetCursorBlink(); +		needsScroll();  	}  	return handled; @@ -2321,34 +1708,31 @@ BOOL LLTextEditor::handleUnicodeCharHere(llwchar uni_char)  	BOOL	handled = FALSE; -	if ( gFocusMgr.getKeyboardFocus() == this ) +	// Handle most keys only if the text editor is writeable. +	if( !mReadOnly )  	{ -		// Handle most keys only if the text editor is writeable. -		if( !mReadOnly ) +		if( '}' == uni_char )  		{ -			if( '}' == uni_char ) -			{ -				unindentLineBeforeCloseBrace(); -			} +			unindentLineBeforeCloseBrace(); +		} -			// TODO: KLW Add auto show of tool tip on ( -			addChar( uni_char ); +		// TODO: KLW Add auto show of tool tip on ( +		addChar( uni_char ); -			// Keys that add characters temporarily hide the cursor -			getWindow()->hideCursorUntilMouseMove(); +		// Keys that add characters temporarily hide the cursor +		getWindow()->hideCursorUntilMouseMove(); -			handled = TRUE; -		} +		handled = TRUE; +	} -		if( handled ) -		{ -			resetKeystrokeTimer(); +	if( handled ) +	{ +		resetCursorBlink(); -			// Most keystrokes will make the selection box go away, but not all will. -			deselect(); +		// Most keystrokes will make the selection box go away, but not all will. +		deselect(); -			needsReflow(); -		} +		onKeyStroke();  	}  	return handled; @@ -2380,8 +1764,7 @@ void LLTextEditor::doDelete()  		if( (text[ mCursorPos ] == ' ') && (mCursorPos + SPACES_PER_TAB < getLength()) )  		{  			// Try to remove a full tab's worth of spaces -			S32 line, offset; -			getLineAndOffset( mCursorPos, &line, &offset ); +			S32 offset = getLineOffsetFromDocIndex(mCursorPos);  			chars_to_remove = SPACES_PER_TAB - (offset % SPACES_PER_TAB);  			if( chars_to_remove == 0 )  			{ @@ -2403,9 +1786,10 @@ void LLTextEditor::doDelete()  			setCursorPos(mCursorPos + 1);  			removeChar();  		} +  	} -	needsReflow(); +	onKeyStroke();  }  //---------------------------------------------------------------------------- @@ -2448,7 +1832,7 @@ void LLTextEditor::undo()  		setCursorPos(pos); -	needsReflow(); +	onKeyStroke();  }  BOOL LLTextEditor::canRedo() const @@ -2490,12 +1874,12 @@ void LLTextEditor::redo()  		setCursorPos(pos); -	needsReflow(); +	onKeyStroke();  }  void LLTextEditor::onFocusReceived()  { -	LLUICtrl::onFocusReceived(); +	LLTextBase::onFocusReceived();  	updateAllowingLanguageInput();  } @@ -2518,323 +1902,57 @@ void LLTextEditor::onFocusLost()  	// Make sure cursor is shown again  	getWindow()->showCursorFromMouseMove(); -	LLUICtrl::onFocusLost(); +	LLTextBase::onFocusLost(); +} + +void LLTextEditor::onCommit() +{ +	setControlValue(getValue());  +	LLTextBase::onCommit();   }  void LLTextEditor::setEnabled(BOOL enabled)  {  	// just treat enabled as read-only flag -	BOOL read_only = !enabled; +	bool read_only = !enabled;  	if (read_only != mReadOnly)  	{ -		mReadOnly = read_only; +		//mReadOnly = read_only; +		LLTextBase::setReadOnly(read_only);  		updateSegments();  		updateAllowingLanguageInput();  	}  } -void LLTextEditor::drawBackground() -{ -	S32 left = 0; -	S32 top = getRect().getHeight(); -	S32 right = getRect().getWidth(); -	S32 bottom = 0; - -	LLColor4 bg_color = mReadOnly ? mReadOnlyBgColor.get() -		: gFocusMgr.getKeyboardFocus() == this ? mFocusBgColor.get() : mWriteableBgColor.get(); -	if( mShowLineNumbers ) { -		gl_rect_2d(left, top, UI_TEXTEDITOR_LINE_NUMBER_MARGIN, bottom, mReadOnlyBgColor.get() ); // line number area always read-only -		gl_rect_2d(UI_TEXTEDITOR_LINE_NUMBER_MARGIN, top, right, bottom, bg_color); // body text area to the right of line numbers -		gl_rect_2d(UI_TEXTEDITOR_LINE_NUMBER_MARGIN, top, UI_TEXTEDITOR_LINE_NUMBER_MARGIN-1, bottom, LLColor4::grey3); // separator -	} else { -		gl_rect_2d(left, top, right, bottom, bg_color); // body text area -	} - -	LLView::draw(); -} - -// Draws the black box behind the selected text -void LLTextEditor::drawSelectionBackground() +void LLTextEditor::showContextMenu(S32 x, S32 y)  { -	// Draw selection even if we don't have keyboard focus for search/replace -	if( hasSelection() ) +	if (!mContextMenu)  	{ -		LLWString text = getWText(); -		const S32 text_len = getLength(); -		std::queue<S32> line_endings; - -		S32 line_height = llround( mGLFont->getLineHeight() ); - -		S32 selection_left		= llmin( mSelectionStart, mSelectionEnd ); -		S32 selection_right		= llmax( mSelectionStart, mSelectionEnd ); -		S32 selection_left_x	= mTextRect.mLeft; -		S32 selection_left_y	= mTextRect.mTop - line_height; -		S32 selection_right_x	= mTextRect.mRight; -		S32 selection_right_y	= mTextRect.mBottom; - -		BOOL selection_left_visible = FALSE; -		BOOL selection_right_visible = FALSE; - -		// Skip through the lines we aren't drawing. -		S32 cur_line = mScrollbar->getDocPos(); - -		S32 left_line_num = cur_line; -		S32 num_lines = getLineCount(); -		S32 right_line_num = num_lines - 1; - -		S32 line_start = -1; -		if (cur_line >= num_lines) -		{ -			return; -		} - -		line_start = getLineStart(cur_line); - -		S32 left_visible_pos	= line_start; -		S32 right_visible_pos	= line_start; - -		S32 text_y = mTextRect.mTop - line_height; - -		// Find the coordinates of the selected area -		while((cur_line < num_lines)) -		{ -			S32 next_line = -1; -			S32 line_end = text_len; -			 -			if ((cur_line + 1) < num_lines) -			{ -				next_line = getLineStart(cur_line + 1); -				line_end = next_line; - -				line_end = ( (line_end - line_start)==0 || text[next_line-1] == '\n' || text[next_line-1] == '\0' || text[next_line-1] == ' ' || text[next_line-1] == '\t'  ) ? next_line-1 : next_line; -			} - -			const llwchar* line = text.c_str() + line_start; - -			if( line_start <= selection_left && selection_left <= line_end ) -			{ -				left_line_num = cur_line; -				selection_left_visible = TRUE; -				selection_left_x = mTextRect.mLeft + mGLFont->getWidth(line, 0, selection_left - line_start, mAllowEmbeddedItems); -				selection_left_y = text_y; -			} -			if( line_start <= selection_right && selection_right <= line_end ) -			{ -				right_line_num = cur_line; -				selection_right_visible = TRUE; -				selection_right_x = mTextRect.mLeft + mGLFont->getWidth(line, 0, selection_right - line_start, mAllowEmbeddedItems); -				if (selection_right == line_end) -				{ -					// add empty space for "newline" -					//selection_right_x += mGLFont->getWidth("n"); -				} -				selection_right_y = text_y; -			} -			 -			// if selection spans end of current line... -			if (selection_left <= line_end && line_end < selection_right && selection_left != selection_right) -			{ -				// extend selection slightly beyond end of line -				// to indicate selection of newline character (use "n" character to determine width) -				const LLWString nstr(utf8str_to_wstring(std::string("n"))); -				line_endings.push(mTextRect.mLeft + mGLFont->getWidth(line, 0, line_end - line_start, mAllowEmbeddedItems) + mGLFont->getWidth(nstr.c_str())); -			} -			 -			// move down one line -			text_y -= line_height; - -			right_visible_pos = line_end; -			line_start = next_line; -			cur_line++; - -			if (selection_right_visible) -			{ -				break; -			} -		} -		 -		// Draw the selection box (we're using a box instead of reversing the colors on the selected text). -		BOOL selection_visible = (left_visible_pos <= selection_right) && (selection_left <= right_visible_pos); -		if( selection_visible ) -		{ -			gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); -			const LLColor4& color = mReadOnly ? mReadOnlyBgColor.get() : mWriteableBgColor.get(); -			F32 alpha = hasFocus() ? 1.f : 0.5f; -			gGL.color4f( 1.f - color.mV[0], 1.f - color.mV[1], 1.f - color.mV[2], alpha ); -			S32 margin_offset = mShowLineNumbers ? UI_TEXTEDITOR_LINE_NUMBER_MARGIN : 0; - -			if( selection_left_y == selection_right_y ) -			{ -				// Draw from selection start to selection end -				gl_rect_2d( selection_left_x + margin_offset, selection_left_y + line_height + 1, -					selection_right_x + margin_offset, selection_right_y); -			} -			else -			{ -				// Draw from selection start to the end of the first line -				if( mTextRect.mRight == selection_left_x ) -				{ -					selection_left_x -= CURSOR_THICKNESS; -				} -				 -				S32 line_end = line_endings.front(); -				line_endings.pop(); -				gl_rect_2d( selection_left_x + margin_offset, selection_left_y + line_height + 1, -					line_end + margin_offset, selection_left_y ); - -				S32 line_num = left_line_num + 1; -				while(line_endings.size()) -				{ -					S32 vert_offset = -(line_num - left_line_num) * line_height; -					// Draw the block between the two lines -					gl_rect_2d( mTextRect.mLeft + margin_offset, selection_left_y + vert_offset + line_height + 1, -						line_endings.front() + margin_offset, selection_left_y + vert_offset); -					line_endings.pop(); -					line_num++; -				} - -				// Draw from the start of the last line to selection end -				if( mTextRect.mLeft == selection_right_x ) -				{ -					selection_right_x += CURSOR_THICKNESS; -				} -				gl_rect_2d( mTextRect.mLeft + margin_offset, selection_right_y + line_height + 1, -					selection_right_x + margin_offset, selection_right_y ); -			} -		} +		mContextMenu = LLUICtrlFactory::instance().createFromFile<LLContextMenu>("menu_text_editor.xml",  +																				LLMenuGL::sMenuContainer,  +																				LLMenuHolderGL::child_registry_t::instance());  	} -} - -void LLTextEditor::drawCursor() -{ -	if( gFocusMgr.getKeyboardFocus() == this -		&& gShowTextEditCursor && !mReadOnly) -	{ -		LLWString text = getWText(); -		const S32 text_len = getLength(); - -		// Skip through the lines we aren't drawing. -		S32 cur_pos = mScrollbar->getDocPos(); -		S32 num_lines = getLineCount(); -		if (cur_pos >= num_lines) -		{ -			return; -		} -		S32 line_start = getLineStart(cur_pos); - -		F32 line_height = mGLFont->getLineHeight(); -		F32 text_y = (F32)(mTextRect.mTop) - line_height; - -		F32 cursor_left = 0.f;  -		F32 next_char_left = 0.f; -		F32 cursor_bottom = 0.f; -		BOOL cursor_visible = FALSE; - -		S32 line_end = 0; -		// Determine if the cursor is visible and if so what its coordinates are. -		while( (mTextRect.mBottom <= llround(text_y)) && (cur_pos < num_lines)) -		{ -			line_end = text_len + 1; -			S32 next_line = -1; - -			if ((cur_pos + 1) < num_lines) -			{ -				next_line = getLineStart(cur_pos + 1); -				line_end = next_line - 1; -			} - -			const llwchar* line = text.c_str() + line_start; - -			// Find the cursor and selection bounds -			if( line_start <= mCursorPos && mCursorPos <= line_end ) -			{ -				cursor_visible = TRUE; -				next_char_left = (F32)mTextRect.mLeft + mGLFont->getWidthF32(line, 0, mCursorPos - line_start, mAllowEmbeddedItems ); -				cursor_left = next_char_left - 1.f; -				cursor_bottom = text_y; -				break; -			} - -			// move down one line -			text_y -= line_height; -			line_start = next_line; -			cur_pos++; -		} - -		if(mShowLineNumbers) -		{ -			cursor_left += UI_TEXTEDITOR_LINE_NUMBER_MARGIN; -		} - -		// Draw the cursor -		if( cursor_visible ) -		{ -			// (Flash the cursor every half second starting a fixed time after the last keystroke) -			F32 elapsed = mKeystrokeTimer.getElapsedTimeF32(); -			if( (elapsed < CURSOR_FLASH_DELAY ) || (S32(elapsed * 2) & 1) ) -			{ -				F32 cursor_top = cursor_bottom + line_height + 1.f; -				F32 cursor_right = cursor_left + (F32)CURSOR_THICKNESS; -				if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection()) -				{ -					cursor_left += CURSOR_THICKNESS; -					const LLWString space(utf8str_to_wstring(std::string(" "))); -					F32 spacew = mGLFont->getWidthF32(space.c_str()); -					if (mCursorPos == line_end) -					{ -						cursor_right = cursor_left + spacew; -					} -					else -					{ -						F32 width = mGLFont->getWidthF32(text.c_str(), mCursorPos, 1, mAllowEmbeddedItems); -						cursor_right = cursor_left + llmax(spacew, width); -					} -				} -				 -				gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); +	// Route menu to this class +	// previously this was done in ::handleRightMoseDown: +	//if(hasTabStop()) +	// setFocus(TRUE)  - why? weird... +	// and then inside setFocus +	// .... +	//    gEditMenuHandler = this; +	// .... +	// but this didn't work in all cases and just weird... +    //why not here?  +	// (all this was done for EXT-4443) -				gGL.color4fv( mCursorColor.get().mV ); -				 -				gl_rect_2d(llfloor(cursor_left), llfloor(cursor_top), -					llfloor(cursor_right), llfloor(cursor_bottom)); +	gEditMenuHandler = this; -				if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection() && text[mCursorPos] != '\n') -				{ -					const LLTextSegment* segmentp = getSegmentAtOffset(mCursorPos); -					LLColor4 text_color; -					if (segmentp) -					{ -						text_color = segmentp->getColor(); -					} -					else if (mReadOnly) -					{ -						text_color = mReadOnlyFgColor.get(); -					} -					else -					{ -						text_color = mFgColor.get(); -					} -					mGLFont->render(text, mCursorPos, next_char_left, cursor_bottom + line_height,  -						LLColor4(1.f - text_color.mV[VRED], 1.f - text_color.mV[VGREEN], 1.f - text_color.mV[VBLUE], 1.f), -						LLFontGL::LEFT, LLFontGL::TOP, -						LLFontGL::NORMAL, -						LLFontGL::NO_SHADOW, -						1); -				} - -				// Make sure the IME is in the right place -				LLRect screen_pos = calcScreenRect(); -				LLCoordGL ime_pos( screen_pos.mLeft + llfloor(cursor_left), screen_pos.mBottom + llfloor(cursor_top) ); - -				ime_pos.mX = (S32) (ime_pos.mX * LLUI::sGLScaleFactor.mV[VX]); -				ime_pos.mY = (S32) (ime_pos.mY * LLUI::sGLScaleFactor.mV[VY]); -				getWindow()->setLanguageTextInput( ime_pos ); -			} -		} -	} +	S32 screen_x, screen_y; +	localPointToScreen(x, y, &screen_x, &screen_y); +	mContextMenu->show(screen_x, screen_y);  } +  void LLTextEditor::drawPreeditMarker()  {  	static LLUICachedControl<F32> preedit_marker_brightness ("UIPreeditMarkerBrightness", 0); @@ -2856,17 +1974,17 @@ void LLTextEditor::drawPreeditMarker()  	const S32 text_len = getLength();  	const S32 num_lines = getLineCount(); -	S32 cur_line = mScrollbar->getDocPos(); +	S32 cur_line = getFirstVisibleLine();  	if (cur_line >= num_lines)  	{  		return;  	} -	const S32 line_height = llround( mGLFont->getLineHeight() ); +	const S32 line_height = llround( mDefaultFont->getLineHeight() );  	S32 line_start = getLineStart(cur_line); -	S32 line_y = mTextRect.mTop - line_height; -	while((mTextRect.mBottom <= line_y) && (num_lines > cur_line)) +	S32 line_y = mVisibleTextRect.mTop - line_height; +	while((mVisibleTextRect.mBottom <= line_y) && (num_lines > cur_line))  	{  		S32 next_start = -1;  		S32 line_end = text_len; @@ -2898,19 +2016,19 @@ void LLTextEditor::drawPreeditMarker()  					continue;  				} -				S32 preedit_left = mTextRect.mLeft; +				S32 preedit_left = mVisibleTextRect.mLeft;  				if (left > line_start)  				{ -					preedit_left += mGLFont->getWidth(text, line_start, left - line_start, mAllowEmbeddedItems); +					preedit_left += mDefaultFont->getWidth(text, line_start, left - line_start);  				} -				S32 preedit_right = mTextRect.mLeft; +				S32 preedit_right = mVisibleTextRect.mLeft;  				if (right < line_end)  				{ -					preedit_right += mGLFont->getWidth(text, line_start, right - line_start, mAllowEmbeddedItems); +					preedit_right += mDefaultFont->getWidth(text, line_start, right - line_start);  				}  				else  				{ -					preedit_right += mGLFont->getWidth(text, line_start, line_end - line_start, mAllowEmbeddedItems); +					preedit_right += mDefaultFont->getWidth(text, line_start, line_end - line_start);  				}  				if (mPreeditStandouts[i]) @@ -2940,251 +2058,85 @@ void LLTextEditor::drawPreeditMarker()  } -void LLTextEditor::drawText() +void LLTextEditor::drawLineNumbers()  { -	LLWString text = getWText(); -	const S32 text_len = getLength(); -	if( text_len <= 0 ) -	{ -		return; -	} -	S32 selection_left = -1; -	S32 selection_right = -1; -	// Draw selection even if we don't have keyboard focus for search/replace -	if( hasSelection()) -	{ -		selection_left = llmin( mSelectionStart, mSelectionEnd ); -		selection_right = llmax( mSelectionStart, mSelectionEnd ); -	} -  	LLGLSUIDefault gls_ui; - -	S32 cur_line = mScrollbar->getDocPos(); +	LLRect scrolled_view_rect = getVisibleDocumentRect(); +	LLRect content_rect = getVisibleTextRect();	 +	LLLocalClipRect clip(content_rect); +	S32 first_line = getFirstVisibleLine();  	S32 num_lines = getLineCount(); -	if (cur_line >= num_lines) +	if (first_line >= num_lines)  	{  		return;  	} -	S32 line_start = getLineStart(cur_line); -	LLTextSegment t(line_start); -	segment_list_t::iterator seg_iter; -	seg_iter = std::upper_bound(mSegments.begin(), mSegments.end(), &t, LLTextSegment::compare()); -	if (seg_iter == mSegments.end() || (*seg_iter)->getStart() > line_start) --seg_iter; -	LLTextSegment* cur_segment = *seg_iter; -	 -	S32 line_height = llround( mGLFont->getLineHeight() ); -	F32 text_y = (F32)(mTextRect.mTop - line_height); -	while((mTextRect.mBottom <= text_y) && (cur_line < num_lines)) +	S32 cursor_line = mLineInfoList[getLineNumFromDocIndex(mCursorPos)].mLineNum; + +	if (mShowLineNumbers)  	{ -		S32 next_start = -1; -		S32 line_end = text_len; +		S32 left = 0; +		S32 top = getRect().getHeight(); +		S32 bottom = 0; -		if ((cur_line + 1) < num_lines) -		{ -			next_start = getLineStart(cur_line + 1); -			line_end = next_start; -		} -		if ( text[line_end-1] == '\n' ) -		{ -			--line_end; -		} -		 -		F32 text_x = (F32)mTextRect.mLeft; +		gl_rect_2d(left, top, UI_TEXTEDITOR_LINE_NUMBER_MARGIN, bottom, mReadOnlyBgColor.get() ); // line number area always read-only +		gl_rect_2d(UI_TEXTEDITOR_LINE_NUMBER_MARGIN, top, UI_TEXTEDITOR_LINE_NUMBER_MARGIN-1, bottom, LLColor4::grey3); // separator + +		S32 last_line_num = -1; -		S32 seg_start = line_start; -		while( seg_start < line_end ) +		for (S32 cur_line = first_line; cur_line < num_lines; cur_line++)  		{ -			while( cur_segment->getEnd() <= seg_start ) +			line_info& line = mLineInfoList[cur_line]; + +			if ((line.mRect.mTop - scrolled_view_rect.mBottom) < mVisibleTextRect.mBottom)   			{ -				seg_iter++; -				if (seg_iter == mSegments.end()) -				{ -					llwarns << "Ran off the segmentation end!" << llendl; -					return; -				} -				cur_segment = *seg_iter; +				break;  			} -			 -			// Draw a segment within the line -			S32 clipped_end	=	llmin( line_end, cur_segment->getEnd() ); -			S32 clipped_len =	clipped_end - seg_start; -			if( clipped_len > 0 ) -			{ -				LLStyleSP style = cur_segment->getStyle(); -				if ( style->isImage() && (cur_segment->getStart() >= seg_start) && (cur_segment->getStart() <= clipped_end)) -				{ -					S32 style_image_height = style->mImageHeight; -					S32 style_image_width = style->mImageWidth; -					LLUIImagePtr image = style->getImage(); -					image->draw(llround(text_x), llround(text_y)+line_height-style_image_height,  -						style_image_width, style_image_height); -				} - -				if (cur_segment == mHoverSegment && style->getIsEmbeddedItem()) -				{ -					style->mUnderline = TRUE; -				} - -				S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); -				 -				if ( (mParseHTML) && (left_pos > seg_start) && (left_pos < clipped_end) &&  mIsSelecting && (mSelectionStart == mSelectionEnd) ) -				{ -					mHTML = style->getLinkHREF(); -				} -				drawClippedSegment( text, seg_start, clipped_end, text_x, text_y, selection_left, selection_right, style, &text_x ); - -				// Note: text_x is incremented by drawClippedSegment() -				seg_start += clipped_len; +			S32 line_bottom = line.mRect.mBottom - scrolled_view_rect.mBottom + mVisibleTextRect.mBottom; +			// draw the line numbers +			if(line.mLineNum != last_line_num && line.mRect.mTop <= scrolled_view_rect.mTop)  +			{ +				const LLFontGL *num_font = LLFontGL::getFontMonospace(); +				const LLWString ltext = utf8str_to_wstring(llformat("%d", line.mLineNum )); +				BOOL is_cur_line = cursor_line == line.mLineNum; +				const U8 style = is_cur_line ? LLFontGL::BOLD : LLFontGL::NORMAL; +				const LLColor4 fg_color = is_cur_line ? mCursorColor : mReadOnlyFgColor; +				num_font->render(  +					ltext, // string to draw +					0, // begin offset +					UI_TEXTEDITOR_LINE_NUMBER_MARGIN - 2, // x +					line_bottom, // y +					fg_color,  +					LLFontGL::RIGHT, // horizontal alignment +					LLFontGL::BOTTOM, // vertical alignment +					style, +					LLFontGL::NO_SHADOW, +					S32_MAX, // max chars +					UI_TEXTEDITOR_LINE_NUMBER_MARGIN - 2); // max pixels +				last_line_num = line.mLineNum;  			}  		} - -			// move down one line -			text_y -= (F32)line_height; - -		line_start = next_start; -		cur_line++;  	}  } - -// Draws a single text segment, reversing the color for selection if needed. -void LLTextEditor::drawClippedSegment(const LLWString &text, S32 seg_start, S32 seg_end, F32 x, F32 y, S32 selection_left, S32 selection_right, const LLStyleSP& style, F32* right_x ) -{ -	if (!style->isVisible()) -	{ -		return; -	} - -	const LLFontGL* font = mGLFont; - -	LLColor4 color = style->getColor(); - -	if ( style->getFontString()[0] ) -	{ -		font = style->getFont(); -	} - -	U8 font_flags = LLFontGL::NORMAL; -	 -	if (style->mBold) -	{ -		font_flags |= LLFontGL::BOLD; -	} -	if (style->mItalic) -	{ -		font_flags |= LLFontGL::ITALIC; -	} -	if (style->mUnderline) -	{ -		font_flags |= LLFontGL::UNDERLINE; -	} - -	if (style->getIsEmbeddedItem()) -	{ -		static LLUICachedControl<LLColor4> text_embedded_item_readonly_color ("TextEmbeddedItemReadOnlyColor", *(new LLColor4)); -		static LLUICachedControl<LLColor4> text_embedded_item_color ("TextEmbeddedItemColor", *(new LLColor4)); -		if (mReadOnly) -		{ -			color = text_embedded_item_readonly_color; -		} -		else -		{ -			color = text_embedded_item_color; -		} -	} - -	F32 y_top = y + (F32)llround(font->getLineHeight()); - -  	if( selection_left > seg_start ) -	{ -		// Draw normally -		S32 start = seg_start; -		S32 end = llmin( selection_left, seg_end ); -		S32 length =  end - start; -		font->render(text, start, x, y_top, color, LLFontGL::LEFT, LLFontGL::TOP, mGLFontStyle, LLFontGL::NO_SHADOW, length, S32_MAX, right_x, mAllowEmbeddedItems); -	} -	x = *right_x; -	 -	if( (selection_left < seg_end) && (selection_right > seg_start) ) -	{ -		// Draw reversed -		S32 start = llmax( selection_left, seg_start ); -		S32 end = llmin( selection_right, seg_end ); -		S32 length = end - start; - -		font->render(text, start, x, y_top, -					 LLColor4( 1.f - color.mV[0], 1.f - color.mV[1], 1.f - color.mV[2], 1.f ), -					 LLFontGL::LEFT, LLFontGL::TOP, mGLFontStyle, LLFontGL::NO_SHADOW, length, S32_MAX, right_x, mAllowEmbeddedItems); -	} -	x = *right_x; -	if( selection_right < seg_end ) -	{ -		// Draw normally -		S32 start = llmax( selection_right, seg_start ); -		S32 end = seg_end; -		S32 length = end - start; -		font->render(text, start, x, y_top, color, LLFontGL::LEFT, LLFontGL::TOP, mGLFontStyle, LLFontGL::NO_SHADOW, length, S32_MAX, right_x, mAllowEmbeddedItems); -	} - } - -  void LLTextEditor::draw()  { -	// do on-demand reflow  -	if (mReflowNeeded)  	{ -		updateLineStartList(); -		mReflowNeeded = FALSE; -	} - -	// then update scroll position, as cursor may have moved -	if (mScrollNeeded) -	{ -		updateScrollFromCursor(); -		mScrollNeeded = FALSE;  -	} - -	{ -		static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0); -		LLLocalClipRect clip(LLRect(0, getRect().getHeight(), getRect().getWidth() - (mScrollbar->getVisible() ? scrollbar_size : 0), 0)); - -		bindEmbeddedChars( mGLFont ); - -		drawBackground(); -		drawSelectionBackground(); +		// pad clipping rectangle so that cursor can draw at full width +		// when at left edge of mVisibleTextRect +		LLRect clip_rect(mVisibleTextRect); +		clip_rect.stretch(1); +		LLLocalClipRect clip(clip_rect);  		drawPreeditMarker(); -		drawText(); -		drawCursor(); - -		unbindEmbeddedChars( mGLFont ); - -		//RN: the decision was made to always show the orange border for keyboard focus but do not put an insertion caret -		// when in readonly mode -		mBorder->setKeyboardFocusHighlight( gFocusMgr.getKeyboardFocus() == this);// && !mReadOnly);  	} -	 -	LLView::draw();  // Draw children (scrollbar and border) - -	// remember if we are supposed to be at the bottom of the buffer -	mScrolledToBottom = isScrolledToBottom(); -} +	LLTextBase::draw(); +	drawLineNumbers(); -void LLTextEditor::onTabInto() -{ -	// selecting all on tabInto causes users to hit tab twice and replace their text with a tab character -	// theoretically, one could selectAll if mTabsToNextField is true, but we couldn't think of a use case -	// where you'd want to select all anyway -	// preserve insertion point when returning to the editor -	//selectAll(); -} - -// virtual -void LLTextEditor::clear() -{ -	setText(LLStringUtil::null); +	//RN: the decision was made to always show the orange border for keyboard focus but do not put an insertion caret +	// when in readonly mode +	mBorder->setKeyboardFocusHighlight( hasFocus() );// && !mReadOnly);  }  // Start or stop the editor from accepting text-editing keystrokes @@ -3202,7 +2154,7 @@ void LLTextEditor::setFocus( BOOL new_state )  		getWindow()->allowLanguageTextInput(this, FALSE);  	} -	LLUICtrl::setFocus( new_state ); +	LLTextBase::setFocus( new_state );  	if( new_state )  	{ @@ -3210,7 +2162,7 @@ void LLTextEditor::setFocus( BOOL new_state )  		gEditMenuHandler = this;  		// Don't start the cursor flashing right away -		resetKeystrokeTimer(); +		resetCursorBlink();  	}  	else  	{ @@ -3224,286 +2176,23 @@ void LLTextEditor::setFocus( BOOL new_state )  	}  } -// virtual -BOOL LLTextEditor::acceptsTextInput() const -{ -	return !mReadOnly; -} - -// Given a line (from the start of the doc) and an offset into the line, find the offset (pos) into text. -S32 LLTextEditor::getPos( S32 line, S32 offset ) -{ -	S32 line_start = getLineStart(line); -	S32 next_start = getLineStart(line+1); -	if (next_start == line_start) -	{ -		next_start = getLength() + 1; -	} -	S32 line_length = next_start - line_start - 1; -	line_length = llmax(line_length, 0); -	return line_start + llmin( offset, line_length ); -} - - -void LLTextEditor::changePage( S32 delta ) -{ -	S32 line, offset; -	getLineAndOffset( mCursorPos, &line, &offset ); - -	// get desired x position to remember previous position -	S32 desired_x_pixel = mDesiredXPixel; - -	// allow one line overlap -	S32 page_size = mScrollbar->getPageSize() - 1; -	if( delta == -1 ) -	{ -		line = llmax( line - page_size, 0); -		setCursorPos(getPos( line, offset )); -		mScrollbar->setDocPos( mScrollbar->getDocPos() - page_size ); -	} -	else -	if( delta == 1 ) -	{ -		setCursorPos(getPos( line + page_size, offset )); -		mScrollbar->setDocPos( mScrollbar->getDocPos() + page_size ); -	} - -	// put desired position into remember-buffer after setCursorPos() -	mDesiredXPixel = desired_x_pixel; - -	if (mOnScrollEndCallback && mOnScrollEndData && (mScrollbar->getDocPos() == mScrollbar->getDocPosMax())) -	{ -		mOnScrollEndCallback(mOnScrollEndData); -	} -} - -void LLTextEditor::changeLine( S32 delta ) -{ -	bindEmbeddedChars(mGLFont); - -	S32 line, offset; -	getLineAndOffset( mCursorPos, &line, &offset ); - -	S32  line_start = getLineStart(line); - -	// set desired x position to remembered previous position -	S32 desired_x_pixel = mDesiredXPixel; -	// if remembered position was reset (thus -1), calculate new one here -	if( desired_x_pixel == -1 ) -	{ -        LLWString text(getWText()); -		desired_x_pixel = mGLFont->getWidth(text.c_str(), line_start, offset, mAllowEmbeddedItems ); -	} - -	S32 new_line = 0; -	if( (delta < 0) && (line > 0 ) ) -	{ -		new_line = line - 1; -	} -	else -	if( (delta > 0) && (line < (getLineCount() - 1)) ) -	{ -		new_line = line + 1; -	} -	else -	{ -		unbindEmbeddedChars(mGLFont); -		return; -	} - -	S32 num_lines = getLineCount(); -	S32 new_line_start = getLineStart(new_line); -	S32 new_line_end = getLength(); -	if (new_line + 1 < num_lines) -	{ -		new_line_end = getLineStart(new_line + 1) - 1; -	} - -	S32 new_line_len = new_line_end - new_line_start; - -	S32 new_offset; -    LLWString text(getWText()); -	new_offset = mGLFont->charFromPixelOffset(text.c_str(), new_line_start, -											  (F32)desired_x_pixel, -											  (F32)mTextRect.getWidth(), -											  new_line_len, -											  mAllowEmbeddedItems); - -	setCursorPos (getPos( new_line, new_offset )); - -	// put desired position into remember-buffer after setCursorPos() -	mDesiredXPixel = desired_x_pixel; -	unbindEmbeddedChars(mGLFont); -} - -BOOL LLTextEditor::isScrolledToTop()  -{  -	return mScrollbar->isAtBeginning();  -} - -BOOL LLTextEditor::isScrolledToBottom()  -{  -	return mScrollbar->isAtEnd();  -} - - -void LLTextEditor::startOfLine() -{ -	S32 line, offset; -	getLineAndOffset( mCursorPos, &line, &offset ); -	setCursorPos(mCursorPos - offset); -} - -  // public  void LLTextEditor::setCursorAndScrollToEnd()  {  	deselect();  	endOfDoc(); -	needsScroll(); -} - -void LLTextEditor::getLineAndColumnForPosition( S32 position, S32* line, S32* col, BOOL include_wordwrap ) -{ -	if( include_wordwrap ) -	{ -		getLineAndOffset( mCursorPos, line, col ); -	} -	else -	{ -		LLWString text = getWText(); -		S32 line_count = 0; -		S32 line_start = 0; -		S32 i; -		for( i = 0; text[i] && (i < position); i++ ) -		{ -			if( '\n' == text[i] ) -			{ -				line_start = i + 1; -				line_count++; -			} -		} -		*line = line_count; -		*col = i - line_start; -	}  }  void LLTextEditor::getCurrentLineAndColumn( S32* line, S32* col, BOOL include_wordwrap )   {  -	getLineAndColumnForPosition(mCursorPos, line, col, include_wordwrap);  -} - -S32 LLTextEditor::getCurrentLine() -{ -	return getLineForPosition(mCursorPos); -} - -S32 LLTextEditor::getLineForPosition(S32 position) -{ -	S32 line, col; -	getLineAndColumnForPosition(position, &line, &col, FALSE); -	return line; -} - - -void LLTextEditor::endOfLine() -{ -	S32 line, offset; -	getLineAndOffset( mCursorPos, &line, &offset ); -	S32 num_lines = getLineCount(); -	if (line + 1 >= num_lines) -	{ -		setCursorPos(getLength()); -	} -	else -	{ -		setCursorPos( getLineStart(line + 1) - 1 ); -	} -} - -void LLTextEditor::endOfDoc() -{ -	mScrollbar->setDocPos(mScrollbar->getDocPosMax()); -	mScrolledToBottom = true; - -	S32 len = getLength(); -	if( len ) -	{ -		setCursorPos(len); -	} -	if (mOnScrollEndCallback && mOnScrollEndData && (mScrollbar->getDocPos() == mScrollbar->getDocPosMax())) -	{ -		mOnScrollEndCallback(mOnScrollEndData); -	} -} - -// Sets the scrollbar from the cursor position -void LLTextEditor::updateScrollFromCursor() -{ -	mScrollbar->setDocSize( getLineCount() ); - -	if (mReadOnly) -	{ -		// no cursor in read only mode -		return; -	} - -	S32 line, offset; -	getLineAndOffset( mCursorPos, &line, &offset );  - -	S32 page_size = mScrollbar->getPageSize(); - -	if( line < mScrollbar->getDocPos() ) -	{ -		// scroll so that the cursor is at the top of the page -		mScrollbar->setDocPos( line ); -	} -	else if( line >= mScrollbar->getDocPos() + page_size - 1 ) -	{ -		S32 new_pos = 0; -		if( line < mScrollbar->getDocSize() - 1 ) -		{ -			// scroll so that the cursor is one line above the bottom of the page, -			new_pos = line - page_size + 1; -		} -		else -		{ -			// if there is less than a page of text remaining, scroll so that the cursor is at the bottom -			new_pos = mScrollbar->getDocPosMax(); -		} -		mScrollbar->setDocPos( new_pos ); -	} - -	// Check if we've scrolled to bottom for callback if asked for callback -	if (mOnScrollEndCallback && mOnScrollEndData && (mScrollbar->getDocPos() == mScrollbar->getDocPosMax())) -	{ -		mOnScrollEndCallback(mOnScrollEndData); -	} -} - -void LLTextEditor::reshape(S32 width, S32 height, BOOL called_from_parent) -{ -	LLView::reshape( width, height, called_from_parent ); - -	// do this first after reshape, because other things depend on -	// up-to-date mTextRect -	updateTextRect(); -	 -	needsReflow(); - -	// propagate shape information to scrollbar -	mScrollbar->setDocSize( getLineCount() ); - -	S32 line_height = llround( mGLFont->getLineHeight() ); -	S32 page_lines = mTextRect.getHeight() / line_height; -	mScrollbar->setPageSize( page_lines ); +	*line = getLineNumFromDocIndex(mCursorPos, include_wordwrap); +	*col = getLineOffsetFromDocIndex(mCursorPos, include_wordwrap);  }  void LLTextEditor::autoIndent()  {  	// Count the number of spaces in the current line -	S32 line, offset; -	getLineAndOffset( mCursorPos, &line, &offset ); +	S32 line = getLineNumFromDocIndex(mCursorPos, false);  	S32 line_start = getLineStart(line);  	S32 space_count = 0;  	S32 i; @@ -3522,7 +2211,10 @@ void LLTextEditor::autoIndent()  	}  	// Insert that number of spaces on the new line -	addChar( '\n' ); + +	//appendLineBreakSegment(LLStyle::Params());//addChar( '\n' ); +	addLineBreakChar(); +  	for( i = 0; i < space_count; i++ )  	{  		addChar( ' ' ); @@ -3541,129 +2233,14 @@ void LLTextEditor::insertText(const std::string &new_text)  		deleteSelection(TRUE);  	} -	setCursorPos(mCursorPos + insert( mCursorPos, utf8str_to_wstring(new_text), FALSE )); +	setCursorPos(mCursorPos + insert( mCursorPos, utf8str_to_wstring(new_text), FALSE, LLTextSegmentPtr() )); -	needsReflow(); -  	setEnabled( enabled );  } - -void LLTextEditor::appendColoredText(const std::string &new_text,  -									 bool allow_undo,  -									 bool prepend_newline, -									 const LLColor4 &color, -									 const std::string& font_name) -{ -	LLColor4 lcolor=color; -	if (mParseHighlights) -	{ -		LLTextParser* highlight = LLTextParser::getInstance(); -		highlight->parseFullLineHighlights(new_text, &lcolor); -	} -	 -	LLStyleSP style(new LLStyle); -	style->setVisible(true); -	style->setColor(lcolor); -	style->setFontName(font_name); -	appendStyledText(new_text, allow_undo, prepend_newline, style); -} - -void LLTextEditor::appendStyledText(const std::string &new_text,  -									 bool allow_undo,  -									 bool prepend_newline, -									 LLStyleSP stylep) -{ -	S32 part = (S32)LLTextParser::WHOLE; -	if(mParseHTML) -	{ - -		S32 start=0,end=0; -		std::string text = new_text; -		while ( findHTML(text, &start, &end) ) -		{ -			LLStyleSP html(new LLStyle); -			html->setVisible(true); -			html->setColor(mLinkColor); -			if (stylep) -			{ -				html->setFontName(stylep->getFontString()); -			} -			html->mUnderline = TRUE; - -			if (start > 0) -			{ -				if (part == (S32)LLTextParser::WHOLE || -					part == (S32)LLTextParser::START) -				{ -					part = (S32)LLTextParser::START; -				} -				else -				{ -					part = (S32)LLTextParser::MIDDLE; -				} -				std::string subtext=text.substr(0,start); -				appendHighlightedText(subtext,allow_undo, prepend_newline, part, stylep);  -			} -			 -			html->setLinkHREF(text.substr(start,end-start)); -			appendText(text.substr(start, end-start),allow_undo, prepend_newline, html); -			if (end < (S32)text.length())  -			{ -				text = text.substr(end,text.length() - end); -				end=0; -				part=(S32)LLTextParser::END; -			} -			else -			{ -				break; -			} -		} -		if (part != (S32)LLTextParser::WHOLE) part=(S32)LLTextParser::END; -		if (end < (S32)text.length()) appendHighlightedText(text,allow_undo, prepend_newline, part, stylep);		 -	} -	else -	{ -		appendHighlightedText(new_text, allow_undo, prepend_newline, part, stylep); -	} -} - -void LLTextEditor::appendHighlightedText(const std::string &new_text,  -										 bool allow_undo,  -										 bool prepend_newline, -										 S32  highlight_part, -										 LLStyleSP stylep) -{ -	if (mParseHighlights)  -	{ -		LLTextParser* highlight = LLTextParser::getInstance(); -		 -		if (highlight && stylep) -		{ -			LLSD pieces = highlight->parsePartialLineHighlights(new_text, stylep->getColor(), highlight_part); -			bool lprepend=prepend_newline; -			for (S32 i=0;i<pieces.size();i++) -			{ -				LLSD color_llsd = pieces[i]["color"]; -				LLColor4 lcolor; -				lcolor.setValue(color_llsd); -				LLStyleSP lstylep(new LLStyle(*stylep)); -				lstylep->setColor(lcolor); -				if (i != 0 && (pieces.size() > 1) ) lprepend=FALSE; -				appendText((std::string)pieces[i]["text"], allow_undo, lprepend, lstylep); -			} -			return; -		} -	} -	appendText(new_text, allow_undo, prepend_newline, stylep); -} - -// Appends new text to end of document -void LLTextEditor::appendText(const std::string &new_text, bool allow_undo, bool prepend_newline, -							  const LLStyleSP stylep) +void LLTextEditor::appendWidget(const LLInlineViewSegment::Params& params, const std::string& text, bool allow_undo)  {  	// Save old state -	BOOL was_scrolled_to_bottom = (mScrollbar->getDocPos() == mScrollbar->getDocPosMax());  	S32 selection_start = mSelectionStart;  	S32 selection_end = mSelectionEnd;  	BOOL was_selecting = mIsSelecting; @@ -3675,50 +2252,17 @@ void LLTextEditor::appendText(const std::string &new_text, bool allow_undo, bool  	setCursorPos(old_length); -	// Add carriage return if not first line -	if (getLength() != 0 -		&& prepend_newline) -	{ -		std::string final_text = "\n"; -		final_text += new_text; -		append(utf8str_to_wstring(final_text), TRUE); -	} -	else -	{ -		append(utf8str_to_wstring(new_text), TRUE ); -	} +	LLWString widget_wide_text = utf8str_to_wstring(text); + +	LLTextSegmentPtr segment = new LLInlineViewSegment(params, old_length, old_length + widget_wide_text.size()); +	insert(getLength(), widget_wide_text, FALSE, segment); -	if (stylep) -	{ -		S32 segment_start = old_length; -		S32 segment_end = getLength(); -		LLTextSegment* segment = new LLTextSegment(stylep, segment_start, segment_end ); -		mSegments.push_back(segment); -	} -	 -	needsReflow(); -	  	// Set the cursor and scroll position -	// Maintain the scroll position unless the scroll was at the end of the doc (in which  -	// case, move it to the new end of the doc) or unless the user was doing actively selecting -	if( was_scrolled_to_bottom && !was_selecting ) -	{ -		if( selection_start != selection_end ) -		{ -			// maintain an existing non-active selection -			mSelectionStart = selection_start; -			mSelectionEnd = selection_end; -		}	 -		endOfDoc(); -	} -	else if( selection_start != selection_end ) +	if( selection_start != selection_end )  	{  		mSelectionStart = selection_start;  		mSelectionEnd = selection_end; - - -  		mIsSelecting = was_selecting;  		setCursorPos(cursor_pos);  	} @@ -3731,7 +2275,7 @@ void LLTextEditor::appendText(const std::string &new_text, bool allow_undo, bool  		setCursorPos(cursor_pos);  	} -	if( !allow_undo ) +	if (!allow_undo)  	{  		blockUndo();  	} @@ -3744,59 +2288,13 @@ void LLTextEditor::removeTextFromEnd(S32 num_chars)  	remove(getLength() - num_chars, num_chars, FALSE);  	S32 len = getLength(); -	mCursorPos = llclamp(mCursorPos, 0, len); +	setCursorPos (llclamp(mCursorPos, 0, len));  	mSelectionStart = llclamp(mSelectionStart, 0, len);  	mSelectionEnd = llclamp(mSelectionEnd, 0, len); -	pruneSegments(); -	 -	// pruneSegments will invalidate mLineStartList. -	updateLineStartList();  	needsScroll();  } -/////////////////////////////////////////////////////////////////// -// Returns change in number of characters in mWText - -S32 LLTextEditor::insertStringNoUndo(const S32 pos, const LLWString &wstr) -{ -    LLWString text(getWText()); -	S32 old_len = text.length();		// length() returns character length -	S32 insert_len = wstr.length(); - -	text.insert(pos, wstr); -    getViewModel()->setDisplay(text); - -	if ( truncate() ) -	{ -		// The user's not getting everything he's hoping for -		make_ui_sound("UISndBadKeystroke"); -		insert_len = getLength() - old_len; -	} - -	return insert_len; -} - -S32 LLTextEditor::removeStringNoUndo(S32 pos, S32 length) -{ -    LLWString text(getWText()); -	text.erase(pos, length); -    getViewModel()->setDisplay(text); -	return -length;	// This will be wrong if someone calls removeStringNoUndo with an excessive length -} - -S32 LLTextEditor::overwriteCharNoUndo(S32 pos, llwchar wc) -{ -	if (pos > (S32)getLength()) -	{ -		return 0; -	} -    LLWString text(getWText()); -	text[pos] = wc; -    getViewModel()->setDisplay(text); -	return 1; -} -  //----------------------------------------------------------------------------  void LLTextEditor::makePristine() @@ -3852,33 +2350,19 @@ BOOL LLTextEditor::tryToRevertToPristineState()  				i--;  			}  		} - -		needsReflow();  	}  	return isPristine(); // TRUE => success  } -void LLTextEditor::updateTextRect() -{ -	static LLUICachedControl<S32> texteditor_border ("UITextEditorBorder", 0); -	static LLUICachedControl<S32> texteditor_h_pad ("UITextEditorHPad", 0); -	static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0); -	static LLUICachedControl<S32> texteditor_vpad_top ("UITextEditorVPadTop", 0); - -	mTextRect.setOriginAndSize(  -		texteditor_border + texteditor_h_pad, -		texteditor_border,  -		getRect().getWidth() - scrollbar_size - 2 * (texteditor_border + texteditor_h_pad), -		getRect().getHeight() - 2 * texteditor_border - texteditor_vpad_top ); -} - +static LLFastTimer::DeclareTimer FTM_SYNTAX_HIGHLIGHTING("Syntax Highlighting");  void LLTextEditor::loadKeywords(const std::string& filename,  								const std::vector<std::string>& funcs,  								const std::vector<std::string>& tooltips,  								const LLColor3& color)  { +	LLFastTimer ft(FTM_SYNTAX_HIGHLIGHTING);  	if(mKeywords.loadFromFile(filename))  	{  		S32 count = llmin(funcs.size(), tooltips.size()); @@ -3887,199 +2371,73 @@ void LLTextEditor::loadKeywords(const std::string& filename,  			std::string name = utf8str_trim(funcs[i]);  			mKeywords.addToken(LLKeywordToken::WORD, name, color, tooltips[i] );  		} +		segment_vec_t segment_list; +		mKeywords.findSegments(&segment_list, getWText(), mDefaultColor.get(), *this); -		mKeywords.findSegments( &mSegments, getWText(), mDefaultColor.get() ); - -		llassert( mSegments.front()->getStart() == 0 ); -		llassert( mSegments.back()->getEnd() == getLength() ); +		mSegments.clear(); +		segment_set_t::iterator insert_it = mSegments.begin(); +		for (segment_vec_t::iterator list_it = segment_list.begin(); list_it != segment_list.end(); ++list_it) +		{ +			insert_it = mSegments.insert(insert_it, *list_it); +		}  	}  }  void LLTextEditor::updateSegments()  { -	if (mKeywords.isLoaded()) +	if (mReflowIndex < S32_MAX && mKeywords.isLoaded())  	{ +		LLFastTimer ft(FTM_SYNTAX_HIGHLIGHTING);  		// HACK:  No non-ascii keywords for now -		mKeywords.findSegments(&mSegments, getWText(), mDefaultColor.get()); -	} -	else if (mAllowEmbeddedItems) -	{ -		findEmbeddedItemSegments(); -	} - -	// Make sure we have at least one segment -	if (mSegments.size() == 1 && mSegments[0]->getIsDefault()) -	{ -		delete mSegments[0]; -		mSegments.clear(); // create default segment -	} -	if (mSegments.empty()) -	{ -		LLColor4 text_color = ( mReadOnly ? mReadOnlyFgColor.get() : mFgColor.get() ); -		LLTextSegment* default_segment = new LLTextSegment( text_color, 0, getLength() ); -		default_segment->setIsDefault(TRUE); -		mSegments.push_back(default_segment); -	} -} +		segment_vec_t segment_list; +		mKeywords.findSegments(&segment_list, getWText(), mDefaultColor.get(), *this); -// Only effective if text was removed from the end of the editor -// *NOTE: Using this will invalidate references to mSegments from mLineStartList. -void LLTextEditor::pruneSegments() -{ -	S32 len = getLength(); -	// Find and update the first valid segment -	segment_list_t::iterator iter = mSegments.end(); -	while(iter != mSegments.begin()) -	{ -		--iter; -		LLTextSegment* seg = *iter; -		if (seg->getStart() < len) +		clearSegments(); +		segment_set_t::iterator insert_it = mSegments.begin(); +		for (segment_vec_t::iterator list_it = segment_list.begin(); list_it != segment_list.end(); ++list_it)  		{ -			// valid segment -			if (seg->getEnd() > len) -			{ -				seg->setEnd(len); -			} -			break; // done -		}			 -	} -	if (iter != mSegments.end()) -	{ -		// erase invalid segments -		++iter; -		std::for_each(iter, mSegments.end(), DeletePointer()); -		mSegments.erase(iter, mSegments.end()); -	} -	else -	{ -		llwarns << "Tried to erase end of empty LLTextEditor" << llendl; -	} -} - -void LLTextEditor::findEmbeddedItemSegments() -{ -	mHoverSegment = NULL; -	std::for_each(mSegments.begin(), mSegments.end(), DeletePointer()); -	mSegments.clear(); - -	BOOL found_embedded_items = FALSE; -	LLWString text = getWText(); -	S32 idx = 0; -	while( text[idx] ) -	{ -		if( text[idx] >= FIRST_EMBEDDED_CHAR && text[idx] <= LAST_EMBEDDED_CHAR ) - 		{ -			found_embedded_items = TRUE; -			break; +			insertSegment(*list_it);  		} -		++idx;  	} -	if( !found_embedded_items ) -	{ -		return; -	} - -	S32 text_len = text.length(); - -	BOOL in_text = FALSE; - -	LLColor4 text_color = ( mReadOnly ? mReadOnlyFgColor.get() : mFgColor.get()  ); - -	if( idx > 0 ) -	{ -		mSegments.push_back( new LLTextSegment( text_color, 0, text_len ) ); // text -		in_text = TRUE; -	} - -	LLStyleSP embedded_style(new LLStyle); -	embedded_style->setIsEmbeddedItem( TRUE ); - -	// Start with i just after the first embedded item -	while ( text[idx] ) -	{ -		if( text[idx] >= FIRST_EMBEDDED_CHAR && text[idx] <= LAST_EMBEDDED_CHAR ) -		{ -			if( in_text ) -			{ -				mSegments.back()->setEnd( idx ); -			} -			mSegments.push_back( new LLTextSegment( embedded_style, idx, idx + 1 ) );  // item -			in_text = FALSE; -		} -		else -		if( !in_text ) -		{ -			mSegments.push_back( new LLTextSegment( text_color, idx, text_len ) );  // text -			in_text = TRUE; -		} -		++idx; -	} +	LLTextBase::updateSegments();  } -BOOL LLTextEditor::handleMouseUpOverSegment(S32 x, S32 y, MASK mask) +void LLTextEditor::updateLinkSegments()  { -	if ( hasMouseCapture() ) +	LLWString wtext = getWText(); + +	// update any segments that contain a link +	for (segment_set_t::iterator it = mSegments.begin(); it != mSegments.end(); ++it)  	{ -		// This mouse up was part of a click. -		// Regardless of where the cursor is, see if we recently touched a link -		// and launch it if we did. -		if (mParseHTML && mHTML.length() > 0) +		LLTextSegment *segment = *it; +		if (segment && segment->getStyle() && segment->getStyle()->isLink())  		{ -				//Special handling for slurls -			if ( (mSecondlifeURLcallback!=NULL) && !(*mSecondlifeURLcallback)(mHTML) ) +			// if the link's label (what the user can edit) is a valid Url, +			// then update the link's HREF to be the same as the label text. +			// This lets users edit Urls in-place. +			LLStyleConstSP style = segment->getStyle(); +			LLStyleSP new_style(new LLStyle(*style)); +			LLWString url_label = wtext.substr(segment->getStart(), segment->getEnd()-segment->getStart()); +			if (LLUrlRegistry::instance().hasUrl(url_label))  			{ -				if (mURLcallback!=NULL) (*mURLcallback)(mHTML); +				std::string new_url = wstring_to_utf8str(url_label); +				LLStringUtil::trim(new_url); +				new_style->setLinkHREF(new_url); +				LLStyleConstSP sp(new_style); +				segment->setStyle(sp);  			} -			mHTML.clear();  		}  	} - -	return FALSE;  } -// Finds the text segment (if any) at the give local screen position -const LLTextSegment* LLTextEditor::getSegmentAtLocalPos( S32 x, S32 y ) const -{ -	// Find the cursor position at the requested local screen position -	S32 offset = getCursorPosFromLocalCoord( x, y, FALSE ); -	S32 idx = getSegmentIdxAtOffset(offset); -	return idx >= 0 ? mSegments[idx] : NULL; -} - -const LLTextSegment* LLTextEditor::getSegmentAtOffset(S32 offset) const -{ -	S32 idx = getSegmentIdxAtOffset(offset); -	return idx >= 0 ? mSegments[idx] : NULL; -} - -S32 LLTextEditor::getSegmentIdxAtOffset(S32 offset) const -{ -	if (mSegments.empty() || offset < 0 || offset >= getLength()) -	{ -		return -1; -	} -	else -	{ -		S32 segidx, segoff; -		getSegmentAndOffset(offset, &segidx, &segoff); -		return segidx; -	} -}  void LLTextEditor::onMouseCaptureLost()  {  	endSelection();  } -void LLTextEditor::setOnScrollEndCallback(void (*callback)(void*), void* userdata) -{ -	mOnScrollEndCallback = callback; -	mOnScrollEndData = userdata; -	mScrollbar->setOnScrollEndCallback(callback, userdata); -} -  ///////////////////////////////////////////////////////////////////  // Hack for Notecards @@ -4163,10 +2521,9 @@ BOOL LLTextEditor::importBuffer(const char* buffer, S32 length )  	delete[] text; -	setCursorPos(0); +	startOfDoc();  	deselect(); -	needsReflow();  	return success;  } @@ -4184,237 +2541,6 @@ BOOL LLTextEditor::exportBuffer(std::string &buffer )  	return TRUE;  } -////////////////////////////////////////////////////////////////////////// -// LLTextSegment - -LLTextSegment::LLTextSegment(S32 start) : -	mStart(start), -	mEnd(0), -	mToken(NULL), -	mIsDefault(FALSE) -{ -}  -LLTextSegment::LLTextSegment( const LLStyleSP& style, S32 start, S32 end ) : -	mStyle( style ), -	mStart( start), -	mEnd( end ), -	mToken(NULL), -	mIsDefault(FALSE) -{ -} -LLTextSegment::LLTextSegment( const LLColor4& color, S32 start, S32 end, BOOL is_visible) : -	mStyle(new LLStyle(is_visible,color,LLStringUtil::null)), -	mStart( start), -	mEnd( end ), -	mToken(NULL), -	mIsDefault(FALSE) -{ -} -LLTextSegment::LLTextSegment( const LLColor4& color, S32 start, S32 end ) : -	mStyle(new LLStyle(TRUE, color,LLStringUtil::null )), -	mStart( start), -	mEnd( end ), -	mToken(NULL), -	mIsDefault(FALSE) -{ -} -LLTextSegment::LLTextSegment( const LLColor3& color, S32 start, S32 end ) : -	mStyle(new LLStyle(TRUE, color,LLStringUtil::null )), -	mStart( start), -	mEnd( end ), -	mToken(NULL), -	mIsDefault(FALSE) -{ -} - -BOOL LLTextSegment::getToolTip(std::string& msg) const -{ -	if (mToken && !mToken->getToolTip().empty()) -	{ -		const LLWString& wmsg = mToken->getToolTip(); -		msg = wstring_to_utf8str(wmsg); -		return TRUE; -	} -	return FALSE; -} - - - -void LLTextSegment::dump() const -{ -	llinfos << "Segment [" <<  -//			mColor.mV[VX] << ", " << -//			mColor.mV[VY] << ", " << -//			mColor.mV[VZ] << "]\t[" << -		mStart << ", " << -		getEnd() << "]" << -		llendl; - -} - -/////////////////////////////////////////////////////////////////// -// Refactoring note: We may eventually want to replace this with boost::regex or  -// boost::tokenizer capabilities since we've already fixed at least two JIRAs -// concerning logic issues associated with this function. -S32 LLTextEditor::findHTMLToken(const std::string &line, S32 pos, BOOL reverse) const -{ -	std::string openers=" \t\n('\"[{<>"; -	std::string closers=" \t\n)'\"]}><;"; - -	if (reverse) -	{ -		for (int index=pos; index >= 0; index--) -		{ -			char c = line[index]; -			S32 m2 = openers.find(c); -			if (m2 >= 0) -			{ -				return index+1; -			} -		} -		return 0; // index is -1, don't want to return that.  -	}  -	else -	{ -		// adjust the search slightly, to allow matching parenthesis inside the URL -		S32 paren_count = 0; -		for (int index=pos; index<(S32)line.length(); index++) -		{ -			char c = line[index]; - -			if (c == '(') -			{ -				paren_count++; -			} -			else if (c == ')') -			{ -				if (paren_count <= 0) -				{ -					return index; -				} -				else -				{ -					paren_count--; -				} -			} -			else -			{ -				S32 m2 = closers.find(c); -				if (m2 >= 0) -				{ -					return index; -				} -			} -		}  -		return line.length(); -	}		 -} - -BOOL LLTextEditor::findHTML(const std::string &line, S32 *begin, S32 *end) const -{ -	   -	S32 m1,m2,m3; -	BOOL matched = FALSE; -	 -	m1=line.find("://",*end); -	 -	if (m1 >= 0) //Easy match. -	{ -		*begin = findHTMLToken(line, m1, TRUE); -		*end   = findHTMLToken(line, m1, FALSE); -		 -		//Load_url only handles http and https so don't hilite ftp, smb, etc. -		m2 = line.substr(*begin,(m1 - *begin)).find("http"); -		m3 = line.substr(*begin,(m1 - *begin)).find("secondlife"); -	 -		std::string badneighbors=".,<>?';\"][}{=-+_)(*&^%$#@!~`\t\r\n\\"; -	 -		if (m2 >= 0 || m3>=0) -		{ -			S32 bn = badneighbors.find(line.substr(m1+3,1)); -			 -			if (bn < 0) -			{ -				matched = TRUE; -			} -		} -	} -/*	matches things like secondlife.com (no http://) needs a whitelist to really be effective. -	else	//Harder match. -	{ -		m1 = line.find(".",*end); -		 -		if (m1 >= 0) -		{ -			*end   = findHTMLToken(line, m1, FALSE); -			*begin = findHTMLToken(line, m1, TRUE); -			 -			m1 = line.rfind(".",*end); - -			if ( ( *end - m1 ) > 2 && m1 > *begin) -			{ -				std::string badneighbors=".,<>/?';\"][}{=-+_)(*&^%$#@!~`"; -				m2 = badneighbors.find(line.substr(m1+1,1)); -				m3 = badneighbors.find(line.substr(m1-1,1)); -				if (m3<0 && m2<0) -				{ -					matched = TRUE; -				} -			} -		} -	} -	*/ -	 -	if (matched) -	{ -		S32 strpos, strpos2; - -		std::string url     = line.substr(*begin,*end - *begin); -		std::string slurlID = "slurl.com/secondlife/"; -		strpos = url.find(slurlID); -		 -		if (strpos < 0) -		{ -			slurlID="secondlife://"; -			strpos = url.find(slurlID); -		} -	 -		if (strpos < 0) -		{ -			slurlID="sl://"; -			strpos = url.find(slurlID); -		} -	 -		if (strpos >= 0)  -		{ -			strpos+=slurlID.length(); -			 -			while ( ( strpos2=url.find("/",strpos) ) == -1 )  -			{ -				if ((*end+2) >= (S32)line.length() || line.substr(*end,1) != " " ) -				{ -					matched=FALSE; -					break; -				} -				 -				strpos = (*end + 1) - *begin; -								 -				*end = findHTMLToken(line,(*begin + strpos),FALSE); -				url = line.substr(*begin,*end - *begin); -			} -		} - -	} -	 -	if (!matched) -	{ -		*begin=*end=0; -	} -	return matched; -} - - -  void LLTextEditor::updateAllowingLanguageInput()  {  	LLWindow* window = getWindow(); @@ -4450,7 +2576,7 @@ void LLTextEditor::resetPreedit()  			deselect();  		} -		mCursorPos = mPreeditPositions.front(); +		setCursorPos(mPreeditPositions.front());  		removeStringNoUndo(mCursorPos, mPreeditPositions.back() - mCursorPos);  		insertStringNoUndo(mCursorPos, mPreeditOverwrittenWString); @@ -4490,7 +2616,7 @@ void LLTextEditor::updatePreedit(const LLWString &preedit_string,  	if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())  	{ -		mPreeditOverwrittenWString = getWSubString(insert_preedit_at, mPreeditWString.length()); +		mPreeditOverwrittenWString = getWText().substr(insert_preedit_at, mPreeditWString.length());  		removeStringNoUndo(insert_preedit_at, mPreeditWString.length());  	}  	else @@ -4501,11 +2627,12 @@ void LLTextEditor::updatePreedit(const LLWString &preedit_string,  	mPreeditStandouts = preedit_standouts; -	needsReflow();  	setCursorPos(insert_preedit_at + caret_position);  	// Update of the preedit should be caused by some key strokes. -	mKeystrokeTimer.reset(); +	resetCursorBlink(); + +	onKeyStroke();  }  BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const @@ -4513,7 +2640,7 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect  	if (control)  	{  		LLRect control_rect_screen; -		localRectToScreen(mTextRect, &control_rect_screen); +		localRectToScreen(mVisibleTextRect, &control_rect_screen);  		LLUI::screenRectToGL(control_rect_screen, control);  	} @@ -4534,7 +2661,7 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect  		return FALSE;  	} -	const S32 first_visible_line = mScrollbar->getDocPos(); +	const S32 first_visible_line = getFirstVisibleLine();  	if (query < getLineStart(first_visible_line))  	{  		return FALSE; @@ -4560,12 +2687,12 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect      const LLWString textString(getWText());  	const llwchar * const text = textString.c_str(); -	const S32 line_height = llround(mGLFont->getLineHeight()); +	const S32 line_height = llround(mDefaultFont->getLineHeight());  	if (coord)  	{ -		const S32 query_x = mTextRect.mLeft + mGLFont->getWidth(text, current_line_start, query - current_line_start, mAllowEmbeddedItems); -		const S32 query_y = mTextRect.mTop - (current_line - first_visible_line) * line_height - line_height / 2; +		const S32 query_x = mVisibleTextRect.mLeft + mDefaultFont->getWidth(text, current_line_start, query - current_line_start); +		const S32 query_y = mVisibleTextRect.mTop - (current_line - first_visible_line) * line_height - line_height / 2;  		S32 query_screen_x, query_screen_y;  		localPointToScreen(query_x, query_y, &query_screen_x, &query_screen_y);  		LLUI::screenPointToGL(query_screen_x, query_screen_y, &coord->mX, &coord->mY); @@ -4573,23 +2700,23 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect  	if (bounds)  	{ -		S32 preedit_left = mTextRect.mLeft; +		S32 preedit_left = mVisibleTextRect.mLeft;  		if (preedit_left_position > current_line_start)  		{ -			preedit_left += mGLFont->getWidth(text, current_line_start, preedit_left_position - current_line_start, mAllowEmbeddedItems); +			preedit_left += mDefaultFont->getWidth(text, current_line_start, preedit_left_position - current_line_start);  		} -		S32 preedit_right = mTextRect.mLeft; +		S32 preedit_right = mVisibleTextRect.mLeft;  		if (preedit_right_position < current_line_end)  		{ -			preedit_right += mGLFont->getWidth(text, current_line_start, preedit_right_position - current_line_start, mAllowEmbeddedItems); +			preedit_right += mDefaultFont->getWidth(text, current_line_start, preedit_right_position - current_line_start);  		}  		else  		{ -			preedit_right += mGLFont->getWidth(text, current_line_start, current_line_end - current_line_start, mAllowEmbeddedItems); +			preedit_right += mDefaultFont->getWidth(text, current_line_start, current_line_end - current_line_start);  		} -		const S32 preedit_top = mTextRect.mTop - (current_line - first_visible_line) * line_height; +		const S32 preedit_top = mVisibleTextRect.mTop - (current_line - first_visible_line) * line_height;  		const S32 preedit_bottom = preedit_top - line_height;  		const LLRect preedit_rect_local(preedit_left, preedit_top, preedit_right, preedit_bottom); @@ -4663,10 +2790,39 @@ void LLTextEditor::markAsPreedit(S32 position, S32 length)  S32 LLTextEditor::getPreeditFontSize() const  { -	return llround(mGLFont->getLineHeight() * LLUI::sGLScaleFactor.mV[VY]); +	return llround(mDefaultFont->getLineHeight() * LLUI::sGLScaleFactor.mV[VY]);  } -LLWString       LLTextEditor::getWText() const +BOOL LLTextEditor::isDirty() const +{ +	if(mReadOnly) +	{ +		return FALSE; +	} + +	if( mPristineCmd ) +	{ +		return ( mPristineCmd == mLastCmd ); +	} +	else +	{ +		return ( NULL != mLastCmd ); +	} +} + +void LLTextEditor::setKeystrokeCallback(const keystroke_signal_t::slot_type& callback) +{ +	mKeystrokeSignal.connect(callback); +} + +void LLTextEditor::onKeyStroke() +{ +	mKeystrokeSignal(this); +} + +//virtual +void LLTextEditor::clear()  { -    return getViewModel()->getDisplay(); +	getViewModel()->setDisplay(LLWStringUtil::null); +	clearSegments();  }  | 
