summaryrefslogtreecommitdiff
path: root/indra/llui/lltexteditor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llui/lltexteditor.cpp')
-rwxr-xr-x[-rw-r--r--]indra/llui/lltexteditor.cpp1054
1 files changed, 518 insertions, 536 deletions
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp
index 7c925b8899..926326aaff 100644..100755
--- 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.
*
- * 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 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.
*
- * 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.
+ * 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.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * 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
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -34,6 +27,7 @@
#include "linden_common.h"
+#define LLTEXTEDITOR_CPP
#include "lltexteditor.h"
#include "llfontfreetype.h" // for LLFontFreetype::FIRST_CHAR
@@ -60,9 +54,11 @@
#include "llwindow.h"
#include "lltextparser.h"
#include "llscrollcontainer.h"
+#include "llspellcheck.h"
#include "llpanel.h"
#include "llurlregistry.h"
#include "lltooltip.h"
+#include "llmenugl.h"
#include <queue>
#include "llcombobox.h"
@@ -72,12 +68,15 @@
//
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 S32 SPACES_PER_TAB = 4;
+const F32 SPELLCHECK_DELAY = 0.5f; // delay between the last keypress and spell checking the word the cursor is on
///////////////////////////////////////////////////////////////////
@@ -232,30 +231,38 @@ private:
///////////////////////////////////////////////////////////////////
LLTextEditor::Params::Params()
: default_text("default_text"),
+ prevalidate_callback("prevalidate_callback"),
embedded_items("embedded_items", false),
ignore_tab("ignore_tab", true),
- handle_edit_keys_directly("handle_edit_keys_directly", false),
- show_line_numbers("show_line_numbers", false),
+ auto_indent("auto_indent", true),
default_color("default_color"),
- commit_on_focus_lost("commit_on_focus_lost", false)
-{}
+ commit_on_focus_lost("commit_on_focus_lost", false),
+ show_context_menu("show_context_menu"),
+ enable_tooltip_paste("enable_tooltip_paste")
+{
+ addSynonym(prevalidate_callback, "text_type");
+}
LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) :
LLTextBase(p),
+ mAutoreplaceCallback(),
mBaseDocIsPristine(TRUE),
mPristineCmd( NULL ),
mLastCmd( NULL ),
- mDefaultColor( p.default_color() ),
- mShowLineNumbers ( p.show_line_numbers ),
+ mDefaultColor( p.default_color() ),
+ mAutoIndent(p.auto_indent),
mCommitOnFocusLost( p.commit_on_focus_lost),
mAllowEmbeddedItems( p.embedded_items ),
- mHandleEditKeysDirectly( p.handle_edit_keys_directly ),
mMouseDownX(0),
mMouseDownY(0),
- mTabsToNextField(p.ignore_tab)
+ mTabsToNextField(p.ignore_tab),
+ mPrevalidateFunc(p.prevalidate_callback()),
+ mContextMenu(NULL),
+ mShowContextMenu(p.show_context_menu),
+ mEnableTooltipPaste(p.enable_tooltip_paste),
+ mPassDelete(FALSE),
+ mKeepSelectionOnReturn(false)
{
- mDefaultFont = p.font;
-
mSourceID.generate();
//FIXME: use image?
@@ -267,20 +274,18 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) :
params.visible = p.border_visible;
mBorder = LLUICtrlFactory::create<LLViewBorder> (params);
addChild( mBorder );
-
setText(p.default_text());
-
- if (mShowLineNumbers)
- {
- mHPad += UI_TEXTEDITOR_LINE_NUMBER_MARGIN;
- updateTextRect();
- }
+
+ mParseOnTheFly = TRUE;
}
void LLTextEditor::initFromParams( const LLTextEditor::Params& p)
{
LLTextBase::initFromParams(p);
+ // HACK: text editors always need to be enabled so that we can scroll
+ LLView::setEnabled(true);
+
if (p.commit_on_focus_lost.isProvided())
{
mCommitOnFocusLost = p.commit_on_focus_lost;
@@ -293,26 +298,36 @@ LLTextEditor::~LLTextEditor()
{
gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() while LLTextEditor still valid
- // Route menu back to the default
- if( gEditMenuHandler == this )
- {
- gEditMenuHandler = NULL;
- }
-
// Scrollbar is deleted by LLView
std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer());
+ mUndoStack.clear();
+ // context menu is owned by menu holder, not us
+ //delete mContextMenu;
}
////////////////////////////////////////////////////////////
// LLTextEditor
// Public methods
-void LLTextEditor::setText(const LLStringExplicit &utf8str)
+void LLTextEditor::setText(const LLStringExplicit &utf8str, const LLStyle::Params& input_params)
{
+ // validate incoming text if necessary
+ if (mPrevalidateFunc)
+ {
+ LLWString test_text = utf8str_to_wstring(utf8str);
+ if (!mPrevalidateFunc(test_text))
+ {
+ // not valid text, nothing to do
+ return;
+ }
+ }
+
blockUndo();
deselect();
-
- LLTextBase::setText(utf8str);
+
+ mParseOnTheFly = FALSE;
+ LLTextBase::setText(utf8str, input_params);
+ mParseOnTheFly = TRUE;
resetDirty();
}
@@ -442,8 +457,13 @@ S32 LLTextEditor::nextWordPos(S32 cursorPos) const
const LLTextSegmentPtr LLTextEditor::getPreviousSegment() const
{
+ static LLPointer<LLIndexSegment> index_segment = new LLIndexSegment;
+
+ 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(new LLIndexSegment(mCursorPos));
+ segment_set_t::const_iterator it = mSegments.lower_bound(index_segment);
if (it != mSegments.end())
{
@@ -481,21 +501,6 @@ void LLTextEditor::getSegmentsInRange(LLTextEditor::segment_vec_t& segments_out,
}
}
-// virtual
-BOOL LLTextEditor::canDeselect() const
-{
- return hasSelection();
-}
-
-
-void LLTextEditor::deselect()
-{
- mSelectionStart = 0;
- mSelectionEnd = 0;
- mIsSelecting = FALSE;
-}
-
-
BOOL LLTextEditor::selectionContainsLineBreaks()
{
if (hasSelection())
@@ -585,6 +590,10 @@ void LLTextEditor::indentSelectedLines( S32 spaces )
}
}
+ // Disabling parsing on the fly to avoid updating text segments
+ // until all indentation commands are executed.
+ mParseOnTheFly = FALSE;
+
// Find each start-of-line and indent it
do
{
@@ -610,6 +619,8 @@ void LLTextEditor::indentSelectedLines( S32 spaces )
}
while( cur < right );
+ mParseOnTheFly = TRUE;
+
if( (right < getLength()) && (text[right] == '\n') )
{
right++;
@@ -642,12 +653,28 @@ void LLTextEditor::selectAll()
mSelectionStart = getLength();
mSelectionEnd = 0;
setCursorPos(mSelectionEnd);
+ updatePrimary();
+}
+
+void LLTextEditor::selectByCursorPosition(S32 prev_cursor_pos, S32 next_cursor_pos)
+{
+ setCursorPos(prev_cursor_pos);
+ startSelection();
+ setCursorPos(next_cursor_pos);
+ endSelection();
}
BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
{
BOOL handled = FALSE;
+ // set focus first, in case click callbacks want to change it
+ // RN: do we really need to have a tab stop?
+ if (hasTabStop())
+ {
+ setFocus( TRUE );
+ }
+
// Let scrollbar have first dibs
handled = LLTextBase::handleMouseDown(x, y, mask);
@@ -684,36 +711,53 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
setCursorAtLocalPos( x, y, true );
startSelection();
}
- gFocusMgr.setMouseCapture( this );
}
handled = TRUE;
}
- if (hasTabStop())
- {
- setFocus( TRUE );
- handled = TRUE;
- }
-
// Delay cursor flashing
resetCursorBlink();
+ if (handled && !gFocusMgr.getMouseCapture())
+ {
+ gFocusMgr.setMouseCapture( this );
+ }
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)
{
- BOOL handled = FALSE;
- handled = LLTextBase::handleMouseDown(x, y, mask);
+ if (hasTabStop())
+ {
+ setFocus(TRUE);
+ }
- if (!handled)
+ if (!LLTextBase::handleMouseDown(x, y, mask))
{
- setFocus( TRUE );
if( canPastePrimary() )
{
setCursorAtLocalPos( x, y, true );
+ // does not rely on focus being set
pastePrimary();
}
}
@@ -729,15 +773,16 @@ BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask)
{
if( mIsSelecting )
{
- mScroller->autoScroll(x, y);
-
- S32 clamped_x = llclamp(x, mTextRect.mLeft, mTextRect.mRight);
- S32 clamped_y = llclamp(y, mTextRect.mBottom, mTextRect.mTop);
+ if(mScroller)
+ {
+ mScroller->autoScroll(x, y);
+ }
+ 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;
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL;
getWindow()->setCursor(UI_CURSOR_IBEAM);
handled = TRUE;
}
@@ -769,7 +814,7 @@ BOOL LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask)
BOOL handled = FALSE;
// if I'm not currently selecting text
- if (!(hasSelection() && hasMouseCapture()))
+ if (!(mIsSelecting && hasMouseCapture()))
{
// let text segments handle mouse event
handled = LLTextBase::handleMouseUp(x, y, mask);
@@ -779,9 +824,12 @@ BOOL LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask)
{
if( mIsSelecting )
{
- mScroller->autoScroll(x, y);
- S32 clamped_x = llclamp(x, mTextRect.mLeft, mTextRect.mRight);
- S32 clamped_y = llclamp(y, mTextRect.mBottom, mTextRect.mTop);
+ if(mScroller)
+ {
+ mScroller->autoScroll(x, y);
+ }
+ 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();
}
@@ -876,6 +924,21 @@ S32 LLTextEditor::execute( TextCmd* cmd )
// 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
{
@@ -894,12 +957,18 @@ S32 LLTextEditor::insert(S32 pos, const LLWString &wstr, bool group_with_next_op
S32 LLTextEditor::remove(S32 pos, S32 length, bool group_with_next_op)
{
S32 end_pos = getEditableIndex(pos + length, true);
+ BOOL removedChar = FALSE;
segment_vec_t segments_to_remove;
// store text segments
getSegmentsInRange(segments_to_remove, pos, pos + length, false);
+
+ if(pos <= end_pos)
+ {
+ removedChar = execute( new TextCmdRemove( pos, group_with_next_op, end_pos - pos, segments_to_remove ) );
+ }
- return execute( new TextCmdRemove( pos, group_with_next_op, end_pos - pos, segments_to_remove ) );
+ return removedChar;
}
S32 LLTextEditor::overwriteChar(S32 pos, llwchar wc)
@@ -960,7 +1029,7 @@ void LLTextEditor::removeCharOrTab()
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
}
@@ -983,7 +1052,7 @@ void LLTextEditor::removeChar()
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
}
@@ -999,7 +1068,21 @@ 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
@@ -1024,6 +1107,46 @@ void LLTextEditor::addChar(llwchar wc)
}
setCursorPos(mCursorPos + addChar( mCursorPos, wc ));
+
+ if (!mReadOnly && mAutoreplaceCallback != NULL)
+ {
+ // autoreplace the text, if necessary
+ S32 replacement_start;
+ S32 replacement_length;
+ LLWString replacement_string;
+ S32 new_cursor_pos = mCursorPos;
+ mAutoreplaceCallback(replacement_start, replacement_length, replacement_string, new_cursor_pos, getWText());
+
+ if (replacement_length > 0 || !replacement_string.empty())
+ {
+ remove(replacement_start, replacement_length, true);
+ insert(replacement_start, replacement_string, false, LLTextSegmentPtr());
+ setCursorPos(new_cursor_pos);
+ }
+ }
+}
+
+void LLTextEditor::addLineBreakChar(BOOL group_together)
+{
+ 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, group_together, '\n', segment));
+
+ setCursorPos(mCursorPos + pos);
}
@@ -1119,22 +1242,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
@@ -1168,6 +1275,7 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)
case KEY_DOWN:
changeLine( 1 );
+ deselect();
break;
case KEY_PAGE_DOWN:
@@ -1181,7 +1289,7 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)
case KEY_LEFT:
if( hasSelection() )
{
- setCursorPos(llmin( mCursorPos - 1, mSelectionStart, mSelectionEnd ));
+ setCursorPos(llmin( mSelectionStart, mSelectionEnd ));
}
else
{
@@ -1191,7 +1299,7 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
}
break;
@@ -1199,7 +1307,7 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)
case KEY_RIGHT:
if( hasSelection() )
{
- setCursorPos(llmax( mCursorPos + 1, mSelectionStart, mSelectionEnd ));
+ setCursorPos(llmax( mSelectionStart, mSelectionEnd ));
}
else
{
@@ -1209,7 +1317,7 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
}
break;
@@ -1220,6 +1328,11 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)
}
}
+ if (handled)
+ {
+ deselect();
+ }
+
return handled;
}
@@ -1252,11 +1365,9 @@ void LLTextEditor::cut()
}
S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
S32 length = llabs( mSelectionStart - mSelectionEnd );
- gClipboard.copyFromSubstring( getWText(), left_pos, length, mSourceID );
+ LLClipboard::instance().copyToClipboard( getWText(), left_pos, length);
deleteSelection( FALSE );
- needsReflow();
-
onKeyStroke();
}
@@ -1274,12 +1385,12 @@ void LLTextEditor::copy()
}
S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
S32 length = llabs( mSelectionStart - mSelectionEnd );
- gClipboard.copyFromSubstring(getWText(), left_pos, length, mSourceID);
+ LLClipboard::instance().copyToClipboard(getWText(), left_pos, length);
}
BOOL LLTextEditor::canPaste() const
{
- return !mReadOnly && gClipboard.canPasteString();
+ return !mReadOnly && LLClipboard::instance().isTextAvailable();
}
// paste from clipboard
@@ -1299,6 +1410,7 @@ void LLTextEditor::pastePrimary()
// paste from primary (itsprimary==true) or clipboard (itsprimary==false)
void LLTextEditor::pasteHelper(bool is_primary)
{
+ mParseOnTheFly = FALSE;
bool can_paste_it;
if (is_primary)
{
@@ -1314,16 +1426,8 @@ void LLTextEditor::pasteHelper(bool is_primary)
return;
}
- LLUUID source_id;
LLWString paste;
- if (is_primary)
- {
- paste = gClipboard.getPastePrimaryWString(&source_id);
- }
- else
- {
- paste = gClipboard.getPasteWString(&source_id);
- }
+ LLClipboard::instance().pasteFromClipboard(paste, is_primary);
if (paste.empty())
{
@@ -1338,6 +1442,23 @@ void LLTextEditor::pasteHelper(bool is_primary)
// Clean up string (replace tabs and remove characters that our fonts don't support).
LLWString clean_string(paste);
+ cleanStringForPaste(clean_string);
+
+ // Insert the new text into the existing text.
+
+ //paste text with linebreaks.
+ pasteTextWithLinebreaks(clean_string);
+
+ deselect();
+
+ onKeyStroke();
+ mParseOnTheFly = TRUE;
+}
+
+
+// Clean up string (replace tabs and remove characters that our fonts don't support).
+void LLTextEditor::cleanStringForPaste(LLWString & clean_string)
+{
LLWStringUtil::replaceTabsWithSpaces(clean_string, SPACES_PER_TAB);
if( mAllowEmbeddedItems )
{
@@ -1356,17 +1477,37 @@ void LLTextEditor::pasteHelper(bool is_primary)
}
}
}
+}
- // Insert the new text into the existing text.
- setCursorPos(mCursorPos + insert(mCursorPos, clean_string, FALSE, LLTextSegmentPtr()));
- deselect();
-
- needsReflow();
- onKeyStroke();
-}
+void LLTextEditor::pasteTextWithLinebreaks(LLWString & clean_string)
+{
+ std::basic_string<llwchar>::size_type start = 0;
+ std::basic_string<llwchar>::size_type pos = clean_string.find('\n',start);
+
+ while((pos != -1) && (pos != clean_string.length() -1))
+ {
+ if(pos!=start)
+ {
+ std::basic_string<llwchar> str = std::basic_string<llwchar>(clean_string,start,pos-start);
+ setCursorPos(mCursorPos + insert(mCursorPos, str, TRUE, LLTextSegmentPtr()));
+ }
+ addLineBreakChar(TRUE); // Add a line break and group with the next addition.
+ start = pos+1;
+ pos = clean_string.find('\n',start);
+ }
+ if (pos != start)
+ {
+ std::basic_string<llwchar> str = std::basic_string<llwchar>(clean_string,start,clean_string.length()-start);
+ setCursorPos(mCursorPos + insert(mCursorPos, str, FALSE, LLTextSegmentPtr()));
+ }
+ else
+ {
+ addLineBreakChar(FALSE); // Add a line break and end the grouping.
+ }
+}
// copy selection to primary
void LLTextEditor::copyPrimary()
@@ -1377,12 +1518,12 @@ void LLTextEditor::copyPrimary()
}
S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
S32 length = llabs( mSelectionStart - mSelectionEnd );
- gClipboard.copyFromPrimarySubstring(getWText(), left_pos, length, mSourceID);
+ LLClipboard::instance().copyToClipboard(getWText(), left_pos, length, true);
}
BOOL LLTextEditor::canPastePrimary() const
{
- return !mReadOnly && gClipboard.canPastePrimaryString();
+ return !mReadOnly && LLClipboard::instance().isTextAvailable(true);
}
void LLTextEditor::updatePrimary()
@@ -1468,7 +1609,7 @@ BOOL LLTextEditor::handleControlKey(const KEY key, const MASK mask)
}
}
- if (handled)
+ if (handled && !gFocusMgr.getMouseCapture())
{
updatePrimary();
}
@@ -1476,75 +1617,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 )
+BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask)
{
- if( canDoDelete() )
- {
- doDelete();
- }
- else
- {
- reportBadKeystroke();
- }
- handled = TRUE;
- }
- else
- if( MASK_CONTROL & 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:
@@ -1566,7 +1645,7 @@ BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask, BOOL* return
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
break;
@@ -1574,11 +1653,14 @@ BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask, BOOL* return
case KEY_RETURN:
if (mask == MASK_NONE)
{
- if( hasSelection() )
+ if( hasSelection() && !mKeepSelectionOnReturn )
{
deleteSelection(FALSE);
}
- autoIndent(); // TODO: make this optional
+ if (mAutoIndent)
+ {
+ autoIndent();
+ }
}
else
{
@@ -1619,6 +1701,10 @@ BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask, BOOL* return
break;
}
+ if (handled)
+ {
+ onKeyStroke();
+ }
return handled;
}
@@ -1639,9 +1725,6 @@ 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;
// Special case for TAB. If want to move to next field, report
// not handled and let the parent take care of field movement.
@@ -1649,110 +1732,55 @@ BOOL LLTextEditor::handleKeyHere(KEY key, MASK mask )
{
return FALSE;
}
- /*
- if (KEY_F10 == key)
- {
- LLComboBox::Params cp;
- cp.name = "combo box";
- cp.label = "my combo";
- cp.rect.width = 100;
- cp.rect.height = 20;
- cp.items.add().label = "item 1";
- cp.items.add().label = "item 2";
- cp.items.add().label = "item 3";
-
- appendWidget(LLUICtrlFactory::create<LLComboBox>(cp), "combo", true, false);
- }
- if (KEY_F11 == key)
- {
- LLButton::Params bp;
- bp.name = "text button";
- bp.label = "Click me";
- bp.rect.width = 100;
- bp.rect.height = 20;
- appendWidget(LLUICtrlFactory::create<LLButton>(bp), "button", true, false);
- }
- */
- if (mReadOnly)
- {
- handled = mScroller->handleKeyHere( key, mask );
- }
- else
+ if (mReadOnly && mScroller)
{
- // handle navigation keys ourself
- handled = handleNavigationKey( key, mask );
+ handled = (mScroller && mScroller->handleKeyHere( key, mask ))
+ || handleSelectionKey(key, mask)
+ || handleControlKey(key, mask);
}
-
-
- if( handled )
- {
- text_may_have_changed = FALSE;
- }
-
- if( !handled )
+ else
{
- handled = handleSelectionKey( key, mask );
- if( handled )
- {
- selection_modified = TRUE;
- }
- }
+ if (mEnableTooltipPaste &&
+ LLToolTipMgr::instance().toolTipVisible() &&
+ KEY_TAB == key)
+ { // Paste the first line of a tooltip into the editor
+ std::string message;
+ LLToolTipMgr::instance().getToolTipMessage(message);
+ LLWString tool_tip_text(utf8str_to_wstring(message));
- if( !handled )
- {
- handled = handleControlKey( key, mask );
- if( handled )
- {
- selection_modified = TRUE;
- }
- }
+ if (tool_tip_text.size() > 0)
+ {
+ // Delete any selected characters (the tooltip text replaces them)
+ if(hasSelection())
+ {
+ deleteSelection(TRUE);
+ }
- if( !handled && mHandleEditKeysDirectly )
- {
- handled = handleEditKey( key, mask );
- if( handled )
- {
- selection_modified = TRUE;
- text_may_have_changed = TRUE;
- }
- }
+ std::basic_string<llwchar>::size_type pos = tool_tip_text.find('\n',0);
+ if (pos != -1)
+ { // Extract the first line of the tooltip
+ tool_tip_text = std::basic_string<llwchar>(tool_tip_text, 0, pos);
+ }
- // 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;
+ // Add the text
+ cleanStringForPaste(tool_tip_text);
+ pasteTextWithLinebreaks(tool_tip_text);
+ handled = TRUE;
}
}
-
+ else
+ { // Normal key handling
+ handled = handleNavigationKey( key, mask )
+ || handleSelectionKey(key, mask)
+ || handleControlKey(key, mask)
+ || handleSpecialKey(key, mask);
+ }
}
if( handled )
{
resetCursorBlink();
-
- // 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();
- }
-
- if(text_may_have_changed)
- {
- needsReflow();
-
- onKeyStroke();
- }
needsScroll();
}
@@ -1793,8 +1821,6 @@ BOOL LLTextEditor::handleUnicodeCharHere(llwchar uni_char)
// Most keystrokes will make the selection box go away, but not all will.
deselect();
- needsReflow();
-
onKeyStroke();
}
@@ -1805,7 +1831,7 @@ BOOL LLTextEditor::handleUnicodeCharHere(llwchar uni_char)
// virtual
BOOL LLTextEditor::canDoDelete() const
{
- return !mReadOnly && ( hasSelection() || (mCursorPos < getLength()) );
+ return !mReadOnly && ( !mPassDelete || ( hasSelection() || (mCursorPos < getLength())) );
}
void LLTextEditor::doDelete()
@@ -1850,10 +1876,9 @@ void LLTextEditor::doDelete()
removeChar();
}
- onKeyStroke();
}
- needsReflow();
+ onKeyStroke();
}
//----------------------------------------------------------------------------
@@ -1896,8 +1921,6 @@ void LLTextEditor::undo()
setCursorPos(pos);
- needsReflow();
-
onKeyStroke();
}
@@ -1940,8 +1963,6 @@ void LLTextEditor::redo()
setCursorPos(pos);
- needsReflow();
-
onKeyStroke();
}
@@ -1951,8 +1972,7 @@ void LLTextEditor::onFocusReceived()
updateAllowingLanguageInput();
}
-// virtual, from LLView
-void LLTextEditor::onFocusLost()
+void LLTextEditor::focusLostHelper()
{
updateAllowingLanguageInput();
@@ -1969,7 +1989,11 @@ void LLTextEditor::onFocusLost()
// Make sure cursor is shown again
getWindow()->showCursorFromMouseMove();
+}
+void LLTextEditor::onFocusLost()
+{
+ focusLostHelper();
LLTextBase::onFocusLost();
}
@@ -1985,12 +2009,74 @@ void LLTextEditor::setEnabled(BOOL enabled)
bool read_only = !enabled;
if (read_only != mReadOnly)
{
- mReadOnly = read_only;
+ //mReadOnly = read_only;
+ LLTextBase::setReadOnly(read_only);
updateSegments();
updateAllowingLanguageInput();
}
}
+void LLTextEditor::showContextMenu(S32 x, S32 y)
+{
+ if (!mContextMenu)
+ {
+ llassert(LLMenuGL::sMenuContainer != NULL);
+ mContextMenu = LLUICtrlFactory::instance().createFromFile<LLContextMenu>("menu_text_editor.xml",
+ LLMenuGL::sMenuContainer,
+ LLMenuHolderGL::child_registry_t::instance());
+ }
+
+ // 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)
+
+ gEditMenuHandler = this;
+
+ S32 screen_x, screen_y;
+ localPointToScreen(x, y, &screen_x, &screen_y);
+
+ setCursorAtLocalPos(x, y, false);
+ if (hasSelection())
+ {
+ if ( (mCursorPos < llmin(mSelectionStart, mSelectionEnd)) || (mCursorPos > llmax(mSelectionStart, mSelectionEnd)) )
+ {
+ deselect();
+ }
+ else
+ {
+ setCursorPos(llmax(mSelectionStart, mSelectionEnd));
+ }
+ }
+
+ bool use_spellcheck = getSpellCheck(), is_misspelled = false;
+ if (use_spellcheck)
+ {
+ mSuggestionList.clear();
+
+ // If the cursor is on a misspelled word, retrieve suggestions for it
+ std::string misspelled_word = getMisspelledWord(mCursorPos);
+ if ((is_misspelled = !misspelled_word.empty()) == true)
+ {
+ LLSpellChecker::instance().getSuggestions(misspelled_word, mSuggestionList);
+ }
+ }
+
+ mContextMenu->setItemVisible("Suggestion Separator", (use_spellcheck) && (!mSuggestionList.empty()));
+ mContextMenu->setItemVisible("Add to Dictionary", (use_spellcheck) && (is_misspelled));
+ mContextMenu->setItemVisible("Add to Ignore", (use_spellcheck) && (is_misspelled));
+ mContextMenu->setItemVisible("Spellcheck Separator", (use_spellcheck) && (is_misspelled));
+ mContextMenu->show(screen_x, screen_y, this);
+}
+
+
void LLTextEditor::drawPreeditMarker()
{
static LLUICachedControl<F32> preedit_marker_brightness ("UIPreeditMarkerBrightness", 0);
@@ -2018,11 +2104,11 @@ void LLTextEditor::drawPreeditMarker()
return;
}
- const S32 line_height = llround( mDefaultFont->getLineHeight() );
+ const S32 line_height = mFont->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;
@@ -2054,36 +2140,41 @@ void LLTextEditor::drawPreeditMarker()
continue;
}
- S32 preedit_left = mTextRect.mLeft;
+ line_info& line = mLineInfoList[cur_line];
+ LLRect text_rect(line.mRect);
+ text_rect.mRight = mDocumentView->getRect().getWidth(); // clamp right edge to document extents
+ text_rect.translate(mDocumentView->getRect().mLeft, mDocumentView->getRect().mBottom); // adjust by scroll position
+
+ S32 preedit_left = text_rect.mLeft;
if (left > line_start)
{
- preedit_left += mDefaultFont->getWidth(text, line_start, left - line_start);
+ preedit_left += mFont->getWidth(text, line_start, left - line_start);
}
- S32 preedit_right = mTextRect.mLeft;
+ S32 preedit_right = text_rect.mLeft;
if (right < line_end)
{
- preedit_right += mDefaultFont->getWidth(text, line_start, right - line_start);
+ preedit_right += mFont->getWidth(text, line_start, right - line_start);
}
else
{
- preedit_right += mDefaultFont->getWidth(text, line_start, line_end - line_start);
+ preedit_right += mFont->getWidth(text, line_start, line_end - line_start);
}
if (mPreeditStandouts[i])
{
gl_rect_2d(preedit_left + preedit_standout_gap,
- line_y + preedit_standout_position,
- preedit_right - preedit_standout_gap - 1,
- line_y + preedit_standout_position - preedit_standout_thickness,
- (mCursorColor.get() * preedit_standout_brightness + mWriteableBgColor.get() * (1 - preedit_standout_brightness)).setAlpha(1.0f));
+ text_rect.mBottom + mFont->getDescenderHeight() - 1,
+ preedit_right - preedit_standout_gap - 1,
+ text_rect.mBottom + mFont->getDescenderHeight() - 1 - preedit_standout_thickness,
+ (mCursorColor.get() * preedit_standout_brightness + mWriteableBgColor.get() * (1 - preedit_standout_brightness)).setAlpha(1.0f));
}
else
{
gl_rect_2d(preedit_left + preedit_marker_gap,
- line_y + preedit_marker_position,
- preedit_right - preedit_marker_gap - 1,
- line_y + preedit_marker_position - preedit_marker_thickness,
- (mCursorColor.get() * preedit_marker_brightness + mWriteableBgColor.get() * (1 - preedit_marker_brightness)).setAlpha(1.0f));
+ text_rect.mBottom + mFont->getDescenderHeight() - 1,
+ preedit_right - preedit_marker_gap - 1,
+ text_rect.mBottom + mFont->getDescenderHeight() - 1 - preedit_marker_thickness,
+ (mCursorColor.get() * preedit_marker_brightness + mWriteableBgColor.get() * (1 - preedit_marker_brightness)).setAlpha(1.0f));
}
}
}
@@ -2095,83 +2186,19 @@ void LLTextEditor::drawPreeditMarker()
}
}
-
-void LLTextEditor::drawLineNumbers()
-{
- LLGLSUIDefault gls_ui;
-
- LLRect scrolled_view_rect = mScroller->getVisibleContentRect();
- LLRect content_rect = mScroller->getContentWindowRect();
- LLLocalClipRect clip(content_rect);
- S32 first_line = getFirstVisibleLine();
- S32 num_lines = getLineCount();
- if (first_line >= num_lines)
- {
- return;
- }
-
- S32 cursor_line = getLineNumFromDocIndex(mCursorPos);
-
- if (mShowLineNumbers)
- {
- S32 left = 0;
- S32 top = getRect().getHeight();
- S32 bottom = 0;
-
- 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;
-
- for (S32 cur_line = first_line; cur_line < num_lines; cur_line++)
- {
- line_info& line = mLineInfoList[cur_line];
-
- if ((line.mRect.mTop - scrolled_view_rect.mBottom) < mTextRect.mBottom)
- {
- break;
- }
-
- S32 line_bottom = line.mRect.mBottom - scrolled_view_rect.mBottom + mTextRect.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;
- }
- }
- }
-}
-
void LLTextEditor::draw()
{
{
// pad clipping rectangle so that cursor can draw at full width
- // when at left edge of mTextRect
- LLRect clip_rect(mTextRect);
+ // when at left edge of mVisibleTextRect
+ LLRect clip_rect(mVisibleTextRect);
clip_rect.stretch(1);
LLLocalClipRect clip(clip_rect);
- drawPreeditMarker();
}
LLTextBase::draw();
- drawLineNumbers();
+
+ drawPreeditMarker();
//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
@@ -2231,13 +2258,14 @@ void LLTextEditor::getCurrentLineAndColumn( S32* line, S32* col, BOOL include_wo
void LLTextEditor::autoIndent()
{
// Count the number of spaces in the current line
- S32 line = getLineNumFromDocIndex(mCursorPos);
+ S32 line = getLineNumFromDocIndex(mCursorPos, false);
S32 line_start = getLineStart(line);
S32 space_count = 0;
S32 i;
LLWString text = getWText();
- while( ' ' == text[line_start] )
+ S32 offset = getLineOffsetFromDocIndex(mCursorPos);
+ while(( ' ' == text[line_start] ) && (space_count < offset))
{
space_count++;
line_start++;
@@ -2250,7 +2278,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( ' ' );
@@ -2271,12 +2302,26 @@ void LLTextEditor::insertText(const std::string &new_text)
setCursorPos(mCursorPos + insert( mCursorPos, utf8str_to_wstring(new_text), FALSE, LLTextSegmentPtr() ));
- needsReflow();
+ setEnabled( enabled );
+}
+
+void LLTextEditor::insertText(LLWString &new_text)
+{
+ BOOL enabled = getEnabled();
+ setEnabled( TRUE );
+
+ // Delete any selected characters (the insertion replaces them)
+ if( hasSelection() )
+ {
+ deleteSelection(TRUE);
+ }
+
+ setCursorPos(mCursorPos + insert( mCursorPos, new_text, FALSE, LLTextSegmentPtr() ));
setEnabled( enabled );
}
-void LLTextEditor::appendWidget(LLView* widget, const std::string &widget_text, bool allow_undo, bool prepend_newline)
+void LLTextEditor::appendWidget(const LLInlineViewSegment::Params& params, const std::string& text, bool allow_undo)
{
// Save old state
S32 selection_start = mSelectionStart;
@@ -2290,24 +2335,11 @@ void LLTextEditor::appendWidget(LLView* widget, const std::string &widget_text,
setCursorPos(old_length);
- LLWString widget_wide_text;
+ LLWString widget_wide_text = utf8str_to_wstring(text);
- // Add carriage return if not first line
- if (getLength() != 0
- && prepend_newline)
- {
- widget_wide_text = utf8str_to_wstring(std::string("\n") + widget_text);
- }
- else
- {
- widget_wide_text = utf8str_to_wstring(widget_text);
- }
-
- LLTextSegmentPtr segment = new LLInlineViewSegment(widget, old_length, old_length + widget_text.size());
+ LLTextSegmentPtr segment = new LLInlineViewSegment(params, old_length, old_length + widget_wide_text.size());
insert(getLength(), widget_wide_text, FALSE, segment);
- needsReflow();
-
// Set the cursor and scroll position
if( selection_start != selection_end )
{
@@ -2326,58 +2358,12 @@ void LLTextEditor::appendWidget(LLView* widget, const std::string &widget_text,
setCursorPos(cursor_pos);
}
- if( !allow_undo )
+ if (!allow_undo)
{
blockUndo();
}
}
-
-void LLTextEditor::replaceUrlLabel(const std::string &url,
- const std::string &label)
-{
- // get the full (wide) text for the editor so we can change it
- LLWString text = getWText();
- LLWString wlabel = utf8str_to_wstring(label);
- bool modified = false;
- S32 seg_start = 0;
-
- // iterate through each segment looking for ones styled as links
- segment_set_t::iterator it;
- for (it = mSegments.begin(); it != mSegments.end(); ++it)
- {
- LLTextSegment *seg = *it;
- const LLStyleSP style = seg->getStyle();
-
- // update segment start/end length in case we replaced text earlier
- S32 seg_length = seg->getEnd() - seg->getStart();
- seg->setStart(seg_start);
- seg->setEnd(seg_start + seg_length);
-
- // if we find a link with our Url, then replace the label
- if (style->isLink() && style->getLinkHREF() == url)
- {
- S32 start = seg->getStart();
- S32 end = seg->getEnd();
- text = text.substr(0, start) + wlabel + text.substr(end, text.size() - end + 1);
- seg->setEnd(start + wlabel.size());
- modified = true;
- }
-
- // work out the character offset for the next segment
- seg_start = seg->getEnd();
- }
-
- // update the editor with the new (wide) text string
- if (modified)
- {
- getViewModel()->setDisplay(text);
- deselect();
- setCursorPos(mCursorPos);
- needsReflow();
- }
-}
-
void LLTextEditor::removeTextFromEnd(S32 num_chars)
{
if (num_chars <= 0) return;
@@ -2389,7 +2375,6 @@ void LLTextEditor::removeTextFromEnd(S32 num_chars)
mSelectionStart = llclamp(mSelectionStart, 0, len);
mSelectionEnd = llclamp(mSelectionEnd, 0, len);
- needsReflow();
needsScroll();
}
@@ -2448,61 +2433,11 @@ BOOL LLTextEditor::tryToRevertToPristineState()
i--;
}
}
-
- needsReflow();
}
return isPristine(); // TRUE => success
}
-
-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());
- for(S32 i = 0; i < count; i++)
- {
- 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);
-
- 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()
-{
- LLFastTimer ft(FTM_SYNTAX_HIGHLIGHTING);
- if (mKeywords.isLoaded())
- {
- // HACK: No non-ascii keywords for now
- segment_vec_t segment_list;
- mKeywords.findSegments(&segment_list, getWText(), mDefaultColor.get(), *this);
-
- 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)
- {
- insertSegment(*list_it);
- }
- }
-
- LLTextBase::updateSegments();
-}
-
void LLTextEditor::updateLinkSegments()
{
LLWString wtext = getWText();
@@ -2513,16 +2448,37 @@ void LLTextEditor::updateLinkSegments()
LLTextSegment *segment = *it;
if (segment && segment->getStyle() && segment->getStyle()->isLink())
{
+ LLStyleConstSP style = segment->getStyle();
+ LLStyleSP new_style(new LLStyle(*style));
+ LLWString url_label = wtext.substr(segment->getStart(), segment->getEnd()-segment->getStart());
+
+ segment_set_t::const_iterator next_it = mSegments.upper_bound(segment);
+ LLTextSegment *next_segment = *next_it;
+ if (next_segment)
+ {
+ LLWString next_url_label = wtext.substr(next_segment->getStart(), next_segment->getEnd()-next_segment->getStart());
+ std::string link_check = wstring_to_utf8str(url_label) + wstring_to_utf8str(next_url_label);
+ LLUrlMatch match;
+
+ if ( LLUrlRegistry::instance().findUrl(link_check, match))
+ {
+ if(match.getQuery() == wstring_to_utf8str(next_url_label))
+ {
+ continue;
+ }
+ }
+ }
+
// 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.
- LLStyleSP style = static_cast<LLStyleSP>(segment->getStyle());
- LLWString url_label = wtext.substr(segment->getStart(), segment->getEnd()-segment->getStart());
if (LLUrlRegistry::instance().hasUrl(url_label))
{
std::string new_url = wstring_to_utf8str(url_label);
LLStringUtil::trim(new_url);
- style->setLinkHREF(new_url);
+ new_style->setLinkHREF(new_url);
+ LLStyleConstSP sp(new_style);
+ segment->setStyle(sp);
}
}
}
@@ -2556,20 +2512,20 @@ BOOL LLTextEditor::importBuffer(const char* buffer, S32 length )
instream.getline(tbuf, MAX_STRING);
if( 1 != sscanf(tbuf, "Linden text version %d", &version) )
{
- llwarns << "Invalid Linden text file header " << llendl;
+ LL_WARNS() << "Invalid Linden text file header " << LL_ENDL;
return FALSE;
}
if( 1 != version )
{
- llwarns << "Invalid Linden text file version: " << version << llendl;
+ LL_WARNS() << "Invalid Linden text file version: " << version << LL_ENDL;
return FALSE;
}
instream.getline(tbuf, MAX_STRING);
if( 0 != sscanf(tbuf, "{") )
{
- llwarns << "Invalid Linden text file format" << llendl;
+ LL_WARNS() << "Invalid Linden text file format" << LL_ENDL;
return FALSE;
}
@@ -2577,13 +2533,13 @@ BOOL LLTextEditor::importBuffer(const char* buffer, S32 length )
instream.getline(tbuf, MAX_STRING);
if( 1 != sscanf(tbuf, "Text length %d", &text_len) )
{
- llwarns << "Invalid Linden text length field" << llendl;
+ LL_WARNS() << "Invalid Linden text length field" << LL_ENDL;
return FALSE;
}
if( text_len > mMaxTextByteLength )
{
- llwarns << "Invalid Linden text length: " << text_len << llendl;
+ LL_WARNS() << "Invalid Linden text length: " << text_len << LL_ENDL;
return FALSE;
}
@@ -2592,21 +2548,21 @@ BOOL LLTextEditor::importBuffer(const char* buffer, S32 length )
char* text = new char[ text_len + 1];
if (text == NULL)
{
- llerrs << "Memory allocation failure." << llendl;
+ LL_ERRS() << "Memory allocation failure." << LL_ENDL;
return FALSE;
}
instream.get(text, text_len + 1, '\0');
text[text_len] = '\0';
if( text_len != (S32)strlen(text) )/* Flawfinder: ignore */
{
- llwarns << llformat("Invalid text length: %d != %d ",strlen(text),text_len) << llendl;/* Flawfinder: ignore */
+ LL_WARNS() << llformat("Invalid text length: %d != %d ",strlen(text),text_len) << LL_ENDL;/* Flawfinder: ignore */
success = FALSE;
}
instream.getline(tbuf, MAX_STRING);
if( success && (0 != sscanf(tbuf, "}")) )
{
- llwarns << "Invalid Linden text file format: missing terminal }" << llendl;
+ LL_WARNS() << "Invalid Linden text file format: missing terminal }" << LL_ENDL;
success = FALSE;
}
@@ -2621,7 +2577,6 @@ BOOL LLTextEditor::importBuffer(const char* buffer, S32 length )
startOfDoc();
deselect();
- needsReflow();
return success;
}
@@ -2666,11 +2621,23 @@ BOOL LLTextEditor::hasPreeditString() const
void LLTextEditor::resetPreedit()
{
+ if (hasSelection())
+ {
+ if (hasPreeditString())
+ {
+ LL_WARNS() << "Preedit and selection!" << LL_ENDL;
+ deselect();
+ }
+ else
+ {
+ deleteSelection(TRUE);
+ }
+ }
if (hasPreeditString())
{
if (hasSelection())
{
- llwarns << "Preedit and selection!" << llendl;
+ LL_WARNS() << "Preedit and selection!" << LL_ENDL;
deselect();
}
@@ -2721,11 +2688,13 @@ void LLTextEditor::updatePreedit(const LLWString &preedit_string,
{
mPreeditOverwrittenWString.clear();
}
- insertStringNoUndo(insert_preedit_at, mPreeditWString);
+
+ segment_vec_t segments;
+ //pass empty segments to let "insertStringNoUndo" make new LLNormalTextSegment and insert it, if needed.
+ insertStringNoUndo(insert_preedit_at, mPreeditWString, &segments);
mPreeditStandouts = preedit_standouts;
- needsReflow();
setCursorPos(insert_preedit_at + caret_position);
// Update of the preedit should be caused by some key strokes.
@@ -2739,7 +2708,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);
}
@@ -2786,12 +2755,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(mDefaultFont->getLineHeight());
+ const S32 line_height = mFont->getLineHeight();
if (coord)
{
- const S32 query_x = mTextRect.mLeft + mDefaultFont->getWidth(text, current_line_start, query - current_line_start);
- const S32 query_y = mTextRect.mTop - (current_line - first_visible_line) * line_height - line_height / 2;
+ const S32 query_x = mVisibleTextRect.mLeft + mFont->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);
@@ -2799,23 +2768,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 += mDefaultFont->getWidth(text, current_line_start, preedit_left_position - current_line_start);
+ preedit_left += mFont->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 += mDefaultFont->getWidth(text, current_line_start, preedit_right_position - current_line_start);
+ preedit_right += mFont->getWidth(text, current_line_start, preedit_right_position - current_line_start);
}
else
{
- preedit_right += mDefaultFont->getWidth(text, current_line_start, current_line_end - current_line_start);
+ preedit_right += mFont->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);
@@ -2861,7 +2830,7 @@ void LLTextEditor::markAsPreedit(S32 position, S32 length)
setCursorPos(position);
if (hasPreeditString())
{
- llwarns << "markAsPreedit invoked when hasPreeditString is true." << llendl;
+ LL_WARNS() << "markAsPreedit invoked when hasPreeditString is true." << LL_ENDL;
}
mPreeditWString = LLWString( getWText(), position, length );
if (length > 0)
@@ -2889,7 +2858,7 @@ void LLTextEditor::markAsPreedit(S32 position, S32 length)
S32 LLTextEditor::getPreeditFontSize() const
{
- return llround(mDefaultFont->getLineHeight() * LLUI::sGLScaleFactor.mV[VY]);
+ return ll_round((F32)mFont->getLineHeight() * LLUI::getScaleFactor().mV[VY]);
}
BOOL LLTextEditor::isDirty() const
@@ -2917,6 +2886,9 @@ void LLTextEditor::setKeystrokeCallback(const keystroke_signal_t::slot_type& cal
void LLTextEditor::onKeyStroke()
{
mKeystrokeSignal(this);
+
+ mSpellCheckStart = mSpellCheckEnd = -1;
+ mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY);
}
//virtual
@@ -2925,3 +2897,13 @@ void LLTextEditor::clear()
getViewModel()->setDisplay(LLWStringUtil::null);
clearSegments();
}
+
+bool LLTextEditor::canLoadOrSaveToFile()
+{
+ return !mReadOnly;
+}
+
+S32 LLTextEditor::spacesPerTab()
+{
+ return SPACES_PER_TAB;
+}